今天想和大家分享的是那些你可能還不知道,或者是還不清楚的WorkManager流程分析和源碼解析。希望本文能對你的學習和工作有所幫助。
原文地址:https://mp.weixin.qq.com/s/KlzYHqd3RhF1D8dDAYiSjA
Android架構系統學習資料:https://github.com/xieyuliang/Android-share/
01 前言
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的實現。
02 WorkManager的特點與適用場景
特點:
- 保證任務一定會被執行
WorkManager有自己的數據庫,每一個任務的信息與任務狀態,都會保存在本地數據庫中。所以即使程序沒有在運行,或者在設備重啓等情況下,WorkManager依然可以保證任務的執行,只是不保證任務立即被執行。 - 合理使用設備資源
在執行很多週期性或非立即執行的任務時,WorkManager提供我們API,幫助我們合理利用設備資源,避免不必要的內存,流量,電量等消耗。
適用場景:
- 可延遲進行的任務
a.滿足某些條件才執行的任務,如需要在充電時才執行的任務。
b.用戶無感知或可延遲感知的任務,如同步配置信息,同步資源,同步通訊錄等。 - 定期重複性任務,但時效性要求不高的,如定期log上傳,數據備份等。
- 退出應用後還應繼續執行的未完成任務。
03 WorkManager的使用WorkManager的使用非常簡單,分爲如下幾個步驟:
- 創建一個後臺任務Worker。
- 定義WorkRequest,配置運行任務的方式和時間。
- 將任務提交給系統處理。
- 觀察Worker的進度或狀態。
3.1創建後臺任務Worker
WorkManager提供了四種Worker的創建模式:
- Worker:最簡單的實現,WorkManager 會在後臺線程上自動運行它。
- CoroutineWorker:CoroutineWorker針對後臺工作公開掛起函數。默認情況下,它們運行默認的Dispatcher。
- RxWorker:如果有很多現有異步代碼是用 RxJava 建模的建議使用。與所有 RxJava2 概念一樣,可以自由選擇所需的線程處理策略。
- 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 ->
})
04 WorkManager流程分析與源碼解析
這個章節將會從以下幾個方面梳理WorkManager的流程與源碼:
- 創建
a.WorkManager的初始化
b.WorkRequest的創建 - 非約束條件任務的執行
- 帶約束條件任務的執行
從最基礎的流程開始分析:創建一個不帶任何約束條件的一次性任務。在doWork()中讓線程休息5s。
val work1Request = OneTimeWorkRequestBuilder<Worker1>().build()
WorkManager.getInstance(this).enqueue(work1Request)
class Worker1(appContext: Context, workerParams: WorkerParameters) :
Worker(appContext, workerParams) {
override fun doWork(): Result {
Thread.sleep(5000)
return Result.success()
}
}
4.1創建
首先梳理一下WorkManager的初始化過程。
4.1.1. WorkManager的初始化
在默認的情況下,WorkManager並不是在我們調用WorkManager.getInstance() 時創建的。通過反編譯一下apk,會發現在AndroidManifest文件中註冊了名爲WorkManagerInitializer的ContentProvider。因此WorkManager在app冷啓動的時候已經被創建。
//AndroidManifest.xml
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:exported="false"
android:multiprocess="true"
android:authorities="com.jandroid.multivideo.workmanager-init"
android:directBootAware="false" />
WorkManagerInitializer的onCreate()方法:
//WorkManagerInitializer
public boolean onCreate() {
// Initialize WorkManager with the default configuration.
WorkManager.initialize(getContext(), new Configuration.Builder().build());
return true;
}
由於WorkManager是個單例,在此時WorkManager就已經被初始化了。在initialize()之前,會創建一個默認的Configuration。Configuration設置了許多屬性,用來管理和調度工作的方式。通常我們使用WorkManager默認創建的Configuration即可。如需使用自己的Configuration,可參考官方文檔,有明確的使用說明。我們繼續看initialize()的實現,由於WorkManager是個抽象類,真正的構造方法是在他的子類WorkManagerImpl實現的:
//WorkManagerImpl
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
synchronized (sLock) {
if (sDelegatedInstance != null && sDefaultInstance != null) {
throw new IllegalStateException("WorkManager is already initialized. Did you "
+ "try to initialize it manually without disabling "
+ "WorkManagerInitializer? See "
+ "WorkManager#initialize(Context, Configuration) or the class level "
+ "Javadoc for more information.");
}
if (sDelegatedInstance == null) {
context = context.getApplicationContext();
if (sDefaultInstance == null) {
sDefaultInstance = new WorkManagerImpl(
context,
configuration,
new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
}
sDelegatedInstance = sDefaultInstance;
}
}
}
此時sDelegatedInstance爲null,WorkManager會先創建一個默認的WorkManagerTaskExecutor對象,用來執行WorkManager的任務。之後創建一個WorkManagerImpl對象:
//WorkManagerImpl
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public WorkManagerImpl(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor) {
this(context,
configuration,
workTaskExecutor,
context.getResources().getBoolean(R.bool.workmanager_test_configuration));
}
//WorkManagerImpl
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public WorkManagerImpl(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
boolean useTestDatabase) {
this(context,
configuration,
workTaskExecutor,
WorkDatabase.create(
context.getApplicationContext(),
workTaskExecutor.getBackgroundExecutor(),
useTestDatabase)
);
}
WorkManager在此時創建了數據庫。WorkDatabase.create()將任務列表序列化到本地,記錄每一個任務的屬性,執行條件,執行順序及執行狀態等。從而保證任務在冷啓動或硬件重啓後,可以根據條件繼續執行。接着看this()的實現:
//WorkManagerImpl
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public WorkManagerImpl(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
@NonNull WorkDatabase database) {
Context applicationContext = context.getApplicationContext();
Logger.setLogger(new Logger.LogcatLogger(configuration.getMinimumLoggingLevel()));
List<Scheduler> schedulers = createSchedulers(applicationContext, workTaskExecutor);
Processor processor = new Processor(
context,
configuration,
workTaskExecutor,
database,
schedulers);
internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
}
到這裏有三個重要的初始化步驟。分別是createSchedulers()來根據Build Version創建不同的Schedulers進行任務調度,Processor()用來管理Schedulers的執行,和internalInit()真正的初始化。先看createSchedulers()的實現:
//WorkManagerImpl
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@NonNull
public List<Scheduler> createSchedulers(
@NonNull Context context,
@NonNull TaskExecutor taskExecutor) {
return Arrays.asList(
Schedulers.createBestAvailableBackgroundScheduler(context, this),
// Specify the task executor directly here as this happens before internalInit.
// GreedyScheduler creates ConstraintTrackers and controllers eagerly.
new GreedyScheduler(context, taskExecutor, this));
}
return一個Scheduler數組。其中GreedyScheduler()是常駐的,用來執行沒有任何約束的非週期性的任務。接下來看createBestAvailableBackgroundScheduler()的實現。
//Scheduler
@NonNull
static Scheduler createBestAvailableBackgroundScheduler(
@NonNull Context context,
@NonNull WorkManagerImpl workManager) {
Scheduler scheduler;
if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
scheduler = new SystemJobScheduler(context, workManager);
setComponentEnabled(context, SystemJobService.class, true);
Logger.get().debug(TAG, "Created SystemJobScheduler and enabled SystemJobService");
} else {
scheduler = tryCreateGcmBasedScheduler(context);
if (scheduler == null) {
scheduler = new SystemAlarmScheduler(context);
setComponentEnabled(context, SystemAlarmService.class, true);
Logger.get().debug(TAG, "Created SystemAlarmScheduler");
}
}
return scheduler;
}
這段代碼對build version進行了判斷。若>=23,則返回SystemJobScheduler(),即利用JobScheduler進行任務管理。<23的時候先嚐試使用GcmScheduler進行管理。若無法創建GcmScheduler則返回SystemAlarmScheduler()使用AlamManager進行任務管理。返回的這個Scheduler是用來執行週期性,或者有約束性的任務。由此可見,WorkManager創建了兩個Scheduler,分別爲執行非約束非週期性任務的GreedyScheduler,和執行約束性週期性任務的SystemJobScheduler/GcmBasedScheduler/SystemAlarmScheduler。
這幾種Scheduler的構造和執行之後再分析。
之後初始化Processor。Processor存儲了Configuration,TaskExecutor,WorkDatabase,schedulers等,用來在適當的時機進行任務調度。再來看internalInit():
//WorkManagerImpl
private void internalInit(@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
@NonNull WorkDatabase workDatabase,
@NonNull List<Scheduler> schedulers,
@NonNull Processor processor) {
context = context.getApplicationContext();
mContext = context;
mConfiguration = configuration;
mWorkTaskExecutor = workTaskExecutor;
mWorkDatabase = workDatabase;
mSchedulers = schedulers;
mProcessor = processor;
mPreferenceUtils = new PreferenceUtils(workDatabase);
mForceStopRunnableCompleted = false;
// Checks for app force stops.
mWorkTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));
}
記錄了Configuration,TaskExecutor,WorkDatabase,schedulers,Processor等。然後我們看最後一行執行語句,啓動了一個ForceStopRunnable,這個Runnable是幹什麼用的呢?直接看run()的實現:
//ForceStopRunnable
@Override
public void run() {
// Migrate the database to the no-backup directory if necessary.
WorkDatabasePathHelper.migrateDatabase(mContext);
// Clean invalid jobs attributed to WorkManager, and Workers that might have been
// interrupted because the application crashed (RUNNING state).
Logger.get().debug(TAG, "Performing cleanup operations.");
try {
boolean needsScheduling = cleanUp();
if (shouldRescheduleWorkers()) {
Logger.get().debug(TAG, "Rescheduling Workers.");
mWorkManager.rescheduleEligibleWork();
// Mark the jobs as migrated.
mWorkManager.getPreferenceUtils().setNeedsReschedule(false);
} else if (isForceStopped()) {
Logger.get().debug(TAG, "Application was force-stopped, rescheduling.");
mWorkManager.rescheduleEligibleWork();
} else if (needsScheduling) {
Logger.get().debug(TAG, "Found unfinished work, scheduling it.");
Schedulers.schedule(
mWorkManager.getConfiguration(),
mWorkManager.getWorkDatabase(),
mWorkManager.getSchedulers());
}
mWorkManager.onForceStopRunnableCompleted();
} catch (SQLiteCantOpenDatabaseException
| SQLiteDatabaseCorruptException
| SQLiteAccessPermException exception) {
// ForceStopRunnable is usually the first thing that accesses a database (or an app's
// internal data directory). This means that weird PackageManager bugs are attributed
// to ForceStopRunnable, which is unfortunate. This gives the developer a better error
// message.
String message =
"The file system on the device is in a bad state. WorkManager cannot access "
+ "the app's internal data store.";
Logger.get().error(TAG, message, exception);
throw new IllegalStateException(message, exception);
}
}
這段代碼的實現細節先不做深究。但是很明顯,這個Runnable的作用就是在WorkManager初始化過程中,發現了未完成的,需要重新執行的任務,或者app被強制kill的情況下,直接對Scheduler進行調度。到此,一個WorkManager的初始化流程就完成了。
總結
- WorkManager的初始化是在app冷啓動後,由WorkManagerInitializer這個ContentProvider執行的。
- 初始化過程包含了Configuration,WorkManagerTaskExecutor,WorkDatabase,Schedulers,Processor等的初始化過程。
- Schedulers有兩個。
(1) GreedyScheduler:執行沒有任何約束的非週期性的任務。
(2) SystemJobScheduler/GcmBasedScheduler/SystemAlarmScheduler:執行週期性或者有約束性的任務。優先返回SystemJobScheduler,在build version小於23的情況下先嚐試返回GcmBasedScheduler,若返回爲空再返回SystemAlarmScheduler。 - 初始化的最後,會根據情況找到需要被執行的任務進行調度執行。
WorkManager的初始化流程圖:
4.1.2.WorkRequest的創建
梳理完WorkManager的初始化過程後,我們回到示例代碼,創建一個OneTimeWorkRequest
val work1Request = OneTimeWorkRequestBuilder<Worker1>().build()
//OneTimeWorkRequest.Builder
/**
* Creates a {@link OneTimeWorkRequest}.
*
* @param workerClass The {@link ListenableWorker} class to run for this work
*/
public Builder(@NonNull Class<? extends ListenableWorker> workerClass) {
super(workerClass);
mWorkSpec.inputMergerClassName = OverwritingInputMerger.class.getName();
}
//WorkRequest.Builder
Builder(@NonNull Class<? extends ListenableWorker> workerClass) {
mId = UUID.randomUUID();
mWorkerClass = workerClass;
mWorkSpec = new WorkSpec(mId.toString(), workerClass.getName());
addTag(workerClass.getName());
}
OneTimeWorkRequest爲builder對象創建了WorkSpec對象用來保存任務id和類名,其中id是通過UUID自動生成的。request的tag默認是通過類名生成的,外部也可調用addTag()方法設置標籤。另外爲WorkSpec設置了默認的任務輸入流的合併規則:OverwritingInputMerger。接着看build()方法的實現:
//WorkRequest.Builder
public final @NonNull W build() {
W returnValue = buildInternal();
// Create a new id and WorkSpec so this WorkRequest.Builder can be used multiple times.
mId = UUID.randomUUID();
mWorkSpec = new WorkSpec(mWorkSpec);
mWorkSpec.id = mId.toString();
return returnValue;
}
buildInternal()方法返回了一個WorkRequest對象,這是個抽象方法,在子類OneTimeWorkRequest.Builder中的實現如下:
//OneTimeWorkRequest.Builder
@Override
@NonNull OneTimeWorkRequest buildInternal() {
if (mBackoffCriteriaSet
&& Build.VERSION.SDK_INT >= 23
&& mWorkSpec.constraints.requiresDeviceIdle()) {
throw new IllegalArgumentException(
"Cannot set backoff criteria on an idle mode job");
}
if (mWorkSpec.runInForeground
&& Build.VERSION.SDK_INT >= 23
&& mWorkSpec.constraints.requiresDeviceIdle()) {
throw new IllegalArgumentException(
"Cannot run in foreground with an idle mode constraint");
}
return new OneTimeWorkRequest(this);
}
由於我們沒有爲WorkSpec設置其他屬性,目前也沒有約束條件,所以直接返回一個OneTimeWorkRequest對象。
//OneTimeWorkRequest
OneTimeWorkRequest(Builder builder) {
super(builder.mId, builder.mWorkSpec, builder.mTags);
}
把Builder的id, WorkSpec對象和tag賦給OneTimeWorkRequest對象。再回到Builder的build()方法:
//OneTimeWorkRequest.Builder
public final @NonNull W build() {
W returnValue = buildInternal();
// Create a new id and WorkSpec so this WorkRequest.Builder can be used multiple times.
mId = UUID.randomUUID();
mWorkSpec = new WorkSpec(mWorkSpec);
mWorkSpec.id = mId.toString();
return returnValue;
}
在buildInternal()拿到OneTimeWorkRequest對象之後,爲Builder創建了一個新的WorkSpec對象,並賦予了新的UUID。雖然與原先的WorkSpec對象中每個屬性的值是一致的,但指向了不同的內存地址。這麼做的目的是爲了這個Builder對象可被重複利用。好了,現在我們一個任務的WorkRequest創建就完成了。
總結
WorkRequest的創建是爲了持有三個重要的成員變量。分別是:
- mId:由UUID生成的任務id。
- mWorkSpec:每個任務的屬性。
- mTags:每個任務的標籤。
WorkRequest創建的流程圖
4.2非約束條件任務的執行過程
執行OneTimeWorkRequest
WorkManager.getInstance(this).enqueue(work1Request)
根據第一節的分析,WorkManager是個單例,在app啓動的時候就已經被初始化了。所以直接看enqueue()的實現:
//WorkManager
@NonNull
public final Operation enqueue(@NonNull WorkRequest workRequest) {
return enqueue(Collections.singletonList(workRequest));
}
//WorkManager
@NonNull
public abstract Operation enqueue(@NonNull List<? extends WorkRequest> requests);
//WorkManagerImpl
@NonNull
public Operation enqueue(
@NonNull List<? extends WorkRequest> workRequests) {
// This error is not being propagated as part of the Operation, as we want the
// app to crash during development. Having no workRequests is always a developer error.
if (workRequests.isEmpty()) {
throw new IllegalArgumentException(
"enqueue needs at least one WorkRequest.");
}
return new WorkContinuationImpl(this, workRequests).enqueue();
}
創建一個WorkContinuationImpl()對象,再執行enqueue()方法。WorkContinuationImpl是WorkContinuation的子類。用來把多個OneTimeWorkRequest根據需求串行,並行或合併處理。我們熟悉的then(),combine(),enqueue()等都是這個類的方法。
//WorkContinuationImpl
WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl,
String name,
ExistingWorkPolicy existingWorkPolicy,
@NonNull List<? extends WorkRequest> work,
@Nullable List<WorkContinuationImpl> parents) {
mWorkManagerImpl = workManagerImpl;
mName = name;
mExistingWorkPolicy = existingWorkPolicy;
mWork = work;
mParents = parents;
mIds = new ArrayList<>(mWork.size());
mAllIds = new ArrayList<>();
if (parents != null) {
for (WorkContinuationImpl parent : parents) {
mAllIds.addAll(parent.mAllIds);
}
}
for (int i = 0; i < work.size(); i++) {
String id = work.get(i).getStringId();
mIds.add(id);
mAllIds.add(id);
}
}
WorkContinuation保存了任務相關的所有信息,如WorkManager,WorkRequest,父WorkContinuation等。繼續看WorkContinuationImpl的enqueue()方法的實現:
//WorkContinuationImpl
@Override
public @NonNull Operation enqueue() {
// Only enqueue if not already enqueued.
if (!mEnqueued) {
// The runnable walks the hierarchy of the continuations
// and marks them enqueued using the markEnqueued() method, parent first.
EnqueueRunnable runnable = new EnqueueRunnable(this);
mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);
mOperation = runnable.getOperation();
} else {
Logger.get().warning(TAG,
String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
}
return mOperation;
}
WorkManager的TaskExecutor執行了EnqueueRunnable。EnqueueRunnable中run()的實現:
//EnqueueRunnable
@Override
public void run() {
try {
if (mWorkContinuation.hasCycles()) {
throw new IllegalStateException(
String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
}
boolean needsScheduling = addToDatabase();
if (needsScheduling) {
// Enable RescheduleReceiver, only when there are Worker's that need scheduling.
final Context context =
mWorkContinuation.getWorkManagerImpl().getApplicationContext();
PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true);
scheduleWorkInBackground();
}
mOperation.setState(Operation.SUCCESS);
} catch (Throwable exception) {
mOperation.setState(new Operation.State.FAILURE(exception));
}
}
addToDatabase()的作用是把WorkSpec存入到數據庫,並對任務的狀態進行校驗。當前的case會返回true。PackageManagerHelper.setComponentEnabled()開啓了RescheduleReceiver。通過反編譯我們得知這個Receiver是在AndroidManifest中註冊的,默認是disable的。監聽了開機,時間變化,時區變化這三個廣播。
//AndroidManifest
<receiver android:directBootAware="false" android:enabled="false" android:exported="false" android:name="androidx.work.impl.background.systemalarm.RescheduleReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.TIME_SET"/>
<action android:name="android.intent.action.TIMEZONE_CHANGED"/>
</intent-filter>
</receiver>
scheduleWorkInBackground()的實現:
//EnqueueRunnable
/**
* Schedules work on the background scheduler.
*/
@VisibleForTesting
public void scheduleWorkInBackground() {
WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
Schedulers.schedule(
workManager.getConfiguration(),
workManager.getWorkDatabase(),
workManager.getSchedulers());
}
這部分就是任務調度的實現。拿到WorkManager對象後調用了Schedulers.schedule()方法,傳入了Configuration, WorkDatabase, Scheduler這三個對象。執行schedule()方法:
//Schedulers
public static void schedule(
@NonNull Configuration configuration,
@NonNull WorkDatabase workDatabase,
List<Scheduler> schedulers) {
if (schedulers == null || schedulers.size() == 0) {
return;
}
WorkSpecDao workSpecDao = workDatabase.workSpecDao();
List<WorkSpec> eligibleWorkSpecs;
workDatabase.beginTransaction();
try {
eligibleWorkSpecs = workSpecDao.getEligibleWorkForScheduling(
configuration.getMaxSchedulerLimit());
if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
long now = System.currentTimeMillis();
// Mark all the WorkSpecs as scheduled.
// Calls to Scheduler#schedule() could potentially result in more schedules
// on a separate thread. Therefore, this needs to be done first.
for (WorkSpec workSpec : eligibleWorkSpecs) {
workSpecDao.markWorkSpecScheduled(workSpec.id, now);
}
}
workDatabase.setTransactionSuccessful();
} finally {
workDatabase.endTransaction();
}
if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);
// Delegate to the underlying scheduler.
for (Scheduler scheduler : schedulers) {
scheduler.schedule(eligibleWorkSpecsArray);
}
}
}
先進行了一系列的數據庫操作,然後開始根據條件每個任務進行調度。其中eligibleWorkSpecs返回的是在ENQUEUED狀態下,未被執行且未被取消的WorkSpec列表,然後更新這些任務的request狀態到數據庫。最後遍歷schedulers調用scheduler.schedule()對每個任務進行調度處理。由於示例代碼創建的是沒有約束的一次性任務,所以看一下GreedyScheduler對於schedule()方法的實現:
//GreedyScheduler
@Override
public void schedule(@NonNull WorkSpec... workSpecs) {
if (mIsMainProcess == null) {
// The default process name is the package name.
mIsMainProcess = TextUtils.equals(mContext.getPackageName(), getProcessName());
}
if (!mIsMainProcess) {
Logger.get().info(TAG, "Ignoring schedule request in non-main process");
return;
}
registerExecutionListenerIfNeeded();
// Keep track of the list of new WorkSpecs whose constraints need to be tracked.
// Add them to the known list of constrained WorkSpecs and call replace() on
// WorkConstraintsTracker. That way we only need to synchronize on the part where we
// are updating mConstrainedWorkSpecs.
List<WorkSpec> constrainedWorkSpecs = new ArrayList<>();
List<String> constrainedWorkSpecIds = new ArrayList<>();
for (WorkSpec workSpec : workSpecs) {
if (workSpec.state == WorkInfo.State.ENQUEUED
&& !workSpec.isPeriodic()
&& workSpec.initialDelay == 0L
&& !workSpec.isBackedOff()) {
if (workSpec.hasConstraints()) {
if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {
// Ignore requests that have an idle mode constraint.
Logger.get().debug(TAG,
String.format("Ignoring WorkSpec %s, Requires device idle.",
workSpec));
} else if (SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) {
// Ignore requests that have content uri triggers.
Logger.get().debug(TAG,
String.format("Ignoring WorkSpec %s, Requires ContentUri triggers.",
workSpec));
} else {
constrainedWorkSpecs.add(workSpec);
constrainedWorkSpecIds.add(workSpec.id);
}
} else {
Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id));
mWorkManagerImpl.startWork(workSpec.id);
}
}
}
// onExecuted() which is called on the main thread also modifies the list of mConstrained
// WorkSpecs. Therefore we need to lock here.
synchronized (mLock) {
if (!constrainedWorkSpecs.isEmpty()) {
Logger.get().debug(TAG, String.format("Starting tracking for [%s]",
TextUtils.join(",", constrainedWorkSpecIds)));
mConstrainedWorkSpecs.addAll(constrainedWorkSpecs);
mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);
}
}
}
在: (1) WorkSpec是ENQUEUED的狀態 (2) 非週期性任務 (3) 非延遲任務 (4) 非撤銷的任務 (5) 沒有其它約束的任務 滿足這五個條件後,直接調用:
//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);
}
StartWorkRunnable會將任務的信息交給Processor,由Processor調用startWork()去執行任務:
//Processor
/**
* Starts a given unit of work in the background.
*
* @param id The work id to execute.
* @param runtimeExtras The {@link WorkerParameters.RuntimeExtras} for this work, if any.
* @return {@code true} if the work was successfully enqueued for processing
*/
public boolean startWork(
@NonNull String id,
@Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
WorkerWrapper workWrapper;
synchronized (mLock) {
// Work may get triggered multiple times if they have passing constraints
// and new work with those constraints are added.
if (mEnqueuedWorkMap.containsKey(id)) {
Logger.get().debug(
TAG,
String.format("Work %s is already enqueued for processing", id));
return false;
}
workWrapper =
new WorkerWrapper.Builder(
mAppContext,
mConfiguration,
mWorkTaskExecutor,
this,
mWorkDatabase,
id)
.withSchedulers(mSchedulers)
.withRuntimeExtras(runtimeExtras)
.build();
ListenableFuture<Boolean> future = workWrapper.getFuture();
future.addListener(
new FutureListener(this, id, future),
mWorkTaskExecutor.getMainThreadExecutor());
mEnqueuedWorkMap.put(id, workWrapper);
}
mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
return true;
}
startWork()方法中創建了一個WorkerWrapper的Runnable對象,交由WorkTaskExecutor調度處理。WorkerWrapper的run()方法的實現:
//WorkerWrapper
@WorkerThread
@Override
public void run() {
mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
mWorkDescription = createWorkDescription(mTags);
runWorker();
}
//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下一個任務。
總結
- 在WorkManager執行了enqueue()後,創建WorkContinuationImpl對象執行enqueue()方法。
- WorkContinuationImpl持有的EnqueueRunnable對象將任務添加到db,並交給Schedulers去調度。
- Schedulers將任務交給每一個Scheduler去處理。在我們的示例中,GreedyScheduler會先處理這個任務。
- GreedyScheduler經過一系列判斷後,調用WorkManager的startWork()方法執行這種一次性,非延遲,無約束的任務。
- WorkManager持有的StartWorkRunnable對象會將任務交給Processor去處理,執行startWork()方法。
- Processor創建一個WorkerWrapper對象,由它去調用Worker的startWork()方法,執行我們自定義worker的任務,並返回相應的result。
- 任務完成後,WorkerWrapper會根據result對任務狀態,db等進行更新,然後schedule下一個任務。
WorkManager任務執行流程圖:
4.3帶約束的任務的執行過程
上面的章節我們分析了一個沒有約束條件的一次性任務的執行過程。接下來我們來分析一個帶約束條件的任務的執行過程。
創建一個非低電量才能執行的任務:
val constraints = Constraints.Builder()
.setRequiresBatteryNotLow(true)
.build()
val work2Request = OneTimeWorkRequestBuilder<Worker2>()
.setConstraints(constraints)
.build()
WorkManager.getInstance(this).enqueue(work2Request)
任務的創建過程中,會爲WorkSpec添加Constraints屬性。
public final @NonNull B setConstraints(@NonNull Constraints constraints) {
mWorkSpec.constraints = constraints;
return getThis();
}
在任務執行的過程中,由於增加了約束條件,根據之前章節的分析,常駐的GreedyScheduler的schedule()方法將不會startWork(),而是根據build version交由SystemJobScheduler或SystemAlarmScheduler進行處理。先來看使用SystemJobScheduler的情況:
4.3.1.SystemJobScheduler(Build Version >=23)
SystemJobScheduler使用的是JobScheduler來調度執行任務。由於JobScheduler的實現過程分析不在本文的討論範圍,所以只看WorkManager是如何使用JobScheduler進行任務調度的。通常JobScheduler的使用步驟如下:
- 創建JobService。
- 配置JobInfo。
- 執行。
SystemJobService: SystemJobService是執行任務的服務類,在onStartJob()中,會調用WorkManagerImpl的startWork()執行任務。
//SystemJobService
@Override
public boolean onStartJob(@NonNull JobParameters params) {
... ...
String workSpecId = getWorkSpecIdFromJobParameters(params);
... ...
synchronized (mJobParameters) {
... ...
mJobParameters.put(workSpecId, params);
}
... ...
mWorkManagerImpl.startWork(workSpecId, runtimeExtras);
return true;
}
SystemJobScheduler: 在初始化SystemJobScheduler的時候會獲取JobScheduler對象:
//SystemJobScheduler
public SystemJobScheduler(@NonNull Context context, @NonNull WorkManagerImpl workManager) {
this(context,
workManager,
(JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE),
new SystemJobInfoConverter(context));
}
SystemJobScheduler的schedule()方法執行了scheduleInternal():
//SystemJobScheduler
public void scheduleInternal(WorkSpec workSpec, int jobId) {
JobInfo jobInfo = mSystemJobInfoConverter.convert(workSpec, jobId);
Logger.get().debug(
TAG,
String.format("Scheduling work ID %s Job ID %s", workSpec.id, jobId));
try {
mJobScheduler.schedule(jobInfo);
} catch (IllegalStateException e) {
... ...
throw new IllegalStateException(message, e);
} catch (Throwable throwable) {
// OEM implementation bugs in JobScheduler cause the app to crash. Avoid crashing.
Logger.get().error(TAG, String.format("Unable to schedule %s", workSpec), throwable);
}
}
SystemJobInfoConverter.convert()方法就是創建了一個JobInfo,並將Constraints裏的約束條件賦予JobInfo對象,之後便執行了JobScheduler.schedule(),根據約束條件對任務進行調度。
4.3.2. SystemAlarmScheduler(Build Version <23)
SystemAlarmScheduler使用的是AlarmManager來調度執行任務。由於AlarmManager的實現過程分析不在本文的討論範圍,所以只看WorkManager是如何使用AlarmManager進行任務調度的。反編譯apk後,在AndroidManifest裏有如下receiver註冊:
<receiver android:directBootAware="false" android:enabled="false" android:exported="false" android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy">
<intent-filter>
<action android:name="android.intent.action.BATTERY_OKAY"/>
<action android:name="android.intent.action.BATTERY_LOW"/>
</intent-filter>
</receiver>
在電量變化時,收到BATTERY_LOW的廣播。在BatteryNotLowProxy的onReceive()進行處理:
//ConstraintProxy
public static class BatteryNotLowProxy extends ConstraintProxy {
}
@Override
public void onReceive(Context context, Intent intent) {
Logger.get().debug(TAG, String.format("onReceive : %s", intent));
Intent constraintChangedIntent = CommandHandler.createConstraintsChangedIntent(context);
context.startService(constraintChangedIntent);
}
createConstraintsChangedIntent()的執行如下:
//ConstraintProxy
static Intent createConstraintsChangedIntent(@NonNull Context context) {
Intent intent = new Intent(context, SystemAlarmService.class);
intent.setAction(ACTION_CONSTRAINTS_CHANGED);
return intent;
}
SystemAlarmService的onStartCommand()處理如下:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
... ...
if (intent != null) {
mDispatcher.add(intent, startId);
}
// If the service were to crash, we want all unacknowledged Intents to get redelivered.
return Service.START_REDELIVER_INTENT;
}
調用了SystemAlarmDispatcher.add()方法。
//SystemAlarmDispatcher
@MainThread
public boolean add(@NonNull final Intent intent, final int startId) {
... ...
if (CommandHandler.ACTION_CONSTRAINTS_CHANGED.equals(action)
&& hasIntentWithAction(CommandHandler.ACTION_CONSTRAINTS_CHANGED)) {
return false;
}
intent.putExtra(KEY_START_ID, startId);
synchronized (mIntents) {
boolean hasCommands = !mIntents.isEmpty();
mIntents.add(intent);
if (!hasCommands) {
// Only call processCommand if this is the first command.
// The call to dequeueAndCheckForCompletion will process the remaining commands
// in the order that they were added.
processCommand();
}
}
return true;
}
add()方法中執行了processCommand(),這段代碼的核心執行語句是:
//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()方法,回到了之前章節分析的任務執行流程。到此爲止,一個任務在不同條件下的創建,執行流程就分析完畢。
05 結語
WorkManager的使用方法簡單,但是在使用時還是要分清場景,適用於可延遲,週期性,必須執行完成的任務。通過對源碼的分析,WorkManager會針對不同Android版本的選擇適當的策略。細緻閱讀代碼,會發現針對指定的系統版本還有一些小的優化點。WorkManager目前已經比較穩定,所以如果在場景適合的情況下,推薦使用WorkManager來代替原有的任務管理方案。
06 參考文獻
[1]https://developer.android.google.cn/topic/libraries/architecture/workmanager?
[2]https://developer.android.google.cn/preview/behavior-changes-11
07 最後
千里之行始於足下。Android學習是一條漫長的道路,我們要學習的東西不僅僅只有表面的 技術,還要深入底層,弄明白下面的 原理,只有這樣,我們才能夠提高自己的競爭力,在當今這個競爭激烈的世界裏立足。
我把自己這段時間整理的Android最重要最熱門的學習方向資料放在了我的GitHub:https://github.com/xieyuliang/Android,裏面還有不同方向的自學編程路線、面試題集合/面經、及系列技術文章等。
資源持續更新中,歡迎大家一起學習和探討。