WorkManager的使用祖屏,流程分析和源碼分析

1.前言

WorkManager統(tǒng)一了對(duì)于Android后臺(tái)任務(wù)的管理。在此之前买羞,從6.0開(kāi)始Google引入了Doze機(jī)制袁勺,并且在之后的幾個(gè)版本對(duì)Android的后臺(tái)行為及廣播的限制越來(lái)越嚴(yán)格。在Android8.0時(shí)Google官方推薦開(kāi)發(fā)者使用JobScheduler來(lái)代替Service+Broadcast的后臺(tái)任務(wù)管理方式畜普。為了兼容老版本期丰,android-job, Firebase JobDispatcher和GCMNetworkManager都曾是開(kāi)發(fā)者的選擇,而Firebase JobDispatcher和GCMNetworkManager需要支持google play service,并不適用于國(guó)內(nèi)app場(chǎng)景钝荡。2018年街立,google推出的jetpack中包含了WorkManager,之后android-job停止維護(hù)化撕,google官方為Firebase JobDispatcher和GCMNetworkManager提出了WorkManager的遷移方案几晤。今年约炎,在Android11的行為變更中提到植阴,如果應(yīng)用以 API 級(jí)別“R”或更高級(jí)別為目標(biāo)平臺(tái),則在搭載 Android 6.0(API 級(jí)別 23)或更高版本的設(shè)備上會(huì)停用 Firebase JobDispatcher 和 GcmNetworkManager API 調(diào)用圾浅。所以在一些場(chǎng)景中掠手,使用WorkManager來(lái)維護(hù)我們的后臺(tái)任務(wù)可以說(shuō)是官方推薦的唯一方式。
本文將介紹WorkManager的使用方式狸捕,并通過(guò)剖析WorkManager的內(nèi)部實(shí)現(xiàn)原理喷鸽,來(lái)幫助大家更好的理解WorkManager的實(shí)現(xiàn)。

2.WorkManager的特點(diǎn)與適用場(chǎng)景

特點(diǎn):
1.保證任務(wù)一定會(huì)被執(zhí)行
WorkManager有自己的數(shù)據(jù)庫(kù)灸拍,每一個(gè)任務(wù)的信息與任務(wù)狀態(tài)做祝,都會(huì)保存在本地?cái)?shù)據(jù)庫(kù)中。所以即使程序沒(méi)有在運(yùn)行鸡岗,或者在設(shè)備重啟等情況下混槐,WorkManager依然可以保證任務(wù)的執(zhí)行,只是不保證任務(wù)立即被執(zhí)行轩性。
2.合理使用設(shè)備資源
在執(zhí)行很多周期性或非立即執(zhí)行的任務(wù)時(shí)声登,WorkManager提供我們API,幫助我們合理利用設(shè)備資源揣苏,避免不必要的內(nèi)存悯嗓,流量,電量等消耗卸察。

適用場(chǎng)景:

  1. 可延遲進(jìn)行的任務(wù)
    a.滿(mǎn)足某些條件才執(zhí)行的任務(wù)脯厨,如需要在充電時(shí)才執(zhí)行的任務(wù)。
    b.用戶(hù)無(wú)感知或可延遲感知的任務(wù)坑质,如同步配置信息合武,同步資源,同步通訊錄等洪乍。
  2. 定期重復(fù)性任務(wù)眯杏,但時(shí)效性要求不高的,如定期log上傳壳澳,數(shù)據(jù)備份等岂贩。
  3. 退出應(yīng)用后還應(yīng)繼續(xù)執(zhí)行的未完成任務(wù)。

3.WorkManager的使用

WorkManager的使用非常簡(jiǎn)單,分為如下幾個(gè)步驟:
1.創(chuàng)建一個(gè)后臺(tái)任務(wù)Worker萎津。
2.定義WorkRequest卸伞,配置運(yùn)行任務(wù)的方式和時(shí)間。
3.將任務(wù)提交給系統(tǒng)處理锉屈。
4.觀察Worker的進(jìn)度或狀態(tài)荤傲。

3.1創(chuàng)建后臺(tái)任務(wù)Worker

