WorkManager 流程分析和源码解析 | 开发者说·DTalk

2020 年 11 月 7 日 谷歌开发者
本文原作者: 狐友技术团队,原文发布于: 搜狐技术产品
https://mp.weixin.qq.com/s/KlzYHqd3RhF1D8dDAYiSjA



前言


WorkManager 统一了对于 Android 后台任务的管理。在此之前,从 6.0 开始 Google 引入了 Doze 机制,并且在之后的几个版本对 Android 的后台行为及广播的限制越来越严格。在 Android 8.0 时 Google 官方推荐开发者使用 JobScheduler 来代替 Service+Broadcast 的后台任务管理方式。为了兼容老版本,android-job, Firebase JobDispatcher 和 GCMNetworkManager 都曾是开发者的选择,而 Firebase JobDispatcher 和 GCMNetworkManager 需要支持 google play service,并不适用于国内 app 场景。

2018 年,Google 推出的 jetpack 中包含了 WorkManager,之后 android-job 停止维护,Google 官方为 Firebase JobDispatcher 和 GCMNetworkManager 提出了 WorkManager 的迁移方案。今年,在 Android 11 的行为变更中提到,如果应用以 API 级别 "R" 或更高级别为目标平台,则在搭载 Android 6.0 (API 级别 23) 或更高版本的设备上会停用 Firebase JobDispatcher 和 GcmNetworkManager API 调用。所以在一些场景中,使用 WorkManager 来维护我们的后台任务可以说是官方推荐的唯一方式。

本文将介绍 WorkManager 的使用方式,并通过剖析 WorkManager 的内部实现原理,来帮助大家更好的理解 WorkManager 的实现。


WorkManager 的特点与适用场景


特点

  1. 保证任务一定会被执行
    WorkManager 有自己的数据库,每一个任务的信息与任务状态,都会保存在本地数据库中。所以即使程序没有在运行,或者在设备重启等情况下,WorkManager 依然可以保证任务的执行,只是不保证任务立即被执行。


  2. 合理使用设备资源
    在执行很多周期性或非立即执行的任务时,WorkManager 提供我们 API,帮助我们合理利用设备资源,避免不必要的内存,流量,电量等消耗。

适用场景 
  1. 可延迟进行的任务
    a.满足某些条件才执行的任务,如需要在充电时才执行的任务。
    b.用户无感知或可延迟感知的任务,如同步配置信息,同步资源,同步通讯录等。
  2. 定期重复性任务,但时效性要求不高的,如定期 log 上传,数据备份等。
  3. 退出应用后还应继续执行的未完成任务。



WorkManager 的使用


WorkManager 的使用非常简单,分为如下几个步骤:
  1. 创建一个后台任务 Worker。
  2. 定义 WorkRequest,配置运行任务的方式和时间。
  3. 将任务提交给系统处理。
  4. 观察 Worker 的进度或状态。

3.1 创建后台任务 Worker
WorkManager 提供了四种 Worker 的创建模式:
  1. Worker: 最简单的实现,WorkManager 会在后台线程上自动运行它。
  2. CoroutineWorkerCoroutineWorker 针对后台工作公开挂起函数。默认情况下,它们运行默认的 Dispatcher。
  3. RxWorker如果有很多现有异步代码是用 RxJava 建模的建议使用。与所有 RxJava2 概念一样,可以自由选择所需的线程处理策略。
  4. ListenableWorker是 Worker,CoroutineWorker,RxWorker 的基类,为需要与基于回调的异步 API 进行交互并且不使用 RxJava2 的 Java 开发者而设计。Worker 的创建示例
