1. Introduction
When we want to implement the processing that is not urgent but needs to be done, it is difficult to implement the extra power consumption if using service, difficult to implement if using Broadcast and need to set trigger conditions. In order to solve this problem, Google introduced JobScheduler in Android 5.0 to replace the previous solution. As a further release of WorkManager on Android Jetpack, it will implement these requirements in different ways depending on the version of the system.
2. A brief introduction to WorkManager
WorkManager is an Android library that gracefully runs deferred background work when working triggers, such as appropriate network state and battery conditions, are met. Where possible, WorkManager uses the framework JobScheduler to help optimize battery life and batch jobs. On devices below Android 6.0 (API level 23), try Firebase JobDispatcher if the WorkManager already includes your application's dependencies. Otherwise, WorkManager returns to the custom AlarmManager implementation to gracefully handle your background work.
According to the official narrative, WorkManager automatically performs background tasks based on constraints. If the system version is higher than 6.0, JobScheduler is used; if AlarmManager+BroadCastReceiver is used, AlarmManager+BroadCastReceiver is used.
3. Usage
3.1 Adding a Dependency
Add the following dependencies to build.gradle in the Module.
implementation "Androidx. Work: work - the runtime - KTX: 2.3.3." "
Copy the code
3.2 Creating background Processing
We first need to create tasks that need to be executed. We need to inherit the Worker class, and we need to override the doWork method.
class CustomWorker(context: Context, workerParameters: WorkerParameters) :
Worker(context, workerParameters) {
override fun doWork(a): Result {
Log.d("CustomWorker"."Worker is active in " + inputData.getString("data"))
return Result.success()
}
}
Copy the code
Inputdata.getstring (key) is to get the external passed value. Use the same method as an intent to pass values. The value we need to return is the enumerated value of Result. There are several kinds as follows.
Result.success()
The task is successfully executed.Result.retry()
The task fails and needs to be tried again. Rules of retreat that can be described laterBackoffPolicy
The setting method is tried.Result.failure()
Indicates that the task fails to be executed but will not be attempted again.
3.3 Creating trigger restrictions
We need to set constraints on the trigger by Constrains.
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.setRequiresStorageNotLow(true)
.setRequiresCharging(true)
.setRequiresDeviceIdle(true)
.build()
Copy the code
3.3.1 setRequiredNetworkType
Set the required network type. There are several kinds as follows.
Enumerated values | State that |
---|---|
NOT_REQUIRED | You don’t need the Internet |
CONNECTED | Any available network |
UNMETERED | Need unmetered network, such as WiFi |
NOT_ROAMING | A non-roaming network is required |
METERED | Need a metering network, such as 4G |
3.3.2 rainfall distribution on 10-12 setRequiresBatteryNotLow
Set the battery status of the mobile phone. The condition here is that the battery of the mobile phone should not be low during the task.
3.3.3 setRequiresStorageNotLow
Set the state of the phone’s memory space, as long as the phone’s memory space is not low when performing tasks.
3.3.4 setRequiresCharging
Set the charging state of the mobile phone. The condition here is that the mobile phone is in the charging state when performing the task.
3.3.5 setRequiresDeviceIdle
Set the state of the phone. The condition here is that the phone is idle when performing the task.
3.3.6 addContentUriTrigger (uri: uri, triggerForDescendants: Boolean)
Add a trigger that fires a task when there is an update in the specified URI. It is worth noting that this can only be set in OneTimeWorkRequest.
3.4 create WorkRequest
There are two types of WorkRequest. OneTimeWorkRequest is executed once, and PeriodicWorkRequest is executed periodically.
val oneTimeWorkRequest =
OneTimeWorkRequestBuilder<CustomWorker>().setConstraints(constraints)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS
)
.setInputData(inputData)
.build()
val periodicWorkRequest =
PeriodicWorkRequestBuilder<CustomWorker>(10, TimeUnit.MILLISECONDS)
.setConstraints(constraints)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
PeriodicWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS
).build()
Copy the code
In the PeriodicWorkRequest constructor, you also need to add an execution cycle, which validates the condition every 10 seconds. However, the interval between periodic tasks is at least 15 minutes. Therefore, even if a task is written for 10 seconds, it will be executed 15 minutes later.
3.4.1 track setConstraints
We need to set Constraints(the Constraints described above) in the WorkRequest.
3.4.2 setBackoffCriteria
We can also set retreat conditions. Retreat conditions have two properties:
BackoffPolicy
, which defaults to exponential, but can be set to linear.- Duration. Default value: 30 seconds.
Rule 3.4.3 setInputData
We can pass values to the Worker through the setInputData method in WorkRequest. Use the same method as Intent, as follows.
val inputData = Data.Builder().putString("data"."MainActivity").build()
Copy the code
3.5 Using WorkManager
Finally, we can implement the requirements specified above through WorkManager.
WorkManager.getInstance(this).enqueue(oneTimeWorkRequest)
Copy the code
3.6 Monitoring Worker execution
We can also monitor the execution of workers through LiveData. When the Worker is executed, the monitoring office is notified.
WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest.id).observe(this,
Observer {
count++
txtId.text = "Work status is changed! $count"
Toast.makeText(this."Work status is changed!",Toast.LENGTH_LONG).show()
})
Copy the code
The source code for WorkInfo is as follows:
private @NonNull UUID mId;
private @NonNull State mState;
private @NonNull Data mOutputData;
private @NonNull Set<String> mTags;
private @NonNull Data mProgress;
private int mRunAttemptCount;
/ * * *@hide* /
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public WorkInfo(
@NonNull UUID id,
@NonNull State state,
@NonNull Data outputData,
@NonNull List<String> tags,
@NonNull Data progress,
int runAttemptCount) {
mId = id;
mState = state;
mOutputData = outputData;
mTags = new HashSet<>(tags);
mProgress = progress;
mRunAttemptCount = runAttemptCount;
}
Copy the code
So let’s first look at State.
public enum State {
ENQUEUED,// Join the queue
RUNNING,/ / run
SUCCEEDED,/ / has been successful
FAILED,/ / fail
BLOCKED,/ / hung
CANCELLED;/ / cancel
public boolean isFinished() {
return (this == SUCCEEDED || this == FAILED || this== CANCELLED); }}Copy the code
When BLOCKED is BLOCKED, the Worker will be suspended if the constraints are not fully met.
3.7 Ending a Task
After the WorkRequest is listed, The WorkManager will assign a work ID to it. We can cancel or stop the task by using the work ID.
WorkManager.getInstance(this).cancelWorkById(workRequest.id)
Copy the code
WorkManager does not necessarily have the ability to end a task, because some tasks may have already completed. There are other ways to end a task besides the above methods:
cancelAllWork()
: Cancel all taskscancelAllWorkByTag(tag:String)
: Cancels a set of tasks with the same labelcancelUniqueWoork(uniqueWorkName:String)
: Cancels a unique task
3.8 Chain call
When we need to execute the WorkRequest sequentially or simultaneously, we can use the chain-called method to execute it.
3.8.1 Parallel Execution
val workRequest1 = OneTimeWorkRequestBuilder<CustomWorker>().build()
val workRequest2 = OneTimeWorkRequestBuilder<CustomWorker>().build()
val workRequest3 = OneTimeWorkRequestBuilder<CustomWorker>().build()
// execute simultaneously.
WorkManager.getInstance().beginWith(workRequest1,workRequest2,workRequest3).enqueue()
Copy the code
3.8.2 Sequential Execution
We can use the beginwith-then method for sequential execution. If one of the sequential tasks fails, subsequent tasks will not be executed.
// Execute sequentially
WorkManager.getInstance().beginWith(workRequest1).then(workRequest2).then(workRequest3).enqueue()
Copy the code
3.8.3 Group Execution
We can perform combinatorial execution using the Combine operator.
// combine execution
val chain1 = WorkManager.getInstance()
.beginWith(workRequest1)
.then(workRequest2)
val chain2 = WorkManager.getInstance()
.beginWith(workRequest3)
.then(workRequest4)
WorkContinuation
.combine(chain1, chain2)
.then(workRequest5)
.enqueue()
Copy the code
The only chain 3.8.4
A unique chain, as its name implies, cannot have tasks with the same name in the execution queue at the same time.
val workRequest = OneTimeWorkRequestBuilder<CustomWorker>().build()
WorkManager.getInstance(this).beginUniqueWork("TAG", ExistingWorkPolicy.REPLACE, workRequest)
Copy the code
Note that the most variable length parameter is WorkRequest. The first parameter is the task name “TAG”. The second parameter is the execution policy for an existing task. Enumeration values are as follows.
public enum ExistingWorkPolicy {
REPLACE, / / replace
KEEP, // Hold, do nothing
APPEND // Add to an existing task
}
Copy the code
REPLACE
: If a task with the same name exists and is suspended, cancel and delete the existing task and replace the new task.KEEP
: If a task with the same name exists and is suspended, no operation is performed.APPEND
: If a task with the same name exists and is suspended, a new task is added to the cache. When all tasks in the queue are executed, the new task is set to the first task.
Making: github.com/HyejeanMOON…