WorkManager提供了四種Worker的創(chuàng)建模式:

  1. Worker:
    最簡(jiǎn)單的實(shí)現(xiàn),WorkManager 會(huì)在后臺(tái)線程上自動(dòng)運(yùn)行它颈渊。
  2. CoroutineWorker:
    CoroutineWorker針對(duì)后臺(tái)工作公開(kāi)掛起函數(shù)遂黍。默認(rèn)情況下,它們運(yùn)行默認(rèn)的Dispatcher俊嗽。
  3. RxWorker:
    如果有很多現(xiàn)有異步代碼是用 RxJava 建模的建議使用雾家。與所有 RxJava2 概念一樣,可以自由選擇所需的線程處理策略绍豁。
  4. ListenableWorker:
    是Worker芯咧,CoroutineWorker,RxWorker的基類(lèi)竹揍,為需要與基于回調(diào)的異步 API進(jìn)行交互并且不使用 RxJava2 的 Java 開(kāi)發(fā)者而設(shè)計(jì)敬飒。
    Worker的創(chuàng)建示例:
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配置運(yùn)行任務(wù)方式和時(shí)間

3.2.1一次性任務(wù)

val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>().build()

3.2.2周期性任務(wù)

//執(zhí)行多次任務(wù),每隔12個(gè)小時(shí)執(zhí)行一次
val uploadWorkRequest = PeriodicWorkRequestBuilder<UploadWorker>(12, TimeUnit.HOURS)
        .build()

3.2.3帶約束條件的任務(wù)

// 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延遲任務(wù)

val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
        .setInitialDelay(10, TimeUnit.SECONDS)//符合觸發(fā)條件后芬位,延遲10秒執(zhí)行
        .build()

3.3將任務(wù)提交給系統(tǒng)處理

WorkManager.getInstance(myContext).enqueue(uploadWorkRequest)

3.3.1多任務(wù)調(diào)度:

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的進(jìn)度或狀態(tài)

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

4.WorkManager流程分析與源碼解析

這個(gè)章節(jié)將會(huì)從一下幾個(gè)方面梳理WorkManager的流程與源碼:

  1. 創(chuàng)建
    a.WorkManager的初始化无拗。
    b.WorkRequest的創(chuàng)建。
  2. 非約束條件任務(wù)的執(zhí)行晶衷。
  3. 帶約束條件任務(wù)的執(zhí)行蓝纲。
    我們從最基礎(chǔ)的流程開(kāi)始分析:
    創(chuàng)建一個(gè)簡(jiǎn)單不帶任何約束條件的一次性任務(wù)。在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創(chuàng)建

我們先梳理一下WorkManager的初始化過(guò)程税迷。

4.1.1.WorkManager的初始化

在默認(rèn)的情況下,WorkManager并不是在我們調(diào)用WorkManager.getInstance() 時(shí)創(chuàng)建的锹漱。通過(guò)反編譯一下apk箭养,會(huì)發(fā)現(xiàn)在AndroidManifest文件中注冊(cè)了名為WorkManagerInitializer的ContentProvider。因此WorkManager在app冷啟動(dòng)的時(shí)候已經(jīng)被創(chuàng)建哥牍。

//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是個(gè)單例毕泌,我們發(fā)現(xiàn)在此時(shí)WorkManager就已經(jīng)被初始化了。在initialize()之前嗅辣,會(huì)創(chuàng)建一個(gè)默認(rèn)的Configuration撼泛,實(shí)現(xiàn)如下:

//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設(shè)置了許多屬性,用來(lái)管理和調(diào)度工作的方式澡谭。通常我們使用WorkManager默認(rèn)創(chuàng)建的Configuration即可愿题。如需使用自己的Configuration,可參考官方文檔,有明確的使用說(shuō)明潘酗。我們繼續(xù)看initialize()的實(shí)現(xiàn)杆兵,由于WorkManager是個(gè)抽象類(lèi),真正的構(gòu)造方法是在他的子類(lèi)WorkManagerImpl實(shí)現(xiàn)的:

//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;
            }
        }
    }

此時(shí)sDelegatedInstance為null仔夺,WorkManager會(huì)先創(chuàng)建一個(gè)默認(rèn)的WorkManagerTaskExecutor對(duì)象琐脏,用來(lái)執(zhí)行WorkManager的任務(wù)。之后創(chuàng)建一個(gè)WorkManagerImpl對(duì)象:

//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在此時(shí)創(chuàng)建了數(shù)據(jù)庫(kù)缸兔。WorkDatabase.create()將任務(wù)列表序列化到本地日裙,并記錄他們的執(zhí)行狀態(tài)。從而保證任務(wù)在冷啟動(dòng)或硬件重啟后依舊可以執(zhí)行灶体。接著看this()的實(shí)現(xiàn):

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