class UploadWorker(appContext: Context, workerParams: WorkerParameters)        : Worker(appContext, workerParams) {        override fun doWork(): Result {            // Do the work here--in this case, upload the images.            uploadImages()            // Indicate whether the task finished successfully with the Result            return Result.success()        }    }


3.2 配置运行任务方式和时间

3.2.1 一次性任务
val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>().build()


3.2.2 周期性任务
//执行多次任务,每隔12个小时执行一次val uploadWorkRequest = PeriodicWorkRequestBuilder<UploadWorker>(12, TimeUnit.HOURS)        .build()


3.2.3 带约束条件的任务
// Create a Constraints object that defines when the task should runval constraints = Constraints.Builder()            .setRequiresDeviceIdle(true)            .setRequiresCharging(true)            .build()
// ...then create a OneTimeWorkRequest that uses those constraintsval compressionWork = OneTimeWorkRequestBuilder<CompressWorker>() .setConstraints(constraints) .build()


3.2.4 延迟任务
val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()        .setInitialDelay(10, TimeUnit.SECONDS)//符合触发条件后,延迟10秒执行        .build()


3.3 将任务提交给系统处理

WorkManager.getInstance(myContext).enqueue(uploadWorkRequest)

3.3.1 多任务调度
WorkManager.getInstance()    // First, run all the A tasks (in parallel):    .beginWith(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(workC1, workC2)    .enqueue()


3.4 观察 Worker 的进度或状态

WorkManager.getInstance(myContext).getWorkInfoByIdLiveData(uploadWorkRequest.id)            .observe(lifecycleOwner, Observer { workInfo ->            })


WorkManager 流程分析与源码解析


这个章节将会从以下几个方面梳理 WorkManager 的流程与源码:
  1. 创建
    a. WorkManager的初始化
    b. WorkRequest的创建
  2. 非约束条件任务的执行
  3. 带约束条件任务的执行

从最基础的流程开始分析: 创建一个不带任何约束条件的一次性任务。在 doWork() 中让线程休息 5s。
val work1Request = OneTimeWorkRequestBuilder<Worker1>().build()WorkManager.getInstance(this).enqueue(work1Request)
class Worker1(appContext: Context, workerParams: WorkerParameters) :    Worker(appContext, workerParams) {
override fun doWork(): Result { Thread.sleep(5000) return Result.success() }}


4.1 创建
首先梳理一下 WorkManager 的初始化过程。

4.1.1. WorkManager 的初始化
在默认的情况下,WorkManager 并不是在我们调用 WorkManager.getInstance() 时创建的。通过反编译一下 apk,会发现在 AndroidManifest 文件中注册了名为 WorkManagerInitializer 的 ContentProvider。因此 WorkManager 在 app 冷启动的时候已经被创建。
//AndroidManifest.xml  <provider            android:name="androidx.work.impl.WorkManagerInitializer"            android:exported="false"            android:multiprocess="true"            android:authorities="com.jandroid.multivideo.workmanager-init"            android:directBootAware="false" />


WorkManagerInitializer 的 onCreate() 方法:

//WorkManagerInitializer public boolean onCreate() {        // Initialize WorkManager with the default configuration.        WorkManager.initialize(getContext(), new Configuration.Builder().build());        return true;    }


由于 WorkManager 是个单例,在此时 WorkManager 就已经被初始化了。在 initialize() 之前,会创建一个默认的 Configuration。Configuration 设置了许多属性,用来管理和调度工作的方式。通常我们使用 WorkManager 默认创建的 Configuration 即可。如需使用自己的 Configuration,可参考官方文档,有明确的使用说明。我们继续看 initialize() 的实现,由于 WorkManager 是个抽象类,真正的构造方法是在他的子类 WorkManagerImpl 实现的:

//WorkManagerImpl @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)    public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {        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(configuration.getTaskExecutor())); } sDelegatedInstance = sDefaultInstance; } } }


此时 sDelegatedInstance 为 null,WorkManager 会先创建一个默认的 WorkManagerTaskExecutor 对象,用来执行 WorkManager 的任务。之后创建一个 WorkManagerImpl 对象:

//WorkManagerImpl    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)    public WorkManagerImpl(            @NonNull Context context,            @NonNull Configuration configuration,            @NonNull TaskExecutor workTaskExecutor) {        this(context,                configuration,                workTaskExecutor,                context.getResources().getBoolean(R.bool.workmanager_test_configuration));    }
//WorkManagerImpl    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)    public WorkManagerImpl(            @NonNull Context context,            @NonNull Configuration configuration,            @NonNull TaskExecutor workTaskExecutor,            boolean useTestDatabase) {        this(context,                configuration,                workTaskExecutor,                WorkDatabase.create(                        context.getApplicationContext(),                        workTaskExecutor.getBackgroundExecutor(),                        useTestDatabase)        );    }


WorkManager 在此时创建了数据库。WorkDatabase.create() 将任务列表序列化到本地,记录每一个任务的属性,执行条件,执行顺序及执行状态等。从而保证任务在冷启动或硬件重启后,可以根据条件继续执行。接着看 this() 的实现:

//WorkManagerImpl    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)    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<Scheduler> schedulers = createSchedulers(applicationContext, workTaskExecutor);        Processor processor = new Processor(                context,                configuration,                workTaskExecutor,                database,                schedulers);        internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);    }


到这里有三个重要的初始化步骤。分别是 createSchedulers() 来根据 Build Version 创建不同的 Schedulers 进行任务调度,Processor() 用来管理 Schedulers 的执行,和 internalInit() 真正的初始化。先看 createSchedulers() 的实现:

//WorkManagerImpl    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)    @NonNull    public List<Scheduler> 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)); }


return 一个 Scheduler 数组。其中 GreedyScheduler() 是常驻的,用来执行没有任何约束的非周期性的任务。接下来看 createBestAvailableBackgroundScheduler() 的实现。

//Scheduler    @NonNull    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; }


这段代码对 build version 进行了判断。若 >=23,则返回 SystemJobScheduler(),即利用 JobScheduler 进行任务管理。<23 的时候先尝试使用 GcmScheduler 进行管理。若无法创建 GcmScheduler 则返回 SystemAlarmScheduler() 使用 AlamManager 进行任务管理。返回的这个 Scheduler 是用来执行周期性,或者有约束性的任务。由此可见,WorkManager 创建了两个 Scheduler,分别为执行非约束非周期性任务的 GreedyScheduler,和执行约束性周期性任务的 SystemJobScheduler/GcmBasedScheduler/SystemAlarmScheduler。


这几种 Scheduler 的构造和执行之后再分析。


之后初始化 Processor。Processor 存储了 Configuration,TaskExecutor,WorkDatabase,schedulers 等,用来在适当的时机进行任务调度。再来看 internalInit():

//WorkManagerImpl    private void internalInit(@NonNull Context context,            @NonNull Configuration configuration,            @NonNull TaskExecutor workTaskExecutor,            @NonNull WorkDatabase workDatabase,            @NonNull List<Scheduler> schedulers,            @NonNull Processor processor) {
context = context.getApplicationContext(); mContext = context; mConfiguration = configuration; mWorkTaskExecutor = workTaskExecutor; mWorkDatabase = workDatabase; mSchedulers = schedulers; mProcessor = processor; mPreferenceUtils = new PreferenceUtils(workDatabase); mForceStopRunnableCompleted = false;
// Checks for app force stops. mWorkTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this)); }


记录了 Configuration,TaskExecutor,WorkDatabase,schedulers,Processor 等。然后我们看最后一行执行语句,启动了一个 ForceStopRunnable,这个 Runnable 是干什么用的呢?直接看 run() 的实现:

//ForceStopRunnable    @Override    public void run() {        // Migrate the database to the no-backup directory if necessary.        WorkDatabasePathHelper.migrateDatabase(mContext);        // Clean invalid jobs attributed to WorkManager, and Workers that might have been        // interrupted because the application crashed (RUNNING state).        Logger.get().debug(TAG, "Performing cleanup operations.");        try {            boolean needsScheduling = cleanUp();            if (shouldRescheduleWorkers()) {                Logger.get().debug(TAG, "Rescheduling Workers.");                mWorkManager.rescheduleEligibleWork();                // Mark the jobs as migrated.                mWorkManager.getPreferenceUtils().setNeedsReschedule(false);            } else if (isForceStopped()) {                Logger.get().debug(TAG, "Application was force-stopped, rescheduling.");                mWorkManager.rescheduleEligibleWork();            } else if (needsScheduling) {                Logger.get().debug(TAG, "Found unfinished work, scheduling it.");                Schedulers.schedule(                        mWorkManager.getConfiguration(),                        mWorkManager.getWorkDatabase(),                        mWorkManager.getSchedulers());            }            mWorkManager.onForceStopRunnableCompleted();        } catch (SQLiteCantOpenDatabaseException                | SQLiteDatabaseCorruptException                | SQLiteAccessPermException exception) {            // ForceStopRunnable is usually the first thing that accesses a database (or an app's            // internal data directory). This means that weird PackageManager bugs are attributed            // to ForceStopRunnable, which is unfortunate. This gives the developer a better error            // message.            String message =                    "The file system on the device is in a bad state. WorkManager cannot access "                            + "the app's internal data store.";            Logger.get().error(TAG, message, exception);            throw new IllegalStateException(message, exception);        }    }


这段代码的实现细节先不做深究。但是很明显,这个 Runnable 的作用就是在 WorkManager 初始化过程中,发现了未完成的,需要重新执行的任务,或者 app 被强制 kill 的情况下,直接对 Scheduler 进行调度。到此,一个 WorkManager 的初始化流程就完成了。


总结:

