Android WorkManager

WorkManager is an Android background tasks management system which came with Jetpack. We use it to run postponed background work. It will run even if the app is closed.

WorkManager is not for tasks that need to be run in a background thread, but they don’t need to survive the process death. Use Kotlin’s coroutines instead. WorkManager is for periodic work requests or one time work requests that need to run even if the user closses the App or needs constraints such as make sure the device is charging or be sure it uses WIFI.
It’s possible to chain multiple Workers together to run one after another.

Implementation

Add this to your build.gradle file.

dependencies {
        def work_version = "2.3.4"
        implementation "androidx.work:work-runtime-ktx:$work_version"
}

One Time Work Requests

Create a worker class. This will be executed by the work manager instance when conditions are met.

class UploadWorker(context: Context, params: WorkerParameters): Worker(context, prams) {
        override fun doWork(): Result {
                try {
                        // add your work here
                        return Result.success()
                } catch(e: Exception) {
                        return result.failure()
                }
        }
}

Now set it up.

class MainActivity: AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_main)
                setOneTimeWorkRequest()
        }
        
        private fun setOneTimeWorkRequest() {
                val uploadRequest = OneTimeWorkRequest.Builder(UploadWorker::class.java)
                        .build()
                WorkManager.getInstance(applicationContext)
                        .enqueue(uploadRequest)
        }
}

Get status updates

For each work request, WorkManager allows us to get a LiveData<WorkInfo>. By observing it, we can determine the current status.

class MainActivity: AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_main)
                setOneTimeWorkRequest()
        }
        
        // change this
        private fun setOneTimeWorkRequest() {
                val workManager = WorkManager.getInstance(applicationContext)
                val uploadRequest = OneTimeWorkRequest.Builder(UploadWorker::class.java)
                        .build()
                workManager.enqueue(uploadRequest)
                workManager.getWorkInfoByIdLiveData(uploadRequest.id)
                        .observe(this, Observer {
                                // this is just a TextView to show it off
                                textView.text = it.state.name
                        })
        }
}

Set up constraints

Sometimes we want to set up constraints. For example, if we want to download a large video, this consumes too much battery and we want to do it only if the battery’s connected.

class MainActivity: AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_main)
                setOneTimeWorkRequest()
        }
        
        private fun setOneTimeWorkRequest() {
                val workManager = WorkManager.getInstance(applicationContext)
                // added this
                val constraints = Constraints.Builder()
                        .setRequiresCharging(true)
                        .setRequiredNetworkType(NetworkType.CONNECTED)
                        .build()
                
                val uploadRequest = OneTimeWorkRequest.Builder(UploadWorker::class.java)
                        // added this
                        .setConstraints(constraints)
                        .build()
                workManager.enqueue(uploadRequest)
                workManager.getWorkInfoByIdLiveData(uploadRequest.id)
                        .observe(this, Observer {
                                // this is just a TextView to show it off
                                textView.text = it.state.name
                        })
        }
}

Pass data to workers

Sometimes we need to pass data to the workers. It’s also possible to send data from the Workers to the Activity.

class MainActivity: AppCompatActivity() {

        // added this
        companion object {
                const val KEY_COUNT_VALUE = "key_count"
        }

        override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_main)
                setOneTimeWorkRequest()
        }
        
        private fun setOneTimeWorkRequest() {
                val workManager = WorkManager.getInstance(applicationContext)

                // added this
                val data = Data.Builder()
                        .putInt(KEY_COUNT_VALUE, 125)
                        .build()
                
                val constraints = Constraints.Builder()
                        .setRequiresCharging(true)
                        .setRequiredNetworkType(NetworkType.CONNECTED)
                        .build()
                
                val uploadRequest = OneTimeWorkRequest.Builder(UploadWorker::class.java)
                        .setConstraints(constraints)
                        // added this
                        .setInputData(data)
                        .build()
                workManager.enqueue(uploadRequest)
                workManager.getWorkInfoByIdLiveData(uploadRequest.id)
                        .observe(this, Observer {
                                // this is just a TextView to show it off
                                textView.text = it.state.name
                        })
        }
}

We get it like this at our Worker.

class UploadWorker(context: Context, params: WorkerParameters): Worker(context, prams) {
        override fun doWork(): Result {
                try {
                        // 0 is the default value
                        val count = inputData.getInt(MainActivity.KEY_COUNT_VALUE, 0)
                        // add your work here
                        return Result.success()
                } catch(e: Exception) {
                        return result.failure()
                }
        }
}

Periodic work requests

For now, Android Jetpack has a minimum repeat time of 15 minutes. The only difference is we need use PeriodicWorkRequest class instead.

fun setPeriodicWorkRequest() {
        val periodicWorkRequest = PeriodicWorkRequest
                .Builder(DownloadingWorker::class.java, 16, TimeUnit.MINUTES)
                .build()
        WorkManager.getInstance(applicationContext).enqueue(periodicWorkRequest)
}