到這里有三個(gè)重要的初始化步驟阅签。分別是createSchedulers()來(lái)根據(jù)版本創(chuàng)建不同的Schedulers掐暮,Processor()用來(lái)管理Schedulers的執(zhí)行蝎抽,和internalInit()真正的初始化。先看createSchedulers()的實(shí)現(xiàn):

//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一個(gè)Scheduler數(shù)組路克。其中GreedyScheduler()是常駐的樟结,用來(lái)執(zhí)行沒(méi)有任何約束的非周期性的任務(wù)。接下來(lái)看createBestAvailableBackgroundScheduler()的實(shí)現(xiàn)精算。

//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;
    }

這段代碼對(duì)build version進(jìn)行了判斷瓢宦。若>=23,則返回SystemJobScheduler()灰羽,即利用JobScheduler進(jìn)行任務(wù)管理驮履。<23的時(shí)候先嘗試使用GcmScheduler進(jìn)行管理。若無(wú)法創(chuàng)建GcmScheduler則返回SystemAlarmScheduler()使用AlamManager進(jìn)行任務(wù)管理廉嚼。返回的這個(gè)Scheduler是用來(lái)執(zhí)行周期性玫镐,或者有約束性的任務(wù)。這幾種Scheduler的構(gòu)造和執(zhí)行之后再分析怠噪。
再來(lái)看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存儲(chǔ)了Configuration恐似,TaskExecutor,WorkDatabase傍念,schedulers等矫夷,用來(lái)在適當(dāng)?shù)臅r(shí)機(jī)進(jìn)行任務(wù)調(diào)度。再來(lái)看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等忧陪。然后我們看最后一行執(zhí)行語(yǔ)句,啟動(dòng)了一個(gè)ForceStopRunnable,這個(gè)Runnable是干什么用的呢赤嚼?直接看run()的實(shí)現(xiàn):

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

這段代碼的實(shí)現(xiàn)細(xì)節(jié)先不做深究旷赖。但是很明顯,這個(gè)Runnable的作用就是在WorkManager初始化過(guò)程中更卒,發(fā)現(xiàn)了未完成的等孵,需要重新執(zhí)行的任務(wù),或者app被強(qiáng)制kill的情況下蹂空,直接對(duì)Scheduler進(jìn)行調(diào)度俯萌。
到此,一個(gè)WorkManager的初始化流程就完成了上枕。

總結(jié)

  1. WorkManager的初始化是在app冷啟動(dòng)后咐熙,由WorkManagerInitializer這個(gè)ContentProvider執(zhí)行的。
  2. 初始化過(guò)程包含了Configuration辨萍,WorkManagerTaskExecutor棋恼,WorkDatabase,Schedulers锈玉,Processor等的初始化過(guò)程爪飘。
  3. Schedulers有兩個(gè)。
    (1) GreedyScheduler:執(zhí)行沒(méi)有任何約束的非周期性的任務(wù)拉背。
    (2) SystemJobScheduler/GcmBasedScheduler/SystemAlarmScheduler:執(zhí)行周期性或者有約束性的任務(wù)师崎。優(yōu)先返回SystemJobScheduler,在build version小于23的情況下先嘗試返回GcmBasedScheduler椅棺,若返回為空再返回SystemAlarmScheduler犁罩。
  4. 初始化的最后,會(huì)根據(jù)情況找到需要被執(zhí)行的任務(wù)進(jìn)行調(diào)度執(zhí)行两疚。

WorkManager的初始化流程圖

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

4.1.2.WorkRequest的創(chuàng)建

梳理完WorkManager的初始化過(guò)程后床估,我們回到示例代碼,創(chuàng)建一個(gè)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對(duì)象創(chuàng)建了WorkSpec對(duì)象用來(lái)保存任務(wù)id和類(lèi)名鬼雀,其中id是通過(guò)UUID自動(dòng)生成的顷窒。request的tag默認(rèn)是通過(guò)類(lèi)名生成的,外部也可調(diào)用addTag()方法設(shè)置標(biāo)簽源哩。另外為WorkSpec設(shè)置了默認(rèn)的任務(wù)輸入流的合并規(guī)則:OverwritingInputMerger鞋吉。接著看build()方法的實(shí)現(xiàn):