  1. WorkManager 的初始化是在 app 冷启动后,由 WorkManagerInitializer 这个 ContentProvider 执行的。
  2. 初始化过程包含了 Configuration,WorkManagerTaskExecutor,WorkDatabase,Schedulers,Processor 等的初始化过程。
  3. Schedulers 有两个。

    (1) GreedyScheduler: 执行没有任何约束的非周期性的任务。
    (2) SystemJobScheduler/GcmBasedScheduler/SystemAlarmScheduler: 执行周期性或者有约束性的任务。优先返回 SystemJobScheduler,在 build version 小于 23 的情况下先尝试返回 GcmBasedScheduler,若返回为空再返回 SystemAlarmScheduler。
  4. 初始化的最后,会根据情况找到需要被执行的任务进行调度执行。

WorkManager 的初始化流程图:

4.1.2. WorkRequest 的创建

梳理完 WorkManager 的初始化过程后,我们回到示例代码,创建一个OneTimeWorkRequest
val work1Request = OneTimeWorkRequestBuilder<Worker1>().build()
//OneTimeWorkRequest.Builder        /**         * Creates a {@link OneTimeWorkRequest}.         *         * @param workerClass The {@link ListenableWorker} class to run for this work         */        public Builder(@NonNull Class<? extends ListenableWorker> workerClass) {            super(workerClass);            mWorkSpec.inputMergerClassName = OverwritingInputMerger.class.getName();        }
//WorkRequest.Builder        Builder(@NonNull Class<? extends ListenableWorker> workerClass) {            mId = UUID.randomUUID();            mWorkerClass = workerClass;            mWorkSpec = new WorkSpec(mId.toString(), workerClass.getName());            addTag(workerClass.getName());        }

OneTimeWorkRequest 为 builder 对象创建了 WorkSpec 对象用来保存任务 id 和类名,其中 id 是通过 UUID 自动生成的。request 的 tag 默认是通过类名生成的,外部也可调用 addTag() 方法设置标签。另外为 WorkSpec 设置了默认的任务输入流的合并规则: OverwritingInputMerger。接着看 build() 方法的实现:
//WorkRequest.Builder        public final @NonNull W build() {            W returnValue = buildInternal();            // Create a new id and WorkSpec so this WorkRequest.Builder can be used multiple times.            mId = UUID.randomUUID();            mWorkSpec = new WorkSpec(mWorkSpec);            mWorkSpec.id = mId.toString();            return returnValue;        }

buildInternal() 方法返回了一个 WorkRequest 对象,这是个抽象方法,在子类 OneTimeWorkRequest.Builder 中的实现如下:
//OneTimeWorkRequest.Builder        @Override        @NonNull OneTimeWorkRequest buildInternal() {            if (mBackoffCriteriaSet                    && Build.VERSION.SDK_INT >= 23                    && mWorkSpec.constraints.requiresDeviceIdle()) {                throw new IllegalArgumentException(                        "Cannot set backoff criteria on an idle mode job");            }            if (mWorkSpec.runInForeground                    && Build.VERSION.SDK_INT >= 23                    && mWorkSpec.constraints.requiresDeviceIdle()) {                throw new IllegalArgumentException(                        "Cannot run in foreground with an idle mode constraint");            }            return new OneTimeWorkRequest(this);        }

由于我们没有为 WorkSpec 设置其他属性,目前也没有约束条件,所以直接返回一个 OneTimeWorkRequest 对象。
//OneTimeWorkRequest    OneTimeWorkRequest(Builder builder) {        super(builder.mId, builder.mWorkSpec, builder.mTags);    }

把 Builder的id, WorkSpec 对象和 tag 赋给 OneTimeWorkRequest 对象。再回到 Builder 的 build() 方法:
//OneTimeWorkRequest.Builder        public final @NonNull W build() {            W returnValue = buildInternal();            // Create a new id and WorkSpec so this WorkRequest.Builder can be used multiple times.            mId = UUID.randomUUID();            mWorkSpec = new WorkSpec(mWorkSpec);            mWorkSpec.id = mId.toString();            return returnValue;        }

在 buildInternal() 拿到 OneTimeWorkRequest 对象之后,为 Builder 创建了一个新的 WorkSpec 对象,并赋予了新的 UUID。虽然与原先的 WorkSpec 对象中每个属性的值是一致的,但指向了不同的内存地址。这么做的目的是为了这个 Builder 对象可被重复利用。好了,现在我们一个任务的 WorkRequest 创建就完成了。

总结:

WorkRequest 的创建是为了持有三个重要的成员变量。分别是:
  1. mId: 由 UUID 生成的任务 id。
  2. mWorkSpec: 每个任务的属性。
  3. mTags: 每个任务的标签。


WorkRequest 创建的流程图

4.2 非约束条件任务的执行过程

执行 OneTimeWorkRequest

WorkManager.getInstance(this).enqueue(work1Request)

根据第一节的分析,WorkManager 是个单例,在 app 启动的时候就已经被初始化了。所以直接看 enqueue() 的实现:
//WorkManager    @NonNull    public final Operation enqueue(@NonNull WorkRequest workRequest) {        return enqueue(Collections.singletonList(workRequest));    }
//WorkManager   @NonNull    public abstract Operation enqueue(@NonNull List<? extends WorkRequest> requests);
//WorkManagerImpl    @NonNull    public Operation enqueue(            @NonNull List<? extends WorkRequest> workRequests) {
// This error is not being propagated as part of the Operation, as we want the // app to crash during development. Having no workRequests is always a developer error. if (workRequests.isEmpty()) { throw new IllegalArgumentException( "enqueue needs at least one WorkRequest."); } return new WorkContinuationImpl(this, workRequests).enqueue(); }

创建一个 WorkContinuationImpl() 对象,再执行 enqueue() 方法。WorkContinuationImpl 是 WorkContinuation 的子类。用来把多个 OneTimeWorkRequest 根据需求串行,并行或合并处理。我们熟悉的 then(),combine(),enqueue() 等都是这个类的方法。

//WorkContinuationImpl    WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl,            String name,            ExistingWorkPolicy existingWorkPolicy,            @NonNull List<? extends WorkRequest> work,            @Nullable List<WorkContinuationImpl> 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);        }    }

WorkContinuation 保存了任务相关的所有信息,如 WorkManager,WorkRequest,父 WorkContinuation 等。继续看 WorkContinuationImpl 的 enqueue() 方法的实现:
//WorkContinuationImpl    @Override    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;    }

WorkManager 的 TaskExecutor 执行了 EnqueueRunnable。EnqueueRunnable 中 run() 的实现:
//EnqueueRunnable    @Override    public void run() {        try {            if (mWorkContinuation.hasCycles()) {                throw new IllegalStateException(                        String.format("WorkContinuation has cycles (%s)", mWorkContinuation));            }            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() 的作用是把 WorkSpec 存入到数据库,并对任务的状态进行校验。当前的 case 会返回 true。PackageManagerHelper.setComponentEnabled() 开启了 RescheduleReceiver。通过反编译我们得知这个 Receiver 是在 AndroidManifest 中注册的,默认是 disable 的。监听了开机,时间变化,时区变化这三个广播。
//AndroidManifest        <receiver android:directBootAware="false" android:enabled="false" android:exported="false" android:name="androidx.work.impl.background.systemalarm.RescheduleReceiver">            <intent-filter>                <action android:name="android.intent.action.BOOT_COMPLETED"/>                <action android:name="android.intent.action.TIME_SET"/>                <action android:name="android.intent.action.TIMEZONE_CHANGED"/>            </intent-filter>        </receiver>

scheduleWorkInBackground() 的实现:
//EnqueueRunnable    /**     * Schedules work on the background scheduler.     */    @VisibleForTesting    public void scheduleWorkInBackground() {        WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();        Schedulers.schedule(                workManager.getConfiguration(),                workManager.getWorkDatabase(),                workManager.getSchedulers());    }

这部分就是任务调度的实现。拿到 WorkManager 对象后调用了 Schedulers.schedule() 方法,传入了 Configuration, WorkDatabase, Scheduler 这三个对象。执行 schedule() 方法:
//Schedulers    public static void schedule(            @NonNull Configuration configuration,            @NonNull WorkDatabase workDatabase,            List<Scheduler> schedulers) {        if (schedulers == null || schedulers.size() == 0) {            return;        }
WorkSpecDao workSpecDao = workDatabase.workSpecDao(); List<WorkSpec> eligibleWorkSpecs;
workDatabase.beginTransaction(); try { eligibleWorkSpecs = workSpecDao.getEligibleWorkForScheduling( configuration.getMaxSchedulerLimit()); if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) { long now = System.currentTimeMillis();
// Mark all the WorkSpecs as scheduled. // Calls to Scheduler#schedule() could potentially result in more schedules // on a separate thread. Therefore, this needs to be done first. for (WorkSpec workSpec : eligibleWorkSpecs) { workSpecDao.markWorkSpecScheduled(workSpec.id, now); } } workDatabase.setTransactionSuccessful(); } finally { workDatabase.endTransaction(); }
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); } } }

