It took two weekends to write it. It was not easy to create it.
Csdn:blog.csdn.net/u013309870/… The Denver nuggets: juejin. Cn/post / 684490…
preface
Before, I wrote a polling framework with IntentService, but it was not very good. Later, I always wanted to find another way to rewrite it. After searching a lot of information, I found WorkManager, which is a very excellent background task management framework provided by Google. Tasks submitted to The WorkManager can be executed immediately or when appropriate, once or repeatedly based on conditions. For multiple tasks, the WorkManager can manage the execution path of the tasks and their execution sequence. This article first introduces the use of WorkManager, and then uses WorkManager to refactor the previous polling framework.
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:
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.
dependencies {
// Other dependency configurations
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… Gets the current work-Runtime version and sets the correct version number.
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:
Worker
Worker is an abstract class. When there is a task to be executed, it can inherit the Worker class and rewrite the doWork() method to realize the logic of specific tasks 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(a) {
Context applicationContext = getApplicationContext();
try {
Bitmap picture = BitmapFactory.decodeResource(
applicationContext.getResources(),
R.drawable.test);
return Worker.Result.SUCCESS;
} catch (Throwable throwable) {
returnWorker.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
WorkRequest
WorkRequest 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, OneTimeWorkRequest
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 WorkRequest
Copy 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
You 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
Canceling a Task
The task ID can be used to obtain the task and 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 Multi-task scheduling
Sometimes there may be a lot of tasks that need to be performed, and those tasks may be preceded by sequencing or dependencies, and WorkManager provides a good way to do that. 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 be used for more complex multipathing execution. WorkContinuation.combine(List
) can be used 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 with WorkManager
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. Therefore, AlarmManager can be used instead of WorkManager to implement precise interval execution.
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
* @since2019/1/23 * /
public class PollingWorker extends Worker {
public static final String TAG = "PollingWorker";
@NonNull
@Override
public Result doWork(a) {
Log.d(TAG, "doWork");
try {
polling();
runOnUIThread(new Runnable() {
@Override
public void run(a) {
// Update UI operations}});return Result.SUCCESS;
} catch (Exception e) {
Log.d(TAG, "failure");
returnResult.FAILURE; }}private void polling(a) {
Log.d(TAG, "Polling");
}
// Throw it into the main thread
private void runOnUIThread(Runnable runnable) {
newHandler(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. To be continued…….. Refactoring the previous polling framework by the end of next week: juejin.cn/post/684490…
reference
1, developer.android.com/topic/libra…
2, codelabs.developers.google.com/codelabs/an…
My CSDN address
blog.csdn.net/u013309870