WorkManager的使用,流程分析和源码分析

1.前言

WorkManager统一了对于Android后台任务的管理。在此之前,从6.0开始Google引入了Doze机制,并且在之后的几个版本对Android的后台行为及广播的限制越来越严格。在Android8.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的迁移方案。今年,在Android11的行为变更中提到,如果应用以 API 级别“R”或更高级别为目标平台,则在搭载 Android 6.0(API 级别 23)或更高版本的设备上会停用 Firebase JobDispatcher 和 GcmNetworkManager API 调用。所以在一些场景中,使用WorkManager来维护我们的后台任务可以说是官方推荐的唯一方式。
本文将介绍WorkManager的使用方式,并通过剖析WorkManager的内部实现原理,来帮助大家更好的理解WorkManager的实现。

2.WorkManager的特点与适用场景

特点:
1.保证任务一定会被执行
WorkManager有自己的数据库,每一个任务的信息与任务状态,都会保存在本地数据库中。所以即使程序没有在运行,或者在设备重启等情况下,WorkManager依然可以保证任务的执行,只是不保证任务立即被执行。
2.合理使用设备资源
在执行很多周期性或非立即执行的任务时,WorkManager提供我们API,帮助我们合理利用设备资源,避免不必要的内存,流量,电量等消耗。

适用场景:

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

3.WorkManager的使用

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

3.1创建后台任务Worker

WorkManager提供了四种Worker的创建模式:

  1. Worker:
    最简单的实现,WorkManager 会在后台线程上自动运行它。
  2. CoroutineWorker:
    CoroutineWorker针对后台工作公开挂起函数。默认情况下,它们运行默认的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 run
val constraints = Constraints.Builder()
            .setRequiresDeviceIdle(true)
            .setRequiresCharging(true)
            .build()

// ...then create a OneTimeWorkRequest that uses those constraints
val 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 ->
            })

4.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
    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;
    }

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()来根据版本创建不同的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是用来执行周期性,或者有约束性的任务。这几种Scheduler的构造和执行之后再分析。
再来看Processor的初始化:

//Processor
    public Processor(
            @NonNull Context appContext,
            @NonNull Configuration configuration,
            @NonNull TaskExecutor workTaskExecutor,
            @NonNull WorkDatabase workDatabase,
            @NonNull List<Scheduler> schedulers) {
        mAppContext = appContext;
        mConfiguration = configuration;
        mWorkTaskExecutor = workTaskExecutor;
        mWorkDatabase = workDatabase;
        mEnqueuedWorkMap = new HashMap<>();
        mForegroundWorkMap = new HashMap<>();
        mSchedulers = schedulers;
        mCancelledIds = new HashSet<>();
        mOuterListeners = new ArrayList<>();
        mForegroundLock = null;
        mLock = new Object();
    }

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的初始化流程图

屏幕快照 2020-04-28 上午10.47.30.png

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创建的流程图

屏幕快照 2020-04-28 上午10.47.53.png

4.2非约束条件任务的执行

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) 没有其它约束的任务
满足这五个条件后,直接调用:

//GreedyScheduler
mWorkManagerImpl.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);
    }

交由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();
    }
//WorkerWrapper
private 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()方法。

//Worker1
class 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任务执行流程图

屏幕快照 2020-04-28 下午2.45.28.png

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

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

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>

在电量变化时,在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(),这段代码的核心执行语句是:

//SystemAlarmDispatcher
mCommandHandler.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()流程。

//ConstraintsCommandHandler
Intent 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的执行是:

//CommandHandler
else 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()方法,回到了之前章节分析的任务执行流程。
到此为止,一个任务在不同条件下的创建,执行流程就分析完毕。

5.结语

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

6.参考文献

[1]https://developer.android.google.cn/topic/libraries/architecture/workmanager?
[2]https://developer.android.google.cn/preview/behavior-changes-11

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容