先进行了一系列的数据库操作,然后开始根据条件每个任务进行调度。其中 eligibleWorkSpecs 返回的是在 ENQUEUED 状态下,未被执行且未被取消的 WorkSpec 列表,然后更新这些任务的 request 状态到数据库。最后遍历 schedulers 调用 scheduler.schedule() 对每个任务进行调度处理。由于示例代码创建的是没有约束的一次性任务,所以看一下 GreedyScheduler 对于 schedule() 方法的实现:
//GreedyScheduler    @Override    public void schedule(@NonNull WorkSpec... workSpecs) {        if (mIsMainProcess == null) {            // The default process name is the package name.            mIsMainProcess = TextUtils.equals(mContext.getPackageName(), getProcessName());        }
if (!mIsMainProcess) { Logger.get().info(TAG, "Ignoring schedule request in non-main process"); return; }
registerExecutionListenerIfNeeded();
// Keep track of the list of new WorkSpecs whose constraints need to be tracked. // Add them to the known list of constrained WorkSpecs and call replace() on // WorkConstraintsTracker. That way we only need to synchronize on the part where we // are updating mConstrainedWorkSpecs. List<WorkSpec> constrainedWorkSpecs = new ArrayList<>(); List<String> constrainedWorkSpecIds = new ArrayList<>(); for (WorkSpec workSpec : workSpecs) { if (workSpec.state == WorkInfo.State.ENQUEUED && !workSpec.isPeriodic() && workSpec.initialDelay == 0L && !workSpec.isBackedOff()) { if (workSpec.hasConstraints()) { if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) { // Ignore requests that have an idle mode constraint. Logger.get().debug(TAG, String.format("Ignoring WorkSpec %s, Requires device idle.", workSpec)); } else if (SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) { // Ignore requests that have content uri triggers. Logger.get().debug(TAG, String.format("Ignoring WorkSpec %s, Requires ContentUri triggers.", workSpec)); } else { constrainedWorkSpecs.add(workSpec); constrainedWorkSpecIds.add(workSpec.id); } } else { Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id)); mWorkManagerImpl.startWork(workSpec.id); } } }
// onExecuted() which is called on the main thread also modifies the list of mConstrained // WorkSpecs. Therefore we need to lock here. synchronized (mLock) { if (!constrainedWorkSpecs.isEmpty()) { Logger.get().debug(TAG, String.format("Starting tracking for [%s]", TextUtils.join(",", constrainedWorkSpecIds))); mConstrainedWorkSpecs.addAll(constrainedWorkSpecs); mWorkConstraintsTracker.replace(mConstrainedWorkSpecs); } } }