//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()方法返回了一個(gè)WorkRequest對(duì)象,這是個(gè)抽象方法励烦,在子類(lèi)OneTimeWorkRequest.Builder中的實(shí)現(xiàn)如下:

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

由于我們沒(méi)有為WorkSpec設(shè)置其他屬性谓着,目前也沒(méi)有約束條件,所以直接返回一個(gè)OneTimeWorkRequest對(duì)象坛掠。

//OneTimeWorkRequest
    OneTimeWorkRequest(Builder builder) {
        super(builder.mId, builder.mWorkSpec, builder.mTags);
    }

把Builder的id, WorkSpec對(duì)象和tag賦給OneTimeWorkRequest對(duì)象赊锚。
再回到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對(duì)象之后治筒,為Builder創(chuàng)建了一個(gè)新的WorkSpec對(duì)象,并賦予了新的UUID舷蒲。雖然與原先的WorkSpec對(duì)象中每個(gè)屬性的值是一致的耸袜,但指向了不同的內(nèi)存地址。這么做的目的是為了這個(gè)Builder對(duì)象可被重復(fù)利用牲平。
好了堤框,現(xiàn)在我們一個(gè)任務(wù)的WorkRequest創(chuàng)建就完成了。

總結(jié)

WorkRequest的創(chuàng)建是為了持有三個(gè)重要的成員變量纵柿。分別是:

  1. mId:由UUID生成的任務(wù)id蜈抓。
  2. mWorkSpec:每個(gè)任務(wù)的屬性。
  3. mTags:每個(gè)任務(wù)的標(biāo)簽昂儒。

WorkRequest創(chuàng)建的流程圖

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

4.2非約束條件任務(wù)的執(zhí)行

WorkManager.getInstance(this).enqueue(work1Request)

根據(jù)第一節(jié)的分析沟使,WorkManager是個(gè)單例,在app啟動(dòng)的時(shí)候就已經(jīng)被初始化了渊跋。所以直接看enqueue()的實(shí)現(xiàn):

//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();
    }

創(chuàng)建一個(gè)WorkContinuationImpl()對(duì)象腊嗡,再執(zhí)行enqueue()方法。WorkContinuationImpl是WorkContinuation的子類(lèi)刹枉。用來(lái)把多個(gè)OneTimeWorkRequest串到一塊兒叽唱。我們熟悉的then(),combine()微宝,enqueue()等方法都來(lái)自于這個(gè)類(lèi)。

//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保存了任務(wù)相關(guān)的所有信息虎眨,如WorkManager蟋软,WorkRequest,父WorkContinuation等嗽桩。繼續(xù)看WorkContinuationImpl的enqueue()方法的實(shí)現(xiàn):

//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執(zhí)行了EnqueueRunnable岳守。
EnqueueRunnable中run()的實(shí)現(xiàn):

//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存入到數(shù)據(jù)庫(kù),并對(duì)任務(wù)的狀態(tài)進(jìn)行校驗(yàn)。當(dāng)前的case會(huì)返回true碌冶。PackageManagerHelper.setComponentEnabled()開(kāi)啟了RescheduleReceiver湿痢。通過(guò)反編譯我們得知這個(gè)Receiver是在AndroidManifest中注冊(cè)的,默認(rèn)是disable的扑庞。監(jiān)聽(tīng)了開(kāi)機(jī)譬重,時(shí)間變化,時(shí)區(qū)變化這三個(gè)廣播罐氨。

//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()的實(shí)現(xiàn):

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

這部分就是任務(wù)調(diào)度的實(shí)現(xiàn)臀规。拿到WorkManager對(duì)象后調(diào)用了Schedulers.schedule()方法,傳入了Configuration, WorkDatabase, Scheduler這三個(gè)對(duì)象栅隐。執(zhí)行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);
            }
        }
    }

先進(jìn)行了一系列的數(shù)據(jù)庫(kù)操作塔嬉,然后開(kāi)始根據(jù)條件每個(gè)任務(wù)進(jìn)行調(diào)度玩徊。
其中eligibleWorkSpecs返回的是在ENQUEUED狀態(tài)下,未被執(zhí)行且未被取消的WorkSpec列表谨究,然后更新這些任務(wù)的request狀態(tài)到數(shù)據(jù)庫(kù)恩袱。
最后遍歷schedulers調(diào)用scheduler.schedule()對(duì)每個(gè)任務(wù)進(jìn)行調(diào)度處理。
由于示例代碼創(chuàng)建的是沒(méi)有約束的一次性任務(wù)胶哲,所以看一下GreedyScheduler對(duì)于schedule()方法的實(shí)現(xiàn):

