space.bilibili.com/474380680

What is the Workmanager

WorkManager is a management framework for asynchronous task execution provided by Google. It selects the appropriate way to execute tasks according to the API version of the phone and the state of the application. When the application is running, a thread is opened in the application process to execute tasks. When the application is exiting, WorkManager chooses to call JobScheduler, Firebase JobDispatcher, or AlarmManager with the appropriate algorithm based on the API version of the device to perform the task. The diagram below:

By the picture above you can see the WorkManager management task executes the bottom or call JobScheduler, JobDispatcher, AlarmManager, but the WorkManager will according to the Android API and application of running state to choose the right way, It is not up to us to consider applying complex running states to choose between using JobScheduler or JobDispatcher or AlarmManager call rules as shown below:

WorkManager is configured in the project

Using WorkManager requires gradle dependencies, which can be used with a few simple configurations. Go to the app/build.gradle directory of your project and add the following dependencies to it.

Def work_version = "1.0.0-beta02" implementation "android.arch.work:work-runtime:$work_version" }Copy the code

Can the developer.android.com/topic/libra…

WorkManager main classes and usage

The following figure shows the main classes and diagrams in WorkManager. The yellow area is the three most important classes, which constitute the basic framework of WorkManager. The red part and the green part are the specific implementation of the associated yellow part or the class contains some rules or data.

1. Specific logic of Worker processing tasks to be executed.

2. WorkerRequest represents an independent task that can be executed, and the conditions and rules when the task is executed, such as whether the task is executed once or multiple times, what are the trigger conditions of the task and what are the constraints of the task.

3. WorkManager provides a queue to manage and execute workerRequests to be executed.As shown below, the relationship between the three main classes:

The following describes the functions and usage of the three classes.

Worker Worker is an abstract class. When there is a task to be executed, you can inherit the Worker class and rewrite the doWork() method to implement the logic of the specific task in the doWork() method.

public class MyWorker extends Worker { public MyWorker( @NonNull Context appContext, @NonNull WorkerParameters workerParams) { super(appContext, workerParams); } @NonNull @Override public Worker.Result doWork() { Context applicationContext = getApplicationContext(); try { Bitmap picture = BitmapFactory.decodeResource( applicationContext.getResources(), R.drawable.test); return Worker.Result.SUCCESS; } catch (Throwable throwable) { return Worker.Result.FAILURE; }}}Copy the code

In the above MyWorker instance, it inherits the Worker and overwrites the doWork() method. It should be noted that the doWork() method returns worker.result, which can return worker.result.success when the task is successfully executed. The worker.result. FAILURE doWork() method returns three main values when an exception occurs during task execution. 1. Worker.result. SUCCESS indicates that the task is successfully executed

Worker.result. FAILURE indicates that the task fails to be executed

3. Worker.result. RETRY Notifies WorkManager to RETRY the task

WorkRequestWorkRequest specifies the Worker to perform the task. You can also add rules to WorkRequest, such as when the task should be executed, whether the task should be executed once or multiple times. Each WorkRequest has a unique ID that is automatically generated. You can obtain the status of a task based on its unique ID and determine whether to cancel the task. WorkRequest has two implementation classes as shown below:

1. The OneTimeWorkRequest task is executed only once

OneTimeWorkRequest myWorkRequest = new OneTimeWorkRequest.Builder(MyWorker.class) .build(); // Add the MyWorker defined above to the OneTimeRequest.Builder method workManager.getInstance ().enqueue(myWorkRequest); // Get the WorkManager instance and queue WorkRequestCopy the code

PeriodicWorkRequest PeriodicWorkRequest repeats tasks until they are cancelled. The first execution is immediately after the task is committed or if the Constraints condition given is satisfied. Subsequent executions will be based on the given interval. Note that there may be delays in the execution of tasks, as WorkManager optimizes based on OS power. If Periodic Work is set to be executed once in 24 hours, the execution process may be as follows according to the battery optimization strategy:

     1     | Jan 01, 06:00 AM
     2     | Jan 02, 06:24 AM
     3     | Jan 03, 07:15 AM
     4     | Jan 04, 08:00 AM
     5     | Jan 05, 08:00 AM
     6     | Jan 06, 08:02 AM
Copy the code

As can be seen from the above execution times, PeriodicWorkRequest is not executed exactly in 24 hours, with a certain amount of time delay. Therefore, PeriodicWorkRequest should not be used if precise intervals are required to perform tasks.