在: (1) WorkSpec是ENQUEUED的状态 (2) 非周期性任务 (3) 非延迟任务 (4) 非撤销的任务 (5) 没有其它约束的任务 满足这五个条件后,直接调用:
//GreedySchedulermWorkManagerImpl.startWork(workSpec.id);

让 WorkManager 直接去执行任务。继续看 startWork() 的实现:
//WorkManagerImpl    /**     * @param workSpecId The {@link WorkSpec} id to start     * @hide     */    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)    public void startWork(@NonNull String workSpecId) {        startWork(workSpecId, null);    }
//WorkManagerImpl    /**     * @param workSpecId The {@link WorkSpec} id to start     * @param runtimeExtras The {@link WorkerParameters.RuntimeExtras} associated with this work     * @hide     */    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)    public void startWork(            @NonNull String workSpecId,            @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {        mWorkTaskExecutor                .executeOnBackgroundThread(                        new StartWorkRunnable(this, workSpecId, runtimeExtras));    }

WorkTaskExecutor 对任务进行了调度。StartWorkRunnable 的 run() 的实现:
//StartWorkRunnable    @Override    public void run() {        mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);    }

StartWorkRunnable 会将任务的信息交给 Processor,由 Processor 调用 startWork() 去执行任务:
//Processor    /**     * Starts a given unit of work in the background.     *     * @param id The work id to execute.     * @param runtimeExtras The {@link WorkerParameters.RuntimeExtras} for this work, if any.     * @return {@code true} if the work was successfully enqueued for processing     */    public boolean startWork(            @NonNull String id,            @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
WorkerWrapper workWrapper; synchronized (mLock) { // Work may get triggered multiple times if they have passing constraints // and new work with those constraints are added. if (mEnqueuedWorkMap.containsKey(id)) { Logger.get().debug( TAG, String.format("Work %s is already enqueued for processing", id)); return false; }
workWrapper = new WorkerWrapper.Builder( mAppContext, mConfiguration, mWorkTaskExecutor, this, mWorkDatabase, id) .withSchedulers(mSchedulers) .withRuntimeExtras(runtimeExtras) .build(); ListenableFuture<Boolean> 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; }

startWork() 方法中创建了一个 WorkerWrapper 的 Runnable 对象,交由 WorkTaskExecutor 调度处理。WorkerWrapper 的 run() 方法的实现:
//WorkerWrapper    @WorkerThread    @Override    public void run() {        mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);        mWorkDescription = createWorkDescription(mTags);        runWorker();    }
//WorkerWrapperprivate void runWorker() {        if (tryCheckForInterruptionAndResolve()) {            return;        }
mWorkDatabase.beginTransaction(); try { mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId); ... mWorkDatabase.setTransactionSuccessful(); } finally { mWorkDatabase.endTransaction(); }
// Merge inputs. This can be potentially expensive code, so this should not be done inside // a database transaction. ...
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));
// Not always creating a worker here, as the WorkerWrapper.Builder can set a worker override // in test mode. if (mWorker == null) { mWorker = mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback( mAppContext, mWorkSpec.workerClassName, params); } ...
// Try to set the work to the running state. Note that this may fail because another thread // may have modified the DB since we checked last at the top of this function. if (trySetRunning()) { if (tryCheckForInterruptionAndResolve()) { return; }
final SettableFuture<ListenableWorker.Result> future = SettableFuture.create(); // Call mWorker.startWork() on the main thread. 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); }
} });
// Avoid synthetic accessors. ... }

这段代码很长,我们省略了一些判断步骤和与示例无关的参数设置。先创建一个 WorkerParameters 对象。然后调用 mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback() 方法创建 Worker 对象。这个方法我们不展开了,返回的就是我们自己的 Woker 对象,即 Worker1 的实例。之后交由 WorkTaskExecutor 调度处理。在 run() 方法的实现,我们看到调用了 mWorker.startWork() 方法:
//ListenableWorker    @MainThread    public abstract @NonNull ListenableFuture<Result> startWork();

ListenableWorker 是个抽象类,是所有 Worker 的父类。Worker1 也继承 Worker 类,startWork()在Worker 中的实现:
//Worker    @Override    public final @NonNull ListenableFuture<Result> 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; }

在 run() 的实现执行了 doWork() 方法,即执行了我们 Worker1 的 doWork() 方法。

//Worker1class Worker1(appContext: Context, workerParams: WorkerParameters) :    Worker(appContext, workerParams) {
override fun doWork(): Result { Thread.sleep(5000) return Result.success() }}

在执行完这个任务后,返回了 success。Worker 也就执行完成了。回到 WorkerWrapper 的 runWorker() 方法看接下来的处理:

//WorkerWrapper    private void runWorker() {        ...            final SettableFuture<ListenableWorker.Result> future = SettableFuture.create();            // Call mWorker.startWork() on the main thread.            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);                            }
} });
// Avoid synthetic accessors. final String workDescription = mWorkDescription; future.addListener(new Runnable() { @Override @SuppressLint("SyntheticAccessor") public void run() { try { // If the ListenableWorker returns a null result treat it as a failure. ListenableWorker.Result result = future.get(); if (result == null) { Logger.get().error(TAG, String.format( "%s returned a null result. Treating it as a failure.", mWorkSpec.workerClassName)); } else { Logger.get().debug(TAG, String.format("%s returned a %s result.", mWorkSpec.workerClassName, result)); mResult = result; } } catch (CancellationException exception) { // Cancellations need to be treated with care here because innerFuture // cancellations will bubble up, and we need to gracefully handle that. Logger.get().info(TAG, String.format("%s was cancelled", workDescription), exception); } catch (InterruptedException | ExecutionException exception) { Logger.get().error(TAG, String.format("%s failed because it threw an exception/error", workDescription), exception); } finally { onWorkFinished(); } } }, mWorkTaskExecutor.getBackgroundExecutor()); } else { resolveIncorrectStatus(); }    }

