其基础是:
JobScheduler
3AlarmManager
4 + BroadcastReceiver
Android Jetpack
组件其中之一,WorkManager
、Paging
、Navigation
、Slices
,兼容 kotlin 语言。其中 WorkManager 一句话概括是:在条件满足(网络状态、电池条件)满足时,管理运行后台 work(即使你的应用没启动也能保证任务能被执行)。WorkManager 尽可能使用框架 JobScheduler,来帮助优化电池寿命和批处理作业; api 23 以下设备,如果应用使用了 Firebase JobDispatcher 和其它的 Firebase 依赖时,尝试使用 Firebase JobDispatcher
,否则,就使用自定义的AlarmManager + BroadcastReceiver 实现。
doWork()
是运行于后台线程的同步方法
Result
Result.success()
Result.failure()
Result.retry()
doWork()
需要我们实现我们要做的 workdoWork()
中不能更新 UI(需要在主线程中做的事情,Toast 也不行)
Can't toast on a thread that has not called Looper.prepare()
Worker
,也是官方推荐的 ListenableWorker
|
+--------------------------+-------------------------+--------------------+
| | | |
CombineContinuationsWorker ConstraintTrackingWorker CoroutineWorker Worker
所有的 Work 在
ListenableWorker
中完成,ListenableWorker
在WorkManager
中是异步执行的
ListenableWorker
在运行时由WorkerFactory
实例化,配置是Configuration
,在主线程中调用其方法startWork()
如果一个 work 被制止后,后来又因为某种原因重新启动后,会重新实例化一个
ListenableWorker
,startWork()
在每个ListenableWorker
中只会被调用一次
一个
ListenableWorker
最大执行时长为 10 min,并会返回ListenableWorker.Result
,如果时间到了, work 将会被停止,ListenableFuture
也会被取消
ListenableWorker.Result
有三个子类:
Result.Success
表示任务执行成功Result.FAILURE
表示任务执行失败Result.Retry
之后再尝试执行该任务enqueue()
)等操作、beginWith()
等、查询 WorkRequest 信息[getWorkInfoByIdLiveData``getWorkInfoById
] 等WorkManager
支持两种 work:OneTimeWorkRequest
和 PeriodicWorkRequest
WorkManagerImpl
实例化 WorkManager
,为单例模式,初始化在 initialize()
方法里,调用在 WorkManagerInitializer
的 onCreate 中,WorkManagerInitializer
继承于 ContentProvider
,所以在系统启动的时候就会实例化 WorkManager
//sDelegatedInstance 和 sDefaultInstance 都是 WorkManager 的实例,所以不能共存
synchronized (sLock) {
if (sDelegatedInstance != null && sDefaultInstance != null) {
throw new IllegalStateException("WorkManager is already initialized. Did you "
+ "try to initialize it manually without disabling "
+ "WorkManagerInitializer? See "
+ "WorkManager#initialize(Context, Configuration) or the class level"
+ "Javadoc for more information.");
}
if (sDelegatedInstance == null) {
context = context.getApplicationContext();
if (sDefaultInstance == null) {
sDefaultInstance = new WorkManagerImpl(
context,
configuration,
new WorkManagerTaskExecutor());
}
sDelegatedInstance = sDefaultInstance;
}
}
getWorkInfoByIdLiveData
通过 Room 数据库操作拿到 LiveData 数据,最后包裹为 LiveData
类型startWork()
调用 TaskExecutor
的 executeOnBackgroundThread()
,内部是调用使用 newSingleThreadExecutor
生成的线程的 excute()
WorkRequest
|
+--------------------------+
| |
OneTimeWorkRequest PeriodicWorkRequest
PeriodicWorkRequest
的时间不是非常精确,其根据电池优化策略或网络情况,可能会有延迟,如果需要准确的间隔时间来执行任务的话不能使用 PeriodicWorkRequest
WorkRequest
绑定一个 id(UUID 类型),可用于查询getWorkInfoByIdLiveData()
、观察observe()
和取消 cancelWorkById()
LiveData
WorkRequest request = new OneTimeWorkRequest.Builder(FooWorker.class).build();
workManager.enqueue(request);
//查询
LiveData<WorkInfo> status = workManager.getWorkInfoByIdLiveData(request.getId());
//观察
status.observe(...);
//取消
workManager.cancelWorkById(request.getId());
setConstraints
,Constraints
为 work 的条件,指定任务在何时运行(例如,“仅在连接到网络时”)@ColumnInfo(name = "required_network_type")
private NetworkType mRequiredNetworkType = NOT_REQUIRED;
@ColumnInfo(name = "requires_charging")
private boolean mRequiresCharging;
@ColumnInfo(name = "requires_device_idle")
private boolean mRequiresDeviceIdle;
@ColumnInfo(name = "requires_battery_not_low")
private boolean mRequiresBatteryNotLow;
@ColumnInfo(name = "requires_storage_not_low")
private boolean mRequiresStorageNotLow;
@ColumnInfo(name = "trigger_content_update_delay")
private long mTriggerContentUpdateDelay = -1;
@ColumnInfo(name = "trigger_max_content_delay")
private long mTriggerMaxContentDelay = -1;
//如当设备空闲时或正在充电等执行 work
Constraints myConstraints = new Constraints.Builder()
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
// 其他约束参见 Constraints.Builder
.build();
OneTimeWorkRequest myWork =
new OneTimeWorkRequest.Builder(CompressWorker.class)
.setConstraints(myConstraints)
.build();
beginWith()
方法以及 WorkContinuation
的 then()
方法,beginWith
返回 WorkContinuation
,其方法then()
可以执行之后执行的 work,如要执行以下的顺序,可使用以下方法,A-E 都是 Request/**
A
|
+----------+
| |
B C
|
+----+
| |
D E
**/
WorkContinuation continuation = workManager.beginWith(A);
continuation.then(B).then(D, E).enqueue(); // A is implicitly enqueued here
continuation.then(C).enqueue();
同一时间内队列里不能存在相同名称的任务,使用 WorkerManager 的 beginUniqueWork()
,有 2 个重载方法
public abstract @NonNull WorkContinuation beginUniqueWork(
@NonNull String uniqueWorkName,
@NonNull ExistingWorkPolicy existingWorkPolicy,
@NonNull OneTimeWorkRequest work);
public abstract @NonNull WorkContinuation beginUniqueWork(
@NonNull String uniqueWorkName,
@NonNull ExistingWorkPolicy existingWorkPolicy,
@NonNull List<OneTimeWorkRequest> work);
其中第三个参数指定入队策略,ExistingWorkPolicy 是枚举类
public enum ExistingWorkPolicy {
/**
* If there is existing pending (uncompleted) work with the same unique name, cancel and delete
* it. Then, insert the newly-specified work.
*/
REPLACE,
/**
* If there is existing pending (uncompleted) work with the same unique name, do nothing.
* Otherwise, insert the newly-specified work.
*/
KEEP,
/**
* If there is existing pending (uncompleted) work with the same unique name, append the
* newly-specified work as a child of all the leaves of that work sequence. Otherwise, insert
* the newly-specified work as the start of a new sequence.
*/
APPEND
}
public enum State {
ENQUEUED,
RUNNING,
SUCCEEDED,
FAILED,
BLOCKED, //如果 WorkRequest 的约束没有通过,那么这个任务就会处于挂起状态
CANCELLED;
public boolean isFinished() {
return (this == SUCCEEDED || this == FAILED || this == CANCELLED);
}
}
Data
:类似于我们平常使用的 bundle,用于给 Worker 传递参数大致我们知道了有关 WorkManager 的几个关键的类
dependencies {
// WorkManager
def work_version = "1.0.1"
implementation "android.arch.work:work-runtime:$work_version"
//WorkManager - kotlin
implementation "android.arch.work:work-runtime-ktx:$work_version"
// optional - RxJava2 support
implementation "androidx.work:work-rxjava2:$work_version"
// optional - Test helpers
androidTestImplementation "android.arch.work:work-testing:$work_version"
}
继承于 Worker,实现 doWork()
方法,需要注意的是版本 1.0.0
和 1.0.1
很不一样,需要注意
//1.0.0 写法
class MyWorker : Worker() {
override fun doWork(): WorkerResult {
val str = inputData.getString("demo")
if (null != str) {
Log.e("whh", "MyWorker 执行了$str")
return WorkerResult.SUCCESS
} else {
return WorkerResult.FAILURE
}
}
}
//1.0.1 写法,数据结构变化了
class UploadWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
override fun doWork(): Result {
Log.e("whh", "我上传了图片")
return Result.success()
}
}
PeriodicWorkRequestBuilder
或 OneTimeWorkRequest
可以定义 constraints、input to the work、延迟、重试策略等,可参考 Defining your Work Requests
val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.build()
The exact time that the worker is going to be executed depends on the constraints that are used in your WorkRequest and on system optimizations. WorkManager is designed to give the best possible behavior under these restrictions.
约束条件和系统优化会影响 worker 执行的时间,WorkManager 是为了在这些限制下实现最优表现而设计出来的
WorkManager.getInstance().enqueue(uploadWorkRequest)
Really Easy 吧!
使用起来很简单,剩下的都交给 WorkManager 来进行工作
Android 开发者官网 ↩︎
api 等级 ↩︎
Android Jobscheduler 使用 理解JobScheduler机制
对于满足网络、电量、时间等一定预定条件而触发的任务非常适合 ↩︎
关于使用 AlarmManager 的注意事项 ↩︎
[译] 从Service到WorkManager
Service(耗电量大) - JobScheduler(api 21-22 有bug)- JobDispatcher - 需要使用 Google Play Services,可以兼容 Android 5.0 -alamManager (在不同 api 上的时间不准问题,运行任务并没有考虑Doze or App Standby模式;无法指定一些约束 )
↩︎
Schedule tasks with WorkManager ↩︎