Last week I wrote an Android 12 adaptation note, which mentioned that Android 12 has more stringent restrictions on starting ForegroundService while the app is running in the background. I suggested using WorkManager instead. In fact, the first stable version of WorkManager was released in early nineteen nineteen, but I haven’t used it before, so I took this opportunity to learn about it.

The WorkManager profile

WorkManager is the latest solution for handling background tasks, compatible as low as Android 4.0, and was developed with battery life in mind. This does not apply to background work that terminates when the application is shut down, or work that needs to be executed immediately.

WorkManager has many advantages, of which I think the most important are:

  • Operating constraints

Constraints can be used to specify the best scenario for work to run. For example, when only Wifi is connected, there is enough storage space.

  • Work chain

Interfaces can be used to concatenate tasks, making it easy to control which tasks are executed in sequence and which are executed in parallel.

  • reliability

The scheduled work is stored in Sqlite built into WorkManager to ensure that the work is completed. Even if the device is restarted midway, WorkManager automatically resumes the work after the device is restarted.

Simple use of WorkManager

1. Introduce WorkManager into your project

Add a dependency to defaultConfig in build.gradle of the project app Module:

Implementation "androidx.work:work-runtime:$work_version" // Kotlin + coroutines implementation "androidx.work:work-runtime-ktx:$work_version" // optional - RxJava2 support implementation "androidx.work:work-rxjava2:$work_version" // optional - GCMNetworkManager support implementation "androidx.work:work-gcm:$work_version" // optional - Test helpers androidTestImplementation "androidx.work:work-testing:$work_version" // optional - Multiprocess support implementation "androidx.work:work-multiprocess:$work_version" }Copy the code

Define the Worker class

Inherit the Worker class, rewrite the doWork() method, and write the code for the task to be performed in the doWork() method. The return value of the method will inform the WorkManager Service whether the work is successful or not, and whether to retry if it fails.

class UploadWorker(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) { override fun doWork(): Result {// code for the task to be executed // Work completed return result.success () // work failed return result.failure () // Work failed Return result.retry ()}}Copy the code

3. Create and configure WorkRequest

After defining the Worker class, WorkRequest is used to define how and when the work runs.

  • Create WorkRequest

WorkRequest is divided into OneTimeWorkRequest and PeriodicWorkRequest:

// Val oneTimeWorkRequest: WorkRequest = OneTimeWorkRequestBuilder < UploadWorker > (). The build () / / periodic work request val periodicWorkRequest: WorkRequest = PeriodicWorkRequestBuilder < UploadWorker > (/ / two work time interval, the unit of time for 1 hour, TimeUnit. HOURS, / / near time interval before the starting time of the work, TimeUnit: 15 MINUTES, timeunit.minutes).build()Copy the code
  • Create Constraints

You can add the following constraints to WorkRequest, and the work will be executed when the conditions are met:

Val constraints = constraints.Builder() // Set network type UNMETERED (Wifi) METERED(traffic) NOT_REQUIRED (unrestricted) .setrequiredNetworkType (networktype.unmetered) // Sets whether to restrict running when battery is low true(low battery limit) false (no limit) SetRequiresBatteryNotLow (true) / / Settings are only work in charging run true (charging run only) or false (not limited) setRequiresCharging (false) / / set up not only in idle equipment when working SetRequiresDeviceIdle (false) // Set whether to limit the running when the storage space is insufficient. True (Limit when the storage space is insufficient) false(No limit) .setRequiresStorageNotLow(true) .build() val oneTimeWorkRequest: WorkRequest = OneTimeWorkRequestBuilder<UploadWorker>() .setConstraints(constraints) .build()Copy the code
  • Setting delay time

Use the following code to configure the delay before work begins:

val oneTimeWorkRequest: WorkRequest = OneTimeWorkRequestBuilder < UploadWorker > () / / set the delay time, the unit is in SECONDS. SetInitialDelay (10, TimeUnit. SECONDS). The build ()Copy the code
  • Setting retry Parameters

Use the following code to configure the retry parameters for work failure:

val oneTimeWorkRequest: WorkRequest = OneTimeWorkRequestBuilder < UploadWorker > () / / set to retry parameters. The setBackoffCriteria (/ / retry time interval between the growth model of LINEAR (such as 20 to 30 40) EXPONENTIAL (e.g., 20 40 80) backoffpolicy. LINEAR, // First retry interval onetimeworkRequest.min_backoff_millis, Timeunit.milliseconds).build()Copy the code
  • Setting the work label

Use the following method to configure a unique identifier for work, which can be used to observe progress or cancel work:

Val oneTimeWorkRequest: WorkRequest = OneTimeWorkRequestBuilder < UploadWorker > () / / add a unique identifier. AddTag (" upload log "). The build ()Copy the code
  • Incoming parameters

The following code shows how to configure the parameters that need to be passed into the Worker and how to get them:

val oneTimeWorkRequest: WorkRequest = OneTimeWorkRequestBuilder < UploadWorker > () / / set the incoming parameters. The setInputData (workDataOf (" UPLOAD_PATH "to" https://.." , "DATA" to "..." )) .build() class UploadWorker(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) { override fun doWork(): Var uploadPath = inputData.getString("UPLOAD_PATH")? : return Result.failure() val data = inputData.getString("DATA") ? : return Result.failure() return Result.success() } }Copy the code
  • Submit WorkRequest

Submit WorkRequest to WorkManager with the following code:

        val oneTimeWorkRequest: WorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
            .build()
        WorkManager.getInstance(this).enqueue(oneTimeWorkRequest)
Copy the code

4. Get information about the job

You can get information about the job by using the following code:

val oneTimeWorkRequest: WorkRequest = OneTimeWorkRequestBuilder<UploadWorker>() .build() val workInfo = The workManager. GetWorkInfoById (oneTimeWorkRequest. Id). The get () / / id workInfo. Id / / state workInfo. State / / label workInfo. Tags // result.success () result.failure () sets the output parameter workinfo.outputDataCopy the code

Or you can use the following code to monitor the status of work in real time:

val oneTimeWorkRequest: WorkRequest = OneTimeWorkRequestBuilder<UploadWorker>() .build() workManager.getWorkInfoByIdLiveData(oneTimeWorkRequest.id).observe(lifecycleOwner, {workInfo -> //id workinfo.id // status workinfo.state // tags workinfo.tags // result.success () result.failure () Output parameters set workInfo.outputData })Copy the code

5. Cancel work

Work can be cancelled by:

val oneTimeWorkRequest: WorkRequest = OneTimeWorkRequestBuilder < UploadWorker > (). The build () / / cancel specified worker The workManager. CancelWorkById (oneTimeWorkRequest. Id) / / cancel all the work the workManager. CancelAllWork ()Copy the code