//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的狀態(tài)
(2) 非周期性任務(wù)
(3) 非延遲任務(wù)
(4) 非撤銷(xiāo)的任務(wù)
(5) 沒(méi)有其它約束的任務(wù)
滿(mǎn)足這五個(gè)條件后憎蛤,直接調(diào)用:

//GreedyScheduler
mWorkManagerImpl.startWork(workSpec.id);

讓W(xué)orkManager直接去執(zhí)行任務(wù)。繼續(xù)看startWork()的實(shí)現(xiàn):

//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對(duì)任務(wù)進(jìn)行了調(diào)度纪吮。StartWorkRunnable的run()的實(shí)現(xiàn):

//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()方法中創(chuàng)建了一個(gè)WorkerWrapper的Runnable對(duì)象俩檬,交由WorkTaskExecutor調(diào)度處理。WorkerWrapper的run()方法的實(shí)現(xiàn):

//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.
            ...
    }

這段代碼很長(zhǎng)碾盟,我們省略了一些判斷步驟和與示例無(wú)關(guān)的參數(shù)設(shè)置棚辽。
先創(chuàng)建一個(gè)WorkerParameters對(duì)象。
然后調(diào)用mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback()方法創(chuàng)建Worker對(duì)象冰肴。這個(gè)方法我們不展開(kāi)了屈藐,返回的就是我們自己的Woker對(duì)象,即Worker1的實(shí)例熙尉。之后交由WorkTaskExecutor調(diào)度處理联逻。
在run()方法的實(shí)現(xiàn),我們看到調(diào)用了mWorker.startWork()方法:

//ListenableWorker
    @MainThread
    public abstract @NonNull ListenableFuture<Result> startWork();

ListenableWorker是個(gè)抽象類(lèi)检痰,是所有Worker的父類(lèi)包归。Worker1也繼承Worker類(lèi),startWork()在Worker中的實(shí)現(xiàn):

//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()的實(shí)現(xiàn)執(zhí)行了doWork()方法铅歼,即執(zhí)行了我們Worker1的doWork()方法公壤。

//Worker1
class Worker1(appContext: Context, workerParams: WorkerParameters) :
    Worker(appContext, workerParams) {

    override fun doWork(): Result {
        Thread.sleep(5000)
        return Result.success()
    }
}

在執(zhí)行完這個(gè)任務(wù)后,返回了success椎椰。Worker也就執(zhí)行完成了厦幅。回到WorkerWrapper的runWorker()方法看接下來(lái)的處理:

//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()返回了一個(gè)Future對(duì)象mInnerFuture慨飘,調(diào)用future.setFuture(mInnerFuture)去處理doWork()返回的result确憨。再經(jīng)過(guò)一系列判斷后,最終執(zhí)行了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()會(huì)對(duì)剛剛執(zhí)行完畢的任務(wù)作進(jìn)一步處理瓤的。首先獲取任務(wù)的當(dāng)前狀態(tài)state休弃,然后從db中刪除這個(gè)任務(wù),再根據(jù)state作進(jìn)一步處理堤瘤。在我們的示例中玫芦,這時(shí)候state應(yīng)該是RUNNING,我們看一下handleResult(mResult)的實(shí)現(xiàn):

//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()方法中會(huì)根據(jù)任務(wù)類(lèi)型和result結(jié)果進(jìn)行不同的處理本辐。例如周期性的任務(wù)會(huì)重新將這個(gè)任務(wù)的狀態(tài)設(shè)置為ENQUEUED桥帆,更新其他相關(guān)參數(shù)医增,并更新數(shù)據(jù)庫(kù)。我們示例中已經(jīng)完成的一次性任務(wù)將會(huì)被更新成SUCCEEDED的狀態(tài)老虫,具體的處理的過(guò)程就不展開(kāi)了叶骨。handleResult()執(zhí)行完畢后更新isWorkFinished。如果isWorkFinished為true祈匙,由于我們?cè)贕reedyScheduler已經(jīng)處理了這個(gè)任務(wù)忽刽,為了避免這個(gè)任務(wù)被其他schedulers處理,WorkManager遍歷了mSchedulers列表夺欲,并將這個(gè)任務(wù)從其他schedulers中移除跪帝。最后再次執(zhí)行Schedulers.schedule()方法,schedule下一個(gè)任務(wù)些阅。

