介绍
WorkManager作为Jetpack的一个重要的组件,可以调度即使应用退出或者手机重启后仍然要运行的可延迟的任务。这么重要的组件,今天我们就分析一下源码的实现原理,关于基本使用方法,这里不在做介绍。
分析
WorkManager.getInstance
WorkManager.getInstance(this) 的源码
public static @NonNull WorkManager getInstance(@NonNull Context context) {
return WorkManagerImpl.getInstance(context);
}
WorkManagerImpl#getInstance
public static @NonNull WorkManagerImpl getInstance(@NonNull Context context) {
synchronized (sLock) {
WorkManagerImpl instance = getInstance();
if (instance == null) {
...
} else {
throw new IllegalStateException("WorkManager is not initialized properly. You "
+ "have explicitly disabled WorkManagerInitializer in your manifest, "
+ "have not manually called WorkManager#initialize at this point, and "
+ "your Application does not implement Configuration.Provider.");
}
}
return instance;
}
}
public static @Nullable WorkManagerImpl getInstance() {
synchronized (sLock) {
if (sDelegatedInstance != null) {
return sDelegatedInstance;
}
return sDefaultInstance;
}
}
WorkManagerImpl 的getInstance是一个单例获得一个代理对象sDelegatedInstance。sDelegatedInstance的初始化是在initialize方法内
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
synchronized (sLock) {
....
if (sDelegatedInstance == null) {
context = context.getApplicationContext();
if (sDefaultInstance == null) {
sDefaultInstance = new WorkManagerImpl(//1
context,
configuration,
new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
}
sDelegatedInstance = sDefaultInstance;
}
}
}
代理对象sDelegatedInstance的值是默认对象sDefaultInstance,那么这个静态的initialize是在WorkManager的initialize被调用的
/**
* Used to do a one-time initialization of the {@link WorkManager} singleton with a custom
* {@link Configuration}. By default, this method should not be called because WorkManager is
* automatically initialized. To initialize WorkManager yourself, please follow these steps:
*
* - Disable {@code androidx.work.impl.WorkManagerInitializer} in your manifest.
*
- Invoke this method in {@code Application#onCreate} or a {@code ContentProvider}. Note
* that this method must be invoked in one of these two places or you risk getting a
* {@code NullPointerException} in {@link #getInstance(Context)}.
*
*
* This method throws an exception if it is called multiple times.
*
* @param context A {@link Context} object for configuration purposes. Internally, this class
* will call {@link Context#getApplicationContext()}, so you may safely pass in
* any Context without risking a memory leak.
* @param configuration The {@link Configuration} for used to set up WorkManager.
* @see Configuration.Provider for on-demand initialization.
*/
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
WorkManagerImpl.initialize(context, configuration);
}
这个方法的注释很重要,大概意思是:
一般使用自定义的配置信息(Configuration)创建一个WorkManager单例。但是通常情况我们不需要调用这个方法因为系统会自动调用这个方法初始化 WorkManage。如果要自己初始化WorkManage,需要以下几个步骤:
1.manifest中不要注册WorkManagerInitializer
2.在Application#onCreate 或者 ContentProvider中调用这个方法,否则会发生空指针异常。
那么系统是如何调用这个方法的呢?通过以上的两个步骤,我们可以看看我们编译后apk中的manifest.xml文件。
看到了吧!系统自动给我们添加了WorkManagerInitializer的ContentProvider。一起看看WorkManagerInitializer吧
@Override
public boolean onCreate() {
// Initialize WorkManager with the default configuration.
WorkManager.initialize(getContext(), new Configuration.Builder().build());
return true;
}
WorkManager的初始化流程序列图如下
Configuration
我们回头看一下WorkManagerInitializer#onCreate()中传递的Configuration对象
public Builder() {
mLoggingLevel = Log.INFO;
mMinJobSchedulerId = IdGenerator.INITIAL_ID;
mMaxJobSchedulerId = Integer.MAX_VALUE;
mMaxSchedulerLimit = MIN_SCHEDULER_LIMIT;
}
Configuration(@NonNull Configuration.Builder builder) {
if (builder.mExecutor == null) {
mExecutor = createDefaultExecutor();
} else {
mExecutor = builder.mExecutor;
}
if (builder.mTaskExecutor == null) {
mIsUsingDefaultTaskExecutor = true;
// This executor is used for *both* WorkManager's tasks and Room's query executor.
// So this should not be a single threaded executor. Writes will still be serialized
// as this will be wrapped with an SerialExecutor.
mTaskExecutor = createDefaultExecutor();
} else {
mIsUsingDefaultTaskExecutor = false;
mTaskExecutor = builder.mTaskExecutor;
}
if (builder.mWorkerFactory == null) {
mWorkerFactory = WorkerFactory.getDefaultWorkerFactory();
} else {
mWorkerFactory = builder.mWorkerFactory;
}
if (builder.mInputMergerFactory == null) {
mInputMergerFactory = InputMergerFactory.getDefaultInputMergerFactory();
} else {
mInputMergerFactory = builder.mInputMergerFactory;
}
mLoggingLevel = builder.mLoggingLevel;
mMinJobSchedulerId = builder.mMinJobSchedulerId;
mMaxJobSchedulerId = builder.mMaxJobSchedulerId;
mMaxSchedulerLimit = builder.mMaxSchedulerLimit;
}
private @NonNull Executor createDefaultExecutor() {
return Executors.newFixedThreadPool(
// This value is the same as the core pool size for AsyncTask#THREAD_POOL_EXECUTOR.
Math.max(2, Math.min(Runtime.getRuntime().availableProcessors() - 1, 4)));
}
mExecutor 和mTaskExecutor 都是一个固定数量的线程池。
我们看一下WorkManagerImpl #initialize的注释1处的new WorkManagerTaskExecutor(configuration.getTaskExecutor())
public SerialExecutor(@NonNull Executor executor) {
mExecutor = executor;
mTasks = new ArrayDeque<>();
mLock = new Object();
}
将我们的线程池装饰成了线性的执行器,并且创建一个任务队列,后边我们添加的任务都会到这个队列中,然后根据这个队列按照先进先出的顺序取出任务并且执行任务。这里SerialExecutor和参数executor使用的是装饰模式。
WorkManagerImpl的构造方法
回到WorkManagerImpl的构造方法
public WorkManagerImpl(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
boolean useTestDatabase) {
this(context,
configuration,
workTaskExecutor,
WorkDatabase.create(
context.getApplicationContext(),
workTaskExecutor.getBackgroundExecutor(),
useTestDatabase)
);
}
public WorkManagerImpl(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
@NonNull WorkDatabase database) {
Context applicationContext = context.getApplicationContext();
Logger.setLogger(new Logger.LogcatLogger(configuration.getMinimumLoggingLevel()));
List schedulers = createSchedulers(applicationContext, workTaskExecutor);
Processor processor = new Processor(
context,
configuration,
workTaskExecutor,
database,
schedulers);
internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
}
public List createSchedulers(
@NonNull Context context,
@NonNull 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));
}
构造方法内做了以下几件事
1.根据后台线程池(SerialExecutor)创建数据库
2.创建schedulers
3.创建Processor 对象
我们看一下内部初始化(internalInit)做了什么工作
private void internalInit(@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
@NonNull WorkDatabase workDatabase,
@NonNull List schedulers,
@NonNull Processor processor) {
....
// Checks for app force stops.
mWorkTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));
}
ForceStopRunnable的run方法内会执行上次app退出后还没有执行的任务。
Schedulers.createBestAvailableBackgroundScheduler
会根据设备的sdk版本和设备当前运行状态选择最优的任务执行器SystemJobScheduler或者SystemAlarmScheduler。
任务加入到队列的enqueue方法
WorkManagerImpl#beginWith方法
@Override
public @NonNull WorkContinuation beginWith(@NonNull List work) {
if (work.isEmpty()) {
throw new IllegalArgumentException(
"beginWith needs at least one OneTimeWorkRequest.");
}
return new WorkContinuationImpl(this, work);
}
WorkContinuationImpl的构造
WorkContinuationImpl(
@NonNull WorkManagerImpl workManagerImpl,
@NonNull List extends WorkRequest> work) {
this(
workManagerImpl,
null,
ExistingWorkPolicy.KEEP,
work,
null);
}
默认使用的KEEP策列,即任务冲突的时候,舍弃新加的任务,保留当前任务。
当我们调用then在后边追加任务的时候。
WorkContinuationImpl#then方法
public @NonNull WorkContinuation then(@NonNull List work) {
if (work.isEmpty()) {
return this;
} else {
return new WorkContinuationImpl(mWorkManagerImpl,
mName,
ExistingWorkPolicy.KEEP,
work,
Collections.singletonList(this));
}
}
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);
}
}
mAllIds存储所有work的id。
WorkContinuationImpl#enqueue
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;
}
EnqueueRunnable#run
@Override
public void run() {
try {
if (mWorkContinuation.hasCycles()) {
throw new IllegalStateException(
String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
}
boolean needsScheduling = addToDatabase();//1
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);//2
scheduleWorkInBackground();//3
}
mOperation.setState(Operation.SUCCESS);
} catch (Throwable exception) {
mOperation.setState(new Operation.State.FAILURE(exception));
}
}
注释1根据策略不同将任务加入数据库或者删除掉。
注释2处,允许将RescheduleReceiver注册到manifest.xml内,我们 可以打开编译后apk的AndroidManifests.xml看到
注释3处在后台执行任务
public void scheduleWorkInBackground() {
WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
Schedulers.schedule(
workManager.getConfiguration(),
workManager.getWorkDatabase(),
workManager.getSchedulers());
}
Schedulers#schedule方法内部会从数据库查询未执行的任务然后根据执行时间排序的所有任务。然后执行任务
public static void schedule(
@NonNull Configuration configuration,
@NonNull WorkDatabase workDatabase,
List schedulers) {
...
WorkSpecDao workSpecDao = workDatabase.workSpecDao();
List eligibleWorkSpecs;
...
if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);
// Delegate to the underlying scheduler.
for (Scheduler scheduler : schedulers) {
scheduler.schedule(eligibleWorkSpecsArray);
}
}
}
我们选择一个GreedyScheduler看一下怎么执行的
@Override
public void schedule(@NonNull WorkSpec... workSpecs) {
...
mWorkManagerImpl.startWork(workSpec.id);
....
}
这里之分析我们没有约束条件的时候
public void startWork(@NonNull String workSpecId) {
startWork(workSpecId, null);
}
public void startWork(
@NonNull String workSpecId,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
mWorkTaskExecutor
.executeOnBackgroundThread(
new StartWorkRunnable(this, workSpecId, runtimeExtras));
}
StartWorkRunnable会对我们的work包装成WorkerWrapper,WorkerWrapper是Runnable的子类run方法内执行runWorker方法,runWorker方法会从从数据库中查询任务,根据输入的data等生成WorkerParameters实例,然后经过反射创建我们自己创建的Worker实例mWorker ,然后调用mWorker的startWork方法
private void runWorker() {
....
try {
mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId);
....
Data input;
...
List inputs = new ArrayList<>();
inputs.add(mWorkSpec.input);
inputs.addAll(mWorkSpecDao.getInputsFromPrerequisites(mWorkSpecId));
input = inputMerger.merge(inputs);
WorkerParameters params = new WorkerParameters(
UUID.fromString(mWorkSpecId),
input,
mTags,
mRuntimeExtras,
mWorkSpec.runAttemptCount,
mConfiguration.getExecutor(),
mWorkTaskExecutor,
mConfiguration.getWorkerFactory(),
new WorkProgressUpdater(mWorkDatabase, mWorkTaskExecutor),
new WorkForegroundUpdater(mForegroundProcessor, mWorkTaskExecutor));
if (mWorker == null) {//反射
mWorker = mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback(
mAppContext,
mWorkSpec.workerClassName,
params);
}
...
mWorkTaskExecutor.getMainThreadExecutor()
.execute(new Runnable() {
@Override
public void run() {
try {
Logger.get().debug(TAG, String.format("Starting work for %s",
mWorkSpec.workerClassName));
mInnerFuture = mWorker.startWork();
future.setFuture(mInnerFuture);
} catch (Throwable e) {
future.setException(e);
}
}
});
....
}
因为回调的结果是要在主线程中返回的,这里使用的主线程Handle.post处理的
Worker#startWork
public final @NonNull ListenableFuture startWork() {
mFuture = SettableFuture.create();
getBackgroundExecutor().execute(new Runnable() {
@Override
public void run() {
try {
Result result = doWork();
mFuture.set(result);
} catch (Throwable throwable) {
mFuture.setException(throwable);
}
}
});
return mFuture;
}
这里又切换后台线程执行doWork,即我们自己实现的doWork方法。
OneTimeWorkRequest
OneTimeWorkRequest的build的方法会生成Work的id和类名,Tag等数据库的字段
public Builder(@NonNull Class extends ListenableWorker> workerClass) {
super(workerClass);
mWorkSpec.inputMergerClassName = OverwritingInputMerger.class.getName();
}
Builder(@NonNull Class extends ListenableWorker> workerClass) {
mId = UUID.randomUUID();
mWorkerClass = workerClass;
mWorkSpec = new WorkSpec(mId.toString(), workerClass.getName());
addTag(workerClass.getName());
}
至此整个WorkManager的执行流程就结束了。