As we all know that Android Jetpack is the great feature given by Android Team. I have explained some of the awesome features of Jetpack like Navigation by NavigationUI and Paging Library for Jetpack.
What is WorkManager?
WorkManager is one of the Android Jetpack Articheture component libraries which is used to run the background services when the constraint is satisfied. As we all know that Google has now restricted to use the background service like Service or Intent Service into Doze Mode. If you want to run the service, then it should be run into the foreground.
While for periodic or schedule background service is always holding the resource like AlarmManager, which consume a lot of memory space. In this case, WorkManager is the best optimized for background services. WorKManager is compatible with all versions of API either for onetime Job required or periodically required to execute the job.
WorkManager is intended for tasks that require a guarantee that the system will run them even if the app exits.
Why is required WorkManager?
Whenever you need to execute the long-running task in the background it means it does not matter your application is running or not. WorkManager provides the guarantee that the Job will surely execute by the System either it is OneTime Job or Schedule Job on time demand. For example, Whenever You need to sync the remote data from the server or upload files to remote servers or fetch some data periodically from the local database kind of job.
There are many benefits to using the WorkManager.
- it is compatible with all versions of Android API.
- It is optimized for background service for on-time demand to execute the Job.
- Execute the Job while the Constraint is satisfied.
- Best optimized by maintaining the device’s health.
- Execute the job either parallel or synchronous.
- Easily create the Chaining of Jobs
How to use the WorkManager?
To use the work manager is required a few dependencies to add to your project Gradle file.
// work manager implementation "android.arch.work:work-runtime-ktx:1.0.1"
Let’s create the sample app to learn the basics of the use case of use the WorkManager. Let’s take a simple example, Whenever a button press by a user, WorkManager executes the One Time job and sends Notification that work is done. Here we can also Observer the Status of the work manager to monitor the job status.
Here I want to set one constraint that the job will execute only when the device is in charging constraint and send input data as a parameter to WorkManager.
val powerConstraint = Constraints.Builder().setRequiresCharging(true).build() val taskData = Data.Builder().putString(MESSAGE_STATUS, "Notify Done.").build() Here I have done these two parameters configured. Now I want to create the One Time Job to set these parameters to build the Job. val request = OneTimeWorkRequest.Builder(NotifyWorker::class.java) .setConstraints(powerConstraint).setInputData(taskData).build() Now I need to create the NotifyWorker Class which extends the Worker Class of WorkManager. class NotifyWorker(context: Context, workParamters: WorkerParameters) : Worker(context, workParamters) { override fun doWork(): Result { // get the input and do the task return Result.success(object) } }
Ok, Now how can I start this Job. It is very simple to enqueue the job.
btn_send.setOnClickListener { WorkManager.getInstance().enqueue(request) }
Now the next question, how can we observe the status of the Job?
worker.getWorkInfoByIdLiveData(request.id).observe(this, Observer { workInfo -> workInfo.let { if (it.state.isFinished) { // do the task } } })
Let’s provide the complete source code of Worker class and Activity Class to better understanding.
class NotifyWorker(context: Context, workParamters: WorkerParameters) : Worker(context, workParamters) { override fun doWork(): Result { val taskData = inputData val taskDataString = taskData.getString(WorkerActivity.MESSAGE_STATUS) showNotification("Work Manager", taskDataString.toString()) val outputData = Data.Builder().putString(WORK_RESULT, "Task Finished").build() return Result.success(outputData) } private fun showNotification(task: String, desc: String) { val manager = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val channelId = "message_channel" val channelName = "message_name" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT) manager.createNotificationChannel(channel) } val builder = NotificationCompat.Builder(applicationContext, channelId) .setContentTitle(task) .setContentText(desc) .setSmallIcon(R.mipmap.ic_launcher) manager.notify(1, builder.build()) } companion object { const val WORK_RESULT = "work_result" } }
In the Activity Class, we need to instantiate the Worker Class to start the Job.
class WorkerActivity : AppCompatActivity() { private lateinit var worker: WorkManager override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.work_activity) worker = WorkManager.getInstance() val powerConstraint = Constraints.Builder().setRequiresCharging(true).build() val taskData = Data.Builder().putString(MESSAGE_STATUS, "Notify Done.").build() val request = OneTimeWorkRequest.Builder(NotifyWorker::class.java) .setConstraints(powerConstraint).setInputData(taskData).build() btn_send.setOnClickListener { worker.enqueue(request) } worker.getWorkInfoByIdLiveData(request.id).observe(this, Observer { workInfo -> workInfo.let { if (it.state.isFinished) { val outputData = it.outputData val taskResult = outputData.getString(NotifyWorker.WORK_RESULT) txt_input.text = taskResult } else { val workStatus = workInfo.state txt_input.text = workStatus.toString() } } }) } companion object { const val MESSAGE_STATUS = "message_status" } }
In the Layout file, I just added a button and Text View to show the status of the running job.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:text="Send" android:id="@+id/btn_send" android:layout_gravity="center" android:gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:id="@+id/txt_input" android:layout_gravity="center" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout>
Now the next is how can we create the hourly repeated unique periodic job to trigger by the system. Here is the sample of periodic job.
fun scheduleWork(tag: String) { val constraints = Constraints.Builder() .setRequiresCharging(true) .build() val work = PeriodicWorkRequestBuilder<NotifyWorker>(1, TimeUnit.HOURS).setConstraints(constraints) .build() worker.enqueueUniquePeriodicWork(tag, ExistingPeriodicWorkPolicy.KEEP , work) }
That’s all guys, I hope this article helps you the understand the basic of WorkManager to run the task in the background.
Wrapping
Now we have a good understanding of the new android Jetpack Component WorkManager. We have played with a sample project to learn the basic concept of WorkManager. I personally like this change from Google. You can get the full Github source code of Jetpack Component. So my next tutorial, we will learn some other jetpack Components in detail, ok till then enjoy your healthy day.
If you are wondering to learn Android then Please learn from Android category and wondering to learn Kotlin then Kotlin Category will help you. If you want to learn all the python article, then learn from the python category.
Happy Coding 🙂
I am a very enthusiastic Android developer to build solid Android apps. I have a keen interest in developing for Android and have published apps to the Google Play Store. I always open to learning new technologies. For any help drop us a line anytime at contact@mobologicplus.com
My work manager is not working when my application is killed from background how can i resolve it.