總結(jié)

  1. 在WorkManager執(zhí)行了enqueue()后伞剑,創(chuàng)建WorkContinuationImpl對(duì)象執(zhí)行enqueue()方法。
  2. WorkContinuationImpl持有的EnqueueRunnable對(duì)象將任務(wù)添加到db市埋,并交給Schedulers去調(diào)度黎泣。
  3. Schedulers將任務(wù)交給每一個(gè)Scheduler去處理。在我們的示例中缤谎,GreedyScheduler會(huì)先處理這個(gè)任務(wù)抒倚。
  4. GreedyScheduler經(jīng)過(guò)一系列判斷后,調(diào)用WorkManager的startWork()方法執(zhí)行這種一次性坷澡,非延遲托呕,無(wú)約束的任務(wù)。
  5. WorkManager持有的StartWorkRunnable對(duì)象會(huì)將任務(wù)交給Processor去處理洋访,執(zhí)行startWork()方法镣陕。
  6. Processor創(chuàng)建一個(gè)WorkerWrapper對(duì)象,由它去調(diào)用Worker的startWork()方法姻政,執(zhí)行我們自定義worker的任務(wù),并返回相應(yīng)的result岂嗓。
  7. 任務(wù)完成后汁展,WorkerWrapper會(huì)根據(jù)result對(duì)任務(wù)狀態(tài),db等進(jìn)行更新厌殉,然后schedule下一個(gè)任務(wù)食绿。

WorkManager任務(wù)執(zhí)行流程圖

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

4.3帶約束的任務(wù)的執(zhí)行:

創(chuàng)建一個(gè)非低電量才能執(zhí)行的任務(wù):

val constraints = Constraints.Builder()
                .setRequiresBatteryNotLow(true)
                .build()
val work2Request = OneTimeWorkRequestBuilder<Worker2>()
                .setConstraints(constraints)
                .build()
WorkManager.getInstance(this).enqueue(work2Request)

任務(wù)的創(chuàng)建過(guò)程中,會(huì)為WorkSpec添加Constraints屬性公罕。

public final @NonNull B setConstraints(@NonNull Constraints constraints) {
            mWorkSpec.constraints = constraints;
            return getThis();
        }

在任務(wù)執(zhí)行的過(guò)程中器紧,由于增加了約束條件,根據(jù)之前章節(jié)的分析楼眷,常駐的GreedyScheduler的schedule()方法將不會(huì)startWork()铲汪,而是根據(jù)build version交由SystemJobScheduler或SystemAlarmScheduler進(jìn)行處理熊尉。
先來(lái)看使用SystemJobScheduler的情況:

4.3.1.SystemJobScheduler

SystemJobScheduler使用的是JobScheduler來(lái)調(diào)度執(zhí)行任務(wù)。由于JobScheduler的實(shí)現(xiàn)過(guò)程分析不在本文的討論范圍掌腰,所以只看WorkManager是如何使用JobScheduler進(jìn)行任務(wù)調(diào)度的狰住。通常JobScheduler的使用步驟如下:

  1. 創(chuàng)建JobService矫夯。
  2. 配置JobInfo害捕。
  3. 執(zhí)行姐军。
    SystemJobService:
    SystemJobService是執(zhí)行任務(wù)的服務(wù)類(lèi)罗标,在onStartJob()中儒飒,會(huì)調(diào)用WorkManagerImpl的startWork()執(zhí)行任務(wù)鲫构。
//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的時(shí)候會(huì)獲取JobScheduler對(duì)象:

//SystemJobScheduler 
    public SystemJobScheduler(@NonNull Context context, @NonNull WorkManagerImpl workManager) {
        this(context,
                workManager,
                (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE),
                new SystemJobInfoConverter(context));
    }

SystemJobScheduler的schedule()方法執(zhí)行了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()方法就是創(chuàng)建了一個(gè)JobInfo巷屿,并將Constraints里的約束條件賦予JobInfo對(duì)象崇众,之后便執(zhí)行了JobScheduler.schedule()省核,根據(jù)約束條件對(duì)任務(wù)進(jìn)行調(diào)度稿辙。