Constraints constraints = new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build(); PeriodicWorkRequest build = new PeriodicWorkRequest.Builder(MyWorker.class, 25, TimeUnit.MILLISECONDS) .addTag(TAG) .setConstraints(constraints) .build(); WorkManager instance = WorkManager.getInstance(); if (instance ! = null) { instance.enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, build); }Copy the code

Constraints can add Constraints conditions to the task to run, such as when the device is idle or is charging or connected to WiFi.

Constraints myConstraints = new Constraints.Builder()
    .setRequiresDeviceIdle(true)
    .setRequiresCharging(true)
    // Many other constraints are available, see the
    // Constraints.Builder reference
     .build();
OneTimeWorkRequest myWork =
                new OneTimeWorkRequest.Builder(CompressWorker.class)
     .setConstraints(myConstraints)
     .build();
Copy the code

WorkManager WorkManager Manages WorkRequest queues. And according to the equipment and other conditions to choose the specific way of implementation. In most cases, if Contraints are not added to the queue, the WorkManager will execute the task immediately.

WorkManager.getInstance().enqueue(myWork);
Copy the code

To check the execution status of a task, you can obtain WorkInfo from LiveData in WorkManager. Here’s how to tell if the task is over.

WorkManager.getInstance().getWorkInfoByIdLiveData(myWork.getId())
    .observe(lifecycleOwner, workInfo -> {
        // Do something with the status
        if (workInfo != null && workInfo.getState().isFinished()) {
            // ...
        }
    });
Copy the code

Cancel task execution You can obtain the task ID to cancel the task. The task ID can be obtained from WorkRequest.

UUID compressionWorkId = compressionWork.getId();
WorkManager.getInstance().cancelWorkById(compressionWorkId);
Copy the code

Note that not all tasks can be cancelled. They cannot be cancelled while the task is in progress, but can only be cancelled when the task is completed, that is, when the task is added to the queue of ManagerWork but has not yet been executed.

WorkManager provides a good way to schedule multiple tasks that may need to be executed in order or with dependencies. For example, if there are three tasks workA,workB, and workC, the execution sequence can only be workA—->workB—->workC.

WorkManager.getInstance()
    .beginWith(workA)
    .then(workB)  instance
    .then(workC)
    .enqueue();
Copy the code

WorkA,workB, and workC above are all subclass implementation objects of WorkRequest. WorkManager executes workA,workB,workC, in the order above, but if one of the three tasks fails, the entire execution ends. And return result.failure (). In some cases, you may need to perform one set of tasks first and then the next. This can be done in the following way.

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();Copy the code

Multipathing execution The preceding two methods are single-path execution and can achieve more complex multipathing execution. WorkContinuation.combine(List) is shown as follows:

WorkContinuation chain1 = WorkManager.getInstance()
    .beginWith(workA)
    .then(workB);
WorkContinuation chain2 = WorkManager.getInstance()
    .beginWith(workC)
    .then(workD);
WorkContinuation chain3 = WorkContinuation
    .combine(Arrays.asList(chain1, chain2))
    .then(workE);
chain3.enqueue();
Copy the code

Problems encountered with WorkManager 1. PeriodicWorkRequest is executed only once, not repeatedly.

     WorkManager instance= new PeriodicWorkRequest.Builder(PollingWorker.class, 10, TimeUnit.MINUTES)
                .build();
Copy the code

Reason: The default PeriodicWorkRequest interval is 15 minutes. Problems occur if the interval is set to less than 15 minutes.

Solution: The default time must be greater than or equal to 15 minutes. Also note that even if you set the time to 15 minutes, you don’t have to do it every 15 minutes.

Updating the UI in the doWork() method crashes. Reason: The doWork() method is executed in a background thread managed by the WorkManager, and UI updates can only be performed in the main thread.

Solution: When the doWork() time-consuming method is finished, the UI update operation is thrown into the main line for execution, which can be implemented with Handle as follows:

/** * Created time 15:32. * * @author huhanjun * @since 2019/1/23 */ public class PollingWorker extends Worker { public static final String TAG = "PollingWorker"; @NonNull @Override public Result doWork() { Log.d(TAG, "doWork"); try { polling(); RunOnUIThread (new Runnable() {@override public void run() {// Update UI operation}}); return Result.SUCCESS; } catch (Exception e) { Log.d(TAG, "failure"); return Result.FAILURE; } } private void polling() { Log.d(TAG, "Polling"); } private void runOnUIThread(Runnable runnable) { new Handler(Looper.getMainLooper()).post(runnable); }}Copy the code

After executing the Polling method, the update is passed to the runOnUIThread() method so that the update can be performed where the code comments above.