startWork() 返回了一个 Future 对象 mInnerFuture,调用 future.setFuture(mInnerFuture) 去处理 doWork() 返回的 result。再经过一系列判断后,最终执行了 onWorkFinished() 方法:
//WorkerWrapper    void onWorkFinished() {        boolean isWorkFinished = false;        if (!tryCheckForInterruptionAndResolve()) {            mWorkDatabase.beginTransaction();            try {                WorkInfo.State state = mWorkSpecDao.getState(mWorkSpecId);                mWorkDatabase.workProgressDao().delete(mWorkSpecId);                if (state == null) {                    // state can be null here with a REPLACE on beginUniqueWork().                    // Treat it as a failure, and rescheduleAndResolve() will                    // turn into a no-op. We still need to notify potential observers                    // holding on to wake locks on our behalf.                    resolve(false);                    isWorkFinished = true;                } else if (state == RUNNING) {                    handleResult(mResult);                    // Update state after a call to handleResult()                    state = mWorkSpecDao.getState(mWorkSpecId);                    isWorkFinished = state.isFinished();                } else if (!state.isFinished()) {                    rescheduleAndResolve();                }                mWorkDatabase.setTransactionSuccessful();            } finally {                mWorkDatabase.endTransaction();            }        }
// Try to schedule any newly-unblocked workers, and workers requiring rescheduling (such as // periodic work using AlarmManager). This code runs after runWorker() because it should // happen in its own transaction.
// Cancel this work in other schedulers. For example, if this work was // completed by GreedyScheduler, we should make sure JobScheduler is informed // that it should remove this job and AlarmManager should remove all related alarms. if (mSchedulers != null) { if (isWorkFinished) { for (Scheduler scheduler : mSchedulers) { scheduler.cancel(mWorkSpecId); } } Schedulers.schedule(mConfiguration, mWorkDatabase, mSchedulers); } }

在 onWorkFinished() 会对刚刚执行完毕的任务作进一步处理。首先获取任务的当前状态 state,然后从 db 中删除这个任务,再根据 state 作进一步处理。在我们的示例中,这时候 state 应该是 RUNNING,我们看一下 handleResult(mResult) 的实现:
//WorkerWrapper    private void handleResult(ListenableWorker.Result result) {        if (result instanceof ListenableWorker.Result.Success) {            Logger.get().info(                    TAG,                    String.format("Worker result SUCCESS for %s", mWorkDescription));            if (mWorkSpec.isPeriodic()) {                resetPeriodicAndResolve();            } else {                setSucceededAndResolve();            }
} else if (result instanceof ListenableWorker.Result.Retry) { Logger.get().info( TAG, String.format("Worker result RETRY for %s", mWorkDescription)); rescheduleAndResolve(); } else { Logger.get().info( TAG, String.format("Worker result FAILURE for %s", mWorkDescription)); if (mWorkSpec.isPeriodic()) { resetPeriodicAndResolve(); } else { setFailedAndResolve(); } } }

在 handleResult() 方法中会根据任务类型和 result 结果进行不同的处理。例如周期性的任务会重新将这个任务的状态设置为 ENQUEUED,更新其他相关参数,并更新数据库。我们示例中已经完成的一次性任务将会被更新成 SUCCEEDED 的状态,具体的处理的过程就不展开了。handleResult() 执行完毕后更新 isWorkFinished。如果 isWorkFinished 为 true,由于我们在 GreedyScheduler 已经处理了这个任务,为了避免这个任务被其他 schedulers 处理,WorkManager 遍历了 mSchedulers 列表,并将这个任务从其他 schedulers 中移除。最后再次执行 Schedulers.schedule() 方法,schedule 下一个任务。


总结:

  1. 在 WorkManager 执行了 enqueue() 后,创建 WorkContinuationImpl 对象执行 enqueue() 方法。

  2. WorkContinuationImpl 持有的 EnqueueRunnable 对象将任务添加到 db,并交给 Schedulers 去调度。

  3. Schedulers 将任务交给每一个 Scheduler 去处理。在我们的示例中,GreedyScheduler 会先处理这个任务。

  4. GreedyScheduler 经过一系列判断后,调用 WorkManager 的 startWork() 方法执行这种一次性,非延迟,无约束的任务。

  5. WorkManager 持有的 StartWorkRunnable 对象会将任务交给 Processor 去处理,执行 startWork() 方法。

  6. Processor 创建一个 WorkerWrapper 对象,由它去调用 Worker 的 startWork() 方法,执行我们自定义 worker 的任务,并返回相应的 result。

  7. 任务完成后,WorkerWrapper 会根据 result 对任务状态,db 等进行更新,然后 schedule 下一个任务。


WorkManager 任务执行流程图:

4.3 带约束的任务的执行过程

上面的章节我们分析了一个没有约束条件的一次性任务的执行过程。接下来我们来分析一个带约束条件的任务的执行过程。

创建一个非低电量才能执行的任务:
val constraints = Constraints.Builder()                .setRequiresBatteryNotLow(true)                .build()val work2Request = OneTimeWorkRequestBuilder<Worker2>()                .setConstraints(constraints)                .build()WorkManager.getInstance(this).enqueue(work2Request)


任务的创建过程中,会为 WorkSpec 添加 Constraints 属性。
public final @NonNull B setConstraints(@NonNull Constraints constraints) {            mWorkSpec.constraints = constraints;            return getThis();        }


在任务执行的过程中,由于增加了约束条件,根据之前章节的分析,常驻的 GreedyScheduler 的 schedule() 方法将不会 startWork(),而是根据 build version 交由 SystemJobScheduler 或 SystemAlarmScheduler 进行处理。先来看使用 SystemJobScheduler 的情况:

4.3.1. SystemJobScheduler (Build Version >=23)
SystemJobScheduler 使用的是 JobScheduler 来调度执行任务。由于 JobScheduler 的实现过程分析不在本文的讨论范围,所以只看 WorkManager 是如何使用 JobScheduler 进行任务调度的。通常 JobScheduler 的使用步骤如下:
  1. 创建 JobService
  2. 配置 JobInfo
  3. 执行