4.3.2. SystemAlarmScheduler

SystemAlarmScheduler使用的是AlarmManager來(lái)調(diào)度執(zhí)行任務(wù)。由于AlarmManager的實(shí)現(xiàn)過(guò)程分析不在本文的討論范圍芳撒,所以只看WorkManager是如何使用AlarmManager進(jìn)行任務(wù)調(diào)度的邓深。反編譯apk后,在AndroidManifest里有如下receiver注冊(cè):

<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>

在電量變化時(shí)笔刹,在BatteryNotLowProxy的onReceive()進(jìn)行處理:

//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()的執(zhí)行如下:

//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;
    }

調(diào)用了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()方法中執(zhí)行了processCommand(),這段代碼的核心執(zhí)行語(yǔ)句是:

//SystemAlarmDispatcher
mCommandHandler.onHandleIntent(mCurrentIntent, startId,
                                    SystemAlarmDispatcher.this);

在CommandHandler的onHandleIntent()方法中舌菜,action為ACTION_CONSTRAINTS_CHANGED的執(zhí)行是:

//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()方法的執(zhí)行中萌壳,會(huì)創(chuàng)建一個(gè)action為ACTION_DELAY_MET的Intent然后由SystemAlarmDispatcher發(fā)送出去,實(shí)際上也是調(diào)用了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的執(zhí)行是:

//CommandHandler
else if (ACTION_DELAY_MET.equals(action)) {
                    handleDelayMet(intent, startId, dispatcher);
                } 

handleDelayMet()的執(zhí)行過(guò)程,會(huì)調(diào)用DelayMetCommandHandler的handleProcessWork()方法精拟,接著執(zhí)行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調(diào)用了Processor的startWork()方法燎斩,回到了之前章節(jié)分析的任務(wù)執(zhí)行流程。
到此為止蜂绎,一個(gè)任務(wù)在不同條件下的創(chuàng)建栅表,執(zhí)行流程就分析完畢。

5.結(jié)語(yǔ)

WorkManager的使用方法簡(jiǎn)單师枣,但是在使用時(shí)還是要分清場(chǎng)景怪瓶。通過(guò)對(duì)源碼的分析,WorkManager會(huì)針對(duì)不同Android版本的選擇適當(dāng)?shù)牟呗约馈<?xì)致閱讀代碼洗贰,會(huì)發(fā)現(xiàn)針對(duì)指定的系統(tǒng)版本還有一些小的優(yōu)化點(diǎn)找岖。WorkManager目前比較穩(wěn)定,所以如果在場(chǎng)景適合的情況下哆姻,推薦使用WorkManager來(lái)代替原有的任務(wù)管理方案宣增。

6.參考文獻(xiàn)

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市矛缨,隨后出現(xiàn)的幾起案子爹脾,更是在濱河造成了極大的恐慌,老刑警劉巖箕昭,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件灵妨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡落竹,警方通過(guò)查閱死者的電腦和手機(jī)泌霍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)述召,“玉大人朱转,你說(shuō)我怎么就攤上這事』” “怎么了藤为?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)夺刑。 經(jīng)常有香客問(wèn)我缅疟,道長(zhǎng),這世上最難降的妖魔是什么遍愿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任存淫,我火速辦了婚禮,結(jié)果婚禮上沼填,老公的妹妹穿的比我還像新娘桅咆。我一直安慰自己,他們只是感情好坞笙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布轧邪。 她就那樣靜靜地躺著,像睡著了一般羞海。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上曲管,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天却邓,我揣著相機(jī)與錄音,去河邊找鬼院水。 笑死腊徙,一個(gè)胖子當(dāng)著我的面吹牛简十,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播撬腾,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼螟蝙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了民傻?” 一聲冷哼從身側(cè)響起胰默,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎漓踢,沒(méi)想到半個(gè)月后牵署,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喧半,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年奴迅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挺据。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡取具,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出扁耐,到底是詐尸還是另有隱情暇检,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布做葵,位于F島的核電站占哟,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏酿矢。R本人自食惡果不足惜榨乎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瘫筐。 院中可真熱鬧蜜暑,春花似錦、人聲如沸策肝。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)之众。三九已至拙毫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間棺禾,已是汗流浹背缀蹄。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缺前。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓蛀醉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親衅码。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拯刁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容