题外话
首先说明下,work的request都是存储在数据库里的,用的是room库
WorkManager 的初始化,是系统自动实现的,如下是通过ContentProvider实现的
public class WorkManagerInitializer extends ContentProvider {
@Override
public boolean onCreate() {
// Initialize WorkManager with the default configuration.
WorkManager.initialize(getContext(), new Configuration.Builder().build());
return true;
}
1.简单理解
1.1 过程
WorkManager 管理一堆WorkRequest【系统实现的有两种,一种一次性的OneTimeWorkRequest,另外一种可以重复的PeriodicWorkRequest】
如下,通过begin方法返回一个WorkContinuation对象
var continuation = WorkManager.getInstance(mContext)
.beginUniqueWork(Constants.IMAGE_MANIPULATION_WORK_NAME,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequest.from(CleanupWorker::class.java))
或者,这种unique name是空
var continuation = WorkManager.getInstance(mContext).beginWith(OneTimeWorkRequest.from(CleanupWorker::class.java))
看下第二个参数的意义,
分三种,有同名的任务的情况下执行 替换,不替换,添加操作
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
}
如下是beginWith的实现,返回一个WorkContinuationImpl对象,和beginUniqueWork的区别,名字是null,policy是KEEP类型
WorkContinuationImpl(
@NonNull WorkManagerImpl workManagerImpl,
@NonNull List extends WorkRequest> work) {
this(
workManagerImpl,
null,
ExistingWorkPolicy.KEEP,
work,
null);
}
完整的构造方法如下
mIds是新加的work request的id集合,mAllIds是包括parent【也就是之前添加的request】的id集合
WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl,
String name,
ExistingWorkPolicy existingWorkPolicy,
@NonNull List extends WorkRequest> work,
@Nullable List parents) {
mWorkManagerImpl = workManagerImpl;
mName = name;
mExistingWorkPolicy = existingWorkPolicy;
mWork = work;
mParents = parents;
mIds = new ArrayList<>(mWork.size());
mAllIds = new ArrayList<>();
if (parents != null) {
for (WorkContinuationImpl parent : parents) {
mAllIds.addAll(parent.mAllIds);
}
}
for (int i = 0; i < work.size(); i++) {
String id = work.get(i).getStringId();
mIds.add(id);
mAllIds.add(id);
}
}
上边的 parent是啥,往下看,这个类还有个then方法,可以继续添加request
public @NonNull WorkContinuation then(@NonNull List work) {
return new WorkContinuationImpl(mWorkManagerImpl,
mName,
ExistingWorkPolicy.KEEP,
work,
Collections.singletonList(this));
}
效果类似这种
等到request都添加完了,执行如下的方法
public @NonNull Operation enqueue() {
// Only enqueue if not already enqueued.
if (!mEnqueued) {
// The runnable walks the hierarchy of the continuations
// and marks them enqueued using the markEnqueued() method, parent first.
EnqueueRunnable runnable = new EnqueueRunnable(this);
mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);
mOperation = runnable.getOperation();
} else {
Logger.get().warning(TAG,
String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
}
return mOperation;
}
可以看到,主要就是里边那个Runnable,我们去看下,Runnable都干啥了
public void run() {
try {
//忽略
boolean needsScheduling = addToDatabase();//添加到数据库
if (needsScheduling) {
// Enable RescheduleReceiver, only when there are Worker's that need scheduling.
final Context context =
mWorkContinuation.getWorkManagerImpl().getApplicationContext();
//这里是个广播,手机重启以后恢复任务用的
PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true);
scheduleWorkInBackground();
}
mOperation.setState(Operation.SUCCESS);
} catch (Throwable exception) {
mOperation.setState(new Operation.State.FAILURE(exception));
}
}
addToDatabase()一路往下点,最后可以看到就是把request里的数据存到数据库里
workDatabase.workSpecDao().insertWorkSpec(workSpec);
if (hasPrerequisite) {
for (String prerequisiteId : prerequisiteIds) {
Dependency dep = new Dependency(work.getStringId(), prerequisiteId);
workDatabase.dependencyDao().insertDependency(dep);
}
}
for (String tag : work.getTags()) {
workDatabase.workTagDao().insert(new WorkTag(tag, work.getStringId()));
}
if (isNamed) {
workDatabase.workNameDao().insert(new WorkName(name, work.getStringId()));
}
后台任务执行
public void scheduleWorkInBackground() {
WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
Schedulers.schedule(
workManager.getConfiguration(),
workManager.getWorkDatabase(),
workManager.getSchedulers());
}
然后里边就是查找出workSpec数据,
public static void schedule(
@NonNull Configuration configuration,
@NonNull WorkDatabase workDatabase,
List schedulers) {
//省略查找..
if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);
// Delegate to the underlying scheduler.
for (Scheduler scheduler : schedulers) {
//看来还得找下Scheduler的实现,
scheduler.schedule(eligibleWorkSpecsArray);
}
}
根据Scheduler的来源workManager.getSchedulers())往上层查找
在WorkManagerImpl构造方法里
List schedulers = createSchedulers(applicationContext, workTaskExecutor);
public @NonNull List createSchedulers(Context context, TaskExecutor taskExecutor) {
return Arrays.asList(
Schedulers.createBestAvailableBackgroundScheduler(context, this),
// Specify the task executor directly here as this happens before internalInit.
// GreedyScheduler creates ConstraintTrackers and controllers eagerly.
new GreedyScheduler(context, taskExecutor, this));
}
先看第一个,这里就是23以上的用的JobScheduler,23以下的用的alarm了
static Scheduler createBestAvailableBackgroundScheduler(
@NonNull Context context,
@NonNull WorkManagerImpl workManager) {
Scheduler scheduler;
if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
scheduler = new SystemJobScheduler(context, workManager);
setComponentEnabled(context, SystemJobService.class, true);
Logger.get().debug(TAG, "Created SystemJobScheduler and enabled SystemJobService");
} else {
scheduler = tryCreateGcmBasedScheduler(context);
if (scheduler == null) {
scheduler = new SystemAlarmScheduler(context);
setComponentEnabled(context, SystemAlarmService.class, true);
Logger.get().debug(TAG, "Created SystemAlarmScheduler");
}
}
return scheduler;
}
继续一路往下看23以上的实现Processor类
public boolean startWork(String id, WorkerParameters.RuntimeExtras runtimeExtras) {
WorkerWrapper workWrapper;
//...........最后其实是执行了WorkerWrapper里的runWrok方法
workWrapper =
new WorkerWrapper.Builder(
mAppContext,
mConfiguration,
mWorkTaskExecutor,
mWorkDatabase,
id)
.withSchedulers(mSchedulers)
.withRuntimeExtras(runtimeExtras)
.build();
ListenableFuture future = workWrapper.getFuture();
future.addListener(
new FutureListener(this, id, future),
mWorkTaskExecutor.getMainThreadExecutor());
mEnqueuedWorkMap.put(id, workWrapper);
}
mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
return true;
}
doWork代码太多就不详细研究了
大致过程就是把worker实例化出来,然后执行里边的
mInnerFuture = mWorker.startWork();
future.setFuture(mInnerFuture);
简单的执行过程看完了
看下request
- OneTimeWorkRequest
实例化有3个方法
OneTimeWorkRequest.from(CleanupWorker::class.java)
OneTimeWorkRequest.Builder(WaterColorFilterWorker::class.java)
OneTimeWorkRequestBuilder()
第三种是worker-runtime-ktx包下的,正常运行需要在gradle的android属性里添加
kotlinOptions {
jvmTarget = "1.8"
}
PeriodicWorkRequest.Builder(WaterColorFilterWorker::class.java,15,TimeUnit.MINUTES)
workerManager
1.beginWith或者beginUniqueWork
如果同时进行的request比较多,可以使用beginWith或者beginUniqueWork,如下,返回的
public @NonNull WorkContinuation beginWith(@NonNull List work) {
//..
return new WorkContinuationImpl(this, work);
}
而WorkContinuationImpl,可以通过then方法添加新的request
public @NonNull WorkContinuation then(@NonNull List work) {
return new WorkContinuationImpl(mWorkManagerImpl,
mName,
ExistingWorkPolicy.KEEP,
work,
Collections.singletonList(this));
}
- enqueue或者enqueueUniquePeriodicWork
可以看到最终和上边的一样,执行的是WorkContinuationImpl的enqueue方法
public Operation enqueue(
@NonNull List extends WorkRequest> workRequests) {
return new WorkContinuationImpl(this, workRequests).enqueue();
}
- 取消任务
取消未完成的任务,任务还在数据库里,只是状态变成了success
cancelAllWork()
cancelAllWorkByTag(@NonNull final String tag)//tag就是class的name 》workerClass.getName()
cancelUniqueWork(@NonNull String uniqueWorkName) //uniqueWork那个name
cancelWorkById(@NonNull UUID id)//uuid自动生成的,可以get拿到
- 删除任务
删除已经完成的work,包括success,failed,cancelled
* Prunes all eligible finished work from the internal database. Eligible work must be finished
* ({@link WorkInfo.State#SUCCEEDED}, {@link WorkInfo.State#FAILED}, or
* {@link WorkInfo.State#CANCELLED}), with zero unfinished dependents.
pruneWork()
- 获取worker
分3种,和cancel差不多,根据标示来查找workInfo
public @NonNull ListenableFuture getWorkInfoById(@NonNull UUID id)
public @NonNull LiveData getWorkInfoByIdLiveData(@NonNull UUID id)
public @NonNull ListenableFuture> getWorkInfosByTag(@NonNull String tag)
public @NonNull LiveData> getWorkInfosByTagLiveData(@NonNull String tag)
public ListenableFuture> getWorkInfosForUniqueWork(@NonNull String name)
public LiveData> getWorkInfosForUniqueWorkLiveData(@NonNull String name)
- WorkRequest.Build的方法
create workRequest的时候可以传一些参数的,key value形式,也就是个Data,就是个map
Map mValues;
setInputData(@NonNull Data inputData)
kotin下生成data比较简单,如下
private fun createInputData(): Data {
return workDataOf(Constants.KEY_IMAGE_URI to mImageUri.toString())
}
实现如下
inline fun workDataOf(vararg pairs: Pair): Data {
val dataBuilder = Data.Builder()
for (pair in pairs) {
dataBuilder.put(pair.first, pair.second)
}
return dataBuilder.build()
}
如何获取这个数据?
在worker里直接get即可
public final @NonNull Data getInputData() {
return mWorkerParams.getInputData();
}
- Worker
实现doWork方法,返回Result,
class MyWoker(context: Context ,workerParameters: WorkerParameters) :Worker(context,workerParameters){
override fun doWork(): Result {
inputData
return Result.success()
}
}
实例
写个简单的实例来说明下数据的传递
class MyWoker(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
override fun doWork(): Result {
val value = inputData.getString("key")
println("doWork==========${javaClass.simpleName}===$value")
val data = Data.Builder().apply {
putString("key", "from My Worker");
}.build()
return Result.success(data)
}
}
class MyWoker2(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
override fun doWork(): Result {
val value = inputData.getString("key")
println("doWork==========${javaClass.simpleName}===$value")
val data = Data.Builder().apply {
putString("key", "from My Worker2");
}.build()
return Result.success(data)
}
}
private fun startWork() {
WorkManager.getInstance(this).beginWith(OneTimeWorkRequest.Builder(MyWoker::class.java).setInputData(createData()).build())
.then(OneTimeWorkRequestBuilder().setInputData(createData()).build())
.enqueue()
println("name===============${MyWoker::class.java.name}")
WorkManager.getInstance(this).getWorkInfosByTagLiveData(MyWoker::class.java.name).observe(this, Observer {
println("observer===========${it.size}")
it.iterator().forEach {
println("work info=======${it.state}=======${it.outputData.getString("key")}")
}
})
WorkManager.getInstance(this).getWorkInfosByTagLiveData(MyWoker2::class.java.name).observe(this, Observer {
println("observer2===========${it.size}")
it.iterator().forEach {
println("work info2=======${it.state}=======${it.outputData.getString("key")}")
}
})
}
private fun createData():Data{
return workDataOf("key" to "default value")
}
日志如下
分析下,第一个任务state是euqueued,其他的默认是blocked,然后第一个任务在success之前还有个running的state,等到success之后,第二个任务状态改为enqueued,
Result.success(data)里边的outputData,下一个任务会通过inputData接收到,默认的inputData覆盖了
11-12 17:21:02.821 I/System.out: anme===============com.charliesong.demo0327.work.MyWoker
11-12 17:21:02.891 I/System.out: observer===========1
11-12 17:21:02.891 I/System.out: work info=======ENQUEUED=======null
11-12 17:21:02.901 I/System.out: observer2===========1
11-12 17:21:02.901 I/System.out: work info2=======BLOCKED=======null
11-12 17:21:02.911 I/System.out: doWork==========MyWoker===default value
11-12 17:21:02.921 I/System.out: observer===========1
11-12 17:21:02.921 I/System.out: work info=======RUNNING=======null
11-12 17:21:02.981 I/System.out: observer===========1
11-12 17:21:02.981 I/System.out: work info=======SUCCEEDED=======from My Worker
11-12 17:21:02.991 I/System.out: observer2===========1
11-12 17:21:02.991 I/System.out: work info2=======ENQUEUED=======null
11-12 17:21:03.021 I/System.out: doWork==========MyWoker2===from My Worker
11-12 17:21:03.061 I/System.out: observer2===========1
11-12 17:21:03.061 I/System.out: work info2=======SUCCEEDED=======from My Worker2
Constraints
WorkRequest的约束条件添加
WorkRequest.Build
setConstraints(@NonNull Constraints constraints)
public static final class Builder {
boolean mRequiresCharging = false;//是否充电的时候执行
boolean mRequiresDeviceIdle = false;//是否设备空闲的时候才执行
NetworkType mRequiredNetworkType = NOT_REQUIRED;//网络条件
boolean mRequiresBatteryNotLow = false;//是否低电量不触发
boolean mRequiresStorageNotLow = false;//是否存储空间低的时候不触发
// Same defaults as JobInfo
long mTriggerContentUpdateDelay = -1;//监听到contentUrl改变以后多久执行work
long mTriggerContentMaxDelay = -1;
ContentUriTriggers mContentUriTriggers = new ContentUriTriggers();//监听的contentUri @RequiresApi(24)
举例 content Uri
还有自定义的contentProvider之类等
所有联系人的Uri: content://contacts/people
某个联系人的Uri: content://contacts/people/5
所有图片Uri: content://media/external
某个图片的Uri:content://media/external/images/media/4
举个例子
如下,把网络断了,第一个work执行完以后,第二个work的state一直是ENQUEUED,知道把网连上,才开始执行里边的doWork方法
我们这里的beginWith,然后then是有顺序的,依次执行的
如果要几个request一起执行,那么放到集合里一起beginWith或者then即可
WorkManager.getInstance(this).beginWith(OneTimeWorkRequest.Builder(MyWoker::class.java)
.setInputData(createData())
.build())
.then(OneTimeWorkRequestBuilder()
.setInputData(createData())
.setConstraints(Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build())
.build())
.enqueue()