SystemJobService: SystemJobService 是执行任务的服务类,在 onStartJob() 中,会调用 WorkManagerImpl 的 startWork() 执行任务。
//SystemJobService    @Override    public boolean onStartJob(@NonNull JobParameters params) {        ... ...
String workSpecId = getWorkSpecIdFromJobParameters(params); ... ...
synchronized (mJobParameters) { ... ... mJobParameters.put(workSpecId, params); }
... ... mWorkManagerImpl.startWork(workSpecId, runtimeExtras); return true; }


SystemJobScheduler : 在初始化 SystemJobScheduler 的时候会获取 JobScheduler 对象:
//SystemJobScheduler     public SystemJobScheduler(@NonNull Context context, @NonNull WorkManagerImpl workManager) {        this(context,                workManager,                (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE),                new SystemJobInfoConverter(context));    }


SystemJobScheduler 的 schedule() 方法执行了 scheduleInternal():

//SystemJobScheduler    public void scheduleInternal(WorkSpec workSpec, int jobId) {        JobInfo jobInfo = mSystemJobInfoConverter.convert(workSpec, jobId);        Logger.get().debug(                TAG,                String.format("Scheduling work ID %s Job ID %s", workSpec.id, jobId));        try {            mJobScheduler.schedule(jobInfo);        } catch (IllegalStateException e) {            ... ...            throw new IllegalStateException(message, e);        } catch (Throwable throwable) {            // OEM implementation bugs in JobScheduler cause the app to crash. Avoid crashing.            Logger.get().error(TAG, String.format("Unable to schedule %s", workSpec), throwable);        }    }


SystemJobInfoConverter.convert() 方法就是创建了一个 JobInfo,并将 Constraints 里的约束条件赋予 JobInfo 对象,之后便执行了 JobScheduler.schedule(),根据约束条件对任务进行调度。


4.3.2. SystemAlarmScheduler (Build Version <23)
SystemAlarmScheduler 使用的是 AlarmManager 来调度执行任务。由于 AlarmManager 的实现过程分析不在本文的讨论范围,所以只看 WorkManager 是如何使用 AlarmManager 进行任务调度的。反编译 apk 后,在 AndroidManifest 里有如下 receiver 注册:
<receiver android:directBootAware="false" android:enabled="false" android:exported="false" android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy">    <intent-filter>          <action android:name="android.intent.action.BATTERY_OKAY"/>          <action android:name="android.intent.action.BATTERY_LOW"/>    </intent-filter></receiver>


在电量变化时,收到 BATTERY_LOW 的广播。在 BatteryNotLowProxy 的 onReceive() 进行处理:
//ConstraintProxy    public static class BatteryNotLowProxy extends ConstraintProxy {    }
@Override public void onReceive(Context context, Intent intent) { Logger.get().debug(TAG, String.format("onReceive : %s", intent)); Intent constraintChangedIntent = CommandHandler.createConstraintsChangedIntent(context); context.startService(constraintChangedIntent); }


createConstraintsChangedIntent() 的执行如下:
//ConstraintProxy    static Intent createConstraintsChangedIntent(@NonNull Context context) {        Intent intent = new Intent(context, SystemAlarmService.class);        intent.setAction(ACTION_CONSTRAINTS_CHANGED);        return intent;    }


