[[428478]] This article is reprinted from the WeChat public account "Android Development Programming", the author is Android Development Programming. Please contact the Android Development Programming public account for reprinting this article. Preface WorkManager is a management framework for asynchronous execution of tasks provided by Google. It will select the appropriate method to execute tasks according to the API version of the phone and the status of the application. When the application is running, a thread will be opened in the application process to execute the task. When the application is exited, WorkManager will choose to call JobScheduler or Firebase JobDispatcher or AlarmManager to execute the task using the appropriate algorithm based on the API version of the device; Today we will introduce; 1. Introduction to WorkManager 1. What is WorkManager- WorkManager is an API that makes it easy to schedule deferrable asynchronous tasks that should run even when you exit your app or reboot the device. The WorkManager API is a suitable, recommended replacement for previous Android background scheduling APIs, including FirebaseJobDispatcher, GcmNetworkManager, and JobScheduler. WorkManager incorporates the functionality of its predecessors in a new consistency API that supports API level 14 while maintaining battery life;
- WorkManager is suitable for deferrable work, that is, work that does not need to be run immediately but needs to be run reliably, even if the user quits or the device restarts. For example: sending logs or analysis data to backend services, regularly synchronizing application data with the server;
- WorkManager is not suitable for running background work that can be safely terminated when the application process ends, nor for work that needs to be executed immediately;
2. Advantages- Backward compatible to API 14;
- You can add constraints for task execution, such as delayed execution, whether to execute in low-power mode, whether to execute in charging mode, whether to execute when the device is idle, etc.
- Scheduling one-time or periodic asynchronous tasks;
- Supervision tasks, can cancel tasks at any time;
- Chain tasks together, for example, you can specify the execution order of multiple tasks;
- Ensures task execution even if the application or device is restarted;
2. Use of WorkManager 1. Add dependencies- //Add dependencies according to project needs, no need to add all
- dependencies {
- def work_version = "2.3.1"
- // (Java only )
- implementation "androidx.work:work-runtime:$work_version" //Select this for Java language
- // Kotlin + coroutines
- implementation "androidx.work:work-runtime-ktx:$work_version" //kotlin choose this
- }
2. Create a background task Upload pictures: - class UploadPicWork(
- private val context: Context,
- private val workerParameters: WorkerParameters
- ) :
- Worker(context, workerParameters) {
- override fun doWork(): Result {
- uploadPic()//Specific logic for uploading pictures
- return Result.success()
- }
- }
2.1 Create a workrequest- //The UploadPicWork here is the task created previously
- val uploadPicWork = OneTimeWorkRequestBuilder<UploadPicWork>()
- .setConstraints(triggerContentMaxDelay).build()
2.2 Execution of tasks- //The uploadPicWork here is the workrequest created in the previous step
- WorkManager.getInstance(myContext).enqueue(uploadPicWork)
3. Complex task processing 3.1 Create task execution constraints- //Note that the following conditions are all && relationships
- val triggerContentMaxDelay =
- Constraints.Builder()
- .setRequiredNetworkType(NetworkType.CONNECTED) //Used when the network is connected to avoid various network judgments, saving time and effort
- .setRequiresDeviceIdle( false ) //Whether to execute when the device is idle
- .setRequiresBatteryNotLow( true ) //Whether to execute when the battery is low
- .setRequiresStorageNotLow( true ) //Whether to execute when memory is insufficient
- .setRequiresCharging( true ) //Whether to execute when charging
- .setTriggerContentMaxDelay(1000 * 1, TimeUnit.MILLISECONDS) // Delay execution
- .build()
3.2 Adding constraints to tasks- val uploadPicWork =
- OneTimeWorkRequestBuilder<UploadPicWork>()
- .setConstraints(triggerContentMaxDelay) //Constraints
- .build()
- WorkManager.getInstance(myContext).enqueue(uploadPicWork)//Execute
3.3 Passing parameters to workers- // You can pass parameters in this way
- val UploadPicWork =
- OneTimeWorkRequestBuilder<UploadPicWork>()
- //The parameter required for set input data here is a Data object. Note that it can only be added once. If there are multiple parameters that need to be passed, they can be encapsulated into a data class.
- .setInputData(workDataOf( "params_tag" to "params" ))
- .setConstraints(triggerContentMaxDelay).build()
3.4 Get Parameters- class UploadPicWork(
- private val context: Context,
- private val workerParameters: WorkerParameters
- ) :
- Worker(context, workerParameters) {
- override fun doWork(): Result {
- val params = inputData.getString( "params_tag" ) //Get the passed parameters
- uploadPic() //Upload pictures
- return Result.success()
- }
- }
4. Worker Status In the doWork function, we return Result.success(); We assume that the uploadPic task is successfully executed, so it returns a success status. However, in the actual development process, it may fail due to various problems, and then it cannot return success; Description of various statuses of Worker - If there is any prerequisite work that has not been completed, the work is in BLOCKED State;
- If a job can be run immediately after the constraints and timing conditions are met, it is considered to be in the ENQUEUED state;
- When a Worker is actively executing, it is in the RUNNING State;
- If the Worker returns Result.success(), it is considered to be in the SUCCEEDED state. This is a terminal state; only OneTimeWorkRequest can enter this state;
- If a Worker returns Result.failure(), it is considered to be in the FAILED state. This is also a terminal state; only OneTimeWorkRequest can enter this state. All dependent work will also be marked as FAILED and will not run;
- When a WorkRequest that has not yet been terminated is canceled, it enters the CANCELLED State. All dependent work will also be marked as CANCELLED and will not run;
5. Observe the status of the Worker Get WorkInfo After getting the ID, you can use WorkManager.getWorkInfoById(UUID) or WorkManager.getWorkInfoByIdLiveData(UUID) to get WorkInfo by WorkRequest id; - WorkManager.getInstance(this)
- .getWorkInfoByIdLiveData(UploadPicWork.id)// Get by id
- .observe(this, Observer { //it:WorkInfo
- it?.apply {
- when (this.state) {
- WorkInfo.State.BLOCKED -> println( "BLOCKED" )
- WorkInfo.State.CANCELLED -> println( "CANCELLED" )
- WorkInfo.State.RUNNING -> println( "RUNNING" )
- WorkInfo.State.ENQUEUED -> println( "ENQUEUED" )
- WorkInfo.State.FAILED -> println( "FAILED" )
- WorkInfo.State.SUCCEEDED -> println( "SUCCEEDED" )
- else -> println( "else status ${this.state}" )
- }
- }
- })
To obtain through tag, you can use WorkManager.getWorkInfosByTag(String) or WorkManager.getWorkInfosByTagLiveData(String) to get the WorkInfo object of WorkRequest; - //To obtain through tag, you need to set tag first
- val UploadPicWork =
- OneTimeWorkRequestBuilder<UploadPicWork>()
- .setInputData(workDataOf( "params_tag" to "params" )) // pass parameters
- .setConstraints(triggerContentMaxDelay) //Set constraints
- .addTag( "tag" ) //Set tag
- .build()
- //Get workInfo
- WorkManager.getInstance(this)
- .getWorkInfosByTagLiveData( "tag" )
- .observe(this, Observer {it:List<WorkInfo>//This returns a collection, as a sample code, only 0 index is taken by default
- it?.apply {
- when (this[0].state) {
- WorkInfo.State.BLOCKED -> println( "BLOCKED" )
- WorkInfo.State.CANCELLED -> println( "CANCELLED" )
- WorkInfo.State.RUNNING -> println( "RUNNING" )
- WorkInfo.State.ENQUEUED -> println( "ENQUEUED" )
- WorkInfo.State.FAILED -> println( "FAILED" )
- WorkInfo.State.SUCCEEDED -> println( "SUCCEEDED" )
- else -> println( "else status ${this[0]}" )
- }
- }
- })
6. Sequential execution of multiple workers You can use WorkManager to create and queue work chains. Work chains are used to specify multiple related tasks and define the order in which these tasks are run. This is particularly useful when you need to run multiple tasks in a specific order; 6.1 Execute individual tasks sequentially For example, if there are three tasks workA, workB, and workC, and the execution order can only be workA---->workB---->workC, it can be handled as follows; - WorkManager.getInstance()
- .beginWith(workA)
- . then (workB) instance
- . then (workC)
- .enqueue();
The above workA, workB, and workC are all subclass implementation objects of WorkRequest. WorkManager will execute workA, workB, and workC in the order above, but if one of the three tasks fails during the execution, the entire execution will end and return Result.failure() 6.2 Execute multiple tasks in sequence Sometimes you may need to execute one set of tasks before executing the next set of tasks. You can use the following method to accomplish this. - WorkManager.getInstance()
-
- // First , run all the A tasks ( in parallel):
-
- .beginWith(Arrays.asList(workA1, workA2, workA3))
-
- // ... when all A tasks are finished, run the single B task:
-
- . then (workB)
-
- // ... then run the C tasks ( in any order ):
-
- .then (Arrays.asList(workC1, workC2))
-
- .enqueue();
7. Perform repetitive tasks It is to execute tasks regularly at given time intervals, such as reporting location information every hour, backing up a log every 3 hours, etc. This time interval cannot be less than 15 minutes; - val triggerContentMaxDelay =
- Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED)
- // .setRequiresDeviceIdle( false )
- .setRequiresBatteryNotLow( true )
- .setRequiresStorageNotLow( true )
- .setRequiresCharging( true )
- .setTriggerContentMaxDelay(1000 * 1, TimeUnit.MILLISECONDS)
- .build()
- // val UploadPicWork =
- // OneTimeWorkRequestBuilder<UploadPicWork>()
- // .setInputData(workDataOf( "params_tag" to "params" ))
- // .setConstraints(triggerContentMaxDelay)
- // .addTag( "tag" )
- // .build()
- //
- val build = PeriodicWorkRequestBuilder<UploadPicWork>(
- 1000*60*15,
- TimeUnit.MICROSECONDS
- ).setConstraints(triggerContentMaxDelay).build()
- WorkManager.getInstance(this).enqueue(build)
8. Cancel task execution The task ID can be used to obtain the task and cancel it. The task ID can be obtained from the WorkRequest; cancelAllWork(): cancel all tasks; cancelAllWorkByTag(String tag): cancel a group of tasks with the same tag; cancelUniqueWork(String uniqueWorkName): cancel the unique task; - UUID compressionWorkId = compressionWork.getId();
- WorkManager.getInstance().cancelWorkById(compressionWorkId);
Note that not all tasks can be canceled. When a task is being executed, it cannot be canceled. Of course, it is also meaningful to cancel the task after it is completed. That is to say, the task can be canceled only when it is added to the ManagerWork queue but has not been executed. 9. Problems encountered when using WorkManager 9.1 Use PeriodicWorkRequest only once, not repeatedly- WorkManager instance= new PeriodicWorkRequest.Builder(PollingWorker.class, 10, TimeUnit.MINUTES)
- .build();
Reason: The default time interval of PeriodicWorkRequest is 15 minutes. If the time is set to less than 15 minutes, problems will occur; Solution: The default time must be greater than or equal to 15 minutes. Also, please note that even if the time is set to 15 minutes, it does not necessarily mean that the execution will be executed every 15 minutes; 9.2 Updating the UI in the doWork() method causes a crash Cause: The doWork() method is executed in the background thread managed by WorkManager. UI updates can only be performed in the main thread. Summarize In this materialistic and impetuous society, let us study together and encourage each other. |