SystemAlarmService 的 onStartCommand() 处理如下:
 @Override    public int onStartCommand(Intent intent, int flags, int startId) {        super.onStartCommand(intent, flags, startId);        ... ...
if (intent != null) { mDispatcher.add(intent, startId); }
// If the service were to crash, we want all unacknowledged Intents to get redelivered. return Service.START_REDELIVER_INTENT; }


调用了 SystemAlarmDispatcher.add() 方法。
//SystemAlarmDispatcher@MainThread    public boolean add(@NonNull final Intent intent, final int startId) {        ... ...        if (CommandHandler.ACTION_CONSTRAINTS_CHANGED.equals(action)                && hasIntentWithAction(CommandHandler.ACTION_CONSTRAINTS_CHANGED)) {            return false;        }
intent.putExtra(KEY_START_ID, startId); synchronized (mIntents) { boolean hasCommands = !mIntents.isEmpty(); mIntents.add(intent); if (!hasCommands) { // Only call processCommand if this is the first command. // The call to dequeueAndCheckForCompletion will process the remaining commands // in the order that they were added. processCommand(); } } return true; }


add() 方法中执行了 processCommand(),这段代码的核心执行语句是:
//SystemAlarmDispatchermCommandHandler.onHandleIntent(mCurrentIntent, startId,                                    SystemAlarmDispatcher.this);


在 CommandHandler 的 onHandleIntent() 方法中,action 为 ACTION_CONSTRAINTS_CHANGED 的执行是:
//CommandHandler if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {            handleConstraintsChanged(intent, startId, dispatcher);        } 
//CommandHandler    private void handleConstraintsChanged(            @NonNull Intent intent, int startId,            @NonNull SystemAlarmDispatcher dispatcher) {
Logger.get().debug(TAG, String.format("Handling constraints changed %s", intent)); // Constraints changed command handler is synchronous. No cleanup // is necessary. ConstraintsCommandHandler changedCommandHandler = new ConstraintsCommandHandler(mContext, startId, dispatcher); changedCommandHandler.handleConstraintsChanged(); }


在 handleConstraintsChanged() 方法的执行中,会创建一个 action 为 ACTION_DELAY_MET 的 Intent 然后由 SystemAlarmDispatcher 发送出去,实际上也是调用了 SystemAlarmDispatcher.add() 方法。回到 SystemAlarmDispatcher 的 add() 流程。
//ConstraintsCommandHandlerIntent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId);            Logger.get().debug(TAG, String.format(                    "Creating a delay_met command for workSpec with id (%s)", workSpecId));            mDispatcher.postOnMainThread(                    new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId));


回到 onHandleIntent() 方法,在 CommandHandler 的 onHandleIntent() 方法中,action 为 ACTION_DELAY_MET 的执行是:
//CommandHandlerelse if (ACTION_DELAY_MET.equals(action)) {                    handleDelayMet(intent, startId, dispatcher);                } 

handleDelayMet() 的执行过程,会调用 DelayMetCommandHandler 的 handleProcessWork() 方法,接着执行 onAllConstraintsMet():
    @Override    public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {        ... ...        synchronized (mLock) {            if (mCurrentState == STATE_INITIAL) {                ... ...                boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);                ... ...            } else {                Logger.get().debug(TAG, String.format("Already started work for %s", mWorkSpecId));            }        }    }


到这里终于看到由 SystemAlarmDispatcher 调用了 Processor 的 startWork() 方法,回到了之前章节分析的任务执行流程。到此为止,一个任务在不同条件下的创建,执行流程就分析完毕。



结语


WorkManager 的使用方法简单,但是在使用时还是要分清场景,适用于可延迟,周期性,必须执行完成的任务。通过对源码的分析,WorkManager 会针对不同 Android 版本的选择适当的策略。细致阅读代码,会发现针对指定的系统版本还有一些小的优化点。WorkManager 目前已经比较稳定,所以如果在场景适合的情况下,推荐使用 WorkManager 来代替原有的任务管理方案。




长按右侧二维码

查看更多开发者精彩分享




"开发者说·DTalk" 面向中国开发者们征集 Google 移动应用 (apps & games) 相关的产品/技术内容。欢迎大家前来分享您对移动应用的行业洞察或见解、移动开发过程中的心得或新发现、以及应用出海的实战经验总结和相关产品的使用反馈等。我们由衷地希望可以给这些出众的中国开发者们提供更好展现自己、充分发挥自己特长的平台。我们将通过大家的技术内容着重选出优秀案例进行谷歌开发技术专家 (GDE) 的推荐。



 

 点击屏末 |  | 即刻报名参与 "开发者说·DTalk" 

 



登录查看更多
0

相关内容

基于机器学习的数据库技术综述
专知会员服务
54+阅读 · 2021年1月2日
专知会员服务
30+阅读 · 2020年12月21日
【2020新书】使用R和Python的高级BI分析,425页pdf
专知会员服务
33+阅读 · 2020年10月14日
【干货书】Python高级数据科学分析,424页pdf
专知会员服务
114+阅读 · 2020年8月7日
【2020新书】C++20快速语法参考,第4版,209页pdf
专知会员服务
71+阅读 · 2020年8月5日
专知会员服务
87+阅读 · 2020年8月2日
【实用书】学习用Python编写代码进行数据分析,103页pdf
专知会员服务
194+阅读 · 2020年6月29日
高效医疗图像分析的统一表示
专知会员服务
34+阅读 · 2020年6月23日
【干货】大数据入门指南:Hadoop、Hive、Spark、 Storm等
专知会员服务
95+阅读 · 2019年12月4日
社区分享|如何让模型在生产环境上推理得更快
阿里开源的微服务中间件 Nacos
性能与架构
5+阅读 · 2019年11月5日
微信小程序官方发布支持Python的自动化测试框架
Python程序员
8+阅读 · 2019年9月4日
漏洞预警丨Xstream远程代码执行漏洞
FreeBuf
4+阅读 · 2019年7月25日
浅谈 Kubernetes 在生产环境中的架构
DevOps时代
11+阅读 · 2019年5月8日
I2P - 适用于黑客的Android应用程序
黑白之道
30+阅读 · 2019年3月6日
百度开源项目OpenRASP快速上手指南
黑客技术与网络安全
5+阅读 · 2019年2月12日
B端产品需求管理经验分享
产品100干货速递
6+阅读 · 2019年1月28日
10个深度学习软件的安装指南(附代码)
数据派THU
17+阅读 · 2017年11月18日
解析京东大数据下高效图像特征提取方案
京东大数据
4+阅读 · 2017年9月29日
Arxiv
0+阅读 · 2021年1月24日
Arxiv
4+阅读 · 2018年9月6日
Arxiv
4+阅读 · 2018年1月29日
Arxiv
4+阅读 · 2016年12月29日
VIP会员
相关VIP内容
基于机器学习的数据库技术综述
专知会员服务
54+阅读 · 2021年1月2日
专知会员服务
30+阅读 · 2020年12月21日
【2020新书】使用R和Python的高级BI分析,425页pdf
专知会员服务
33+阅读 · 2020年10月14日
【干货书】Python高级数据科学分析,424页pdf
专知会员服务
114+阅读 · 2020年8月7日
【2020新书】C++20快速语法参考,第4版,209页pdf
专知会员服务
71+阅读 · 2020年8月5日
专知会员服务
87+阅读 · 2020年8月2日
【实用书】学习用Python编写代码进行数据分析,103页pdf
专知会员服务
194+阅读 · 2020年6月29日
高效医疗图像分析的统一表示
专知会员服务
34+阅读 · 2020年6月23日
【干货】大数据入门指南:Hadoop、Hive、Spark、 Storm等
专知会员服务
95+阅读 · 2019年12月4日
相关资讯
社区分享|如何让模型在生产环境上推理得更快
阿里开源的微服务中间件 Nacos
性能与架构
5+阅读 · 2019年11月5日
微信小程序官方发布支持Python的自动化测试框架
Python程序员
8+阅读 · 2019年9月4日
漏洞预警丨Xstream远程代码执行漏洞
FreeBuf
4+阅读 · 2019年7月25日
浅谈 Kubernetes 在生产环境中的架构
DevOps时代
11+阅读 · 2019年5月8日
I2P - 适用于黑客的Android应用程序
黑白之道
30+阅读 · 2019年3月6日
百度开源项目OpenRASP快速上手指南
黑客技术与网络安全
5+阅读 · 2019年2月12日
B端产品需求管理经验分享
产品100干货速递
6+阅读 · 2019年1月28日
10个深度学习软件的安装指南(附代码)
数据派THU
17+阅读 · 2017年11月18日
解析京东大数据下高效图像特征提取方案
京东大数据
4+阅读 · 2017年9月29日
Top
微信扫码咨询专知VIP会员