Android WorkManager使用以及源碼分析
1、前言
WorkManager 是適合用于持久性工作的推薦解決方案。如果工作始終要通過應(yīng)用重啟和系統(tǒng)重新啟動來調(diào)度,便是持久性的工作。由于大多數(shù)后臺處理操作都是通過持久性工作完成的,因此 WorkManager 是適用于后臺處理操作的主要推薦 API。 WorkManager 可處理三種類型的持久性工作:
- 立即執(zhí)行:必須立即開始且很快就完成的任務(wù),可以加急。
- 長時間運行:運行時間可能較長(有可能超過 10 分鐘)的任務(wù)。
- 可延期執(zhí)行:延期開始并且可以定期運行的預(yù)定任務(wù)。
2、使用
2.1、引用
implementation "androidx.work:work-runtime:2.7.1" //基礎(chǔ)使用 implementation "androidx.work:work-multiprocess:2.7.1" //跨進程時引用
2.2 使用
執(zhí)行一次性任務(wù)
Data data = new Data.Builder().putBoolean("is_test", false).build(); WorkManager.getInstance(this).enqueue( new OneTimeWorkRequest.Builder(Test.class) // 執(zhí)行任務(wù)一次性任務(wù) .setInputMerger(NewInputMerge.class) // 輸入數(shù)據(jù)合并策略,這里并沒有用,鏈式處理時,多個上流執(zhí)行結(jié)果合并,作為下流輸入數(shù)據(jù) .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 5, TimeUnit.MINUTES) // 重試策略 .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) // 加急處理 .addTag("test").addTag("huahua") // 標識 .setInputData(data) // 輸入數(shù)據(jù) .setInitialDelay(5, TimeUnit.SECONDS) // 執(zhí)行延時時間 .setConstraints(new Constraints.Builder().setRequiresBatteryNotLow(true).build()) // 約束,部分約束只對高版本有效 .build() );
執(zhí)行周期性任務(wù)
WorkManager.getInstance(this).enqueue( new PeriodicWorkRequest.Builder(Test.class, 2, TimeUnit.HOURS) // 執(zhí)行周期性任務(wù),周期2小時 .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 5, TimeUnit.MINUTES) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .addTag("test").addTag("huahua") .setInputData(data) .setInitialDelay(5, TimeUnit.SECONDS) // 首次執(zhí)行延時時間 .setConstraints(new Constraints.Builder().setRequiresBatteryNotLow(true).build()) .build() );
擁有名字的任務(wù)
WorkManager.getInstance(this).enqueueUniqueWork("test", ExistingWorkPolicy.KEEP, new OneTimeWorkRequest.Builder(Test.class).build()); // 此種方法會對重復(fù)名字的任務(wù)進行處理
監(jiān)聽狀態(tài)
WorkManager.getInstance(this).getWorkInfosByTagLiveData("test").observe(this, new Observer<List<WorkInfo>>() { @Override public void onChanged(List<WorkInfo> workInfos) { } });
定義Work代碼
public class Test extends Worker { public Test(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); Data input = getInputData(); // 獲取輸入數(shù)據(jù) boolean isTest = input.getBoolean("is_test", true); } @NonNull @Override public Result doWork() { return Result.success(); } }
繼承使Worker類,實現(xiàn)doWork()方法,此方法是實現(xiàn)任務(wù)的主體;doWork()返回的 Result會通知 WorkManager 服務(wù)工作是否成功,以及工作失敗時是否應(yīng)重試工作。
- Result.success():工作成功完成。
- Result.failure():工作失敗。
- Result.retry():工作失敗,應(yīng)根據(jù)其重試政策在其他時間嘗試。
配置初始化 不同的版本初始化不同,但是都是通過Provider來進行的,2.6之前是WorkManagerInitializer, 2.6之后是InitializationProvider;這里按照2.7.1的版本來說,老版本有需要留言回復(fù);有兩種處理方案
- 移除默認Provider
- 按照Provider流程來進行
按照提供初始化流程處理
1.首先注意一個字符串配置:這個很重要,不建議修改
<string name="androidx_startup" translatable="false">androidx.startup</string>
2.提供實現(xiàn)Initializer的類,這個類是被調(diào)用的關(guān)鍵
3.移除默認實現(xiàn)
<provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> <meta-data android:name="androidx.work.WorkManagerInitializer" android:value="androidx.startup" tools:node="remove" /> // 這表示移除 </provider>
默認實現(xiàn)Initializer類WorkManagerInitializer,使用的默認configuration
public final class WorkManagerInitializer implements Initializer<WorkManager> { private static final String TAG = Logger.tagWithPrefix("WrkMgrInitializer"); @NonNull @Override public WorkManager create(@NonNull Context context) { Logger.get().debug(TAG, "Initializing WorkManager with default configuration."); WorkManager.initialize(context, new Configuration.Builder().build()); // 這里提供Configuration return WorkManager.getInstance(context); } @NonNull @Override public List<Class<? extends androidx.startup.Initializer<?>>> dependencies() { return Collections.emptyList(); // 提供需要執(zhí)行的其它Initializer } }
4.進行注冊自定義實現(xiàn); 在meta-data數(shù)據(jù)處理,key為你需要調(diào)用的初始化類,value必須為R.sting.androidx_startup這個字符串的值; 如下
<provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge" > <meta-data android:name="androidx.work.WorkManagerInitializer" // 這里為你的實現(xiàn)的Initializer android:value="androidx.startup" /> // 這里必須與R.sting.androidx_startup保持一致 </provider>
2.3 重要概念
任務(wù)標識
- id : 通過WorkRequest對象getStringId獲取,每次添加一次任務(wù),就會得到唯一的id
- name :任務(wù)名字,暫時一次性任務(wù)才可以有; 一個任務(wù)最多一個名字,而一個名字可以對應(yīng)多個任務(wù); 同名字任務(wù)可以通過定義不同名字沖突來解決
- tag: 一個任務(wù)可以擁有多個tag,一個tag可以對應(yīng)多個任務(wù)
任務(wù)類型
- 一次性任務(wù): 僅僅執(zhí)行一次, 如果結(jié)果返回retry時,按照重試退避政策,進行重試,直至成功
- 周期性任務(wù): 按照周期執(zhí)行,無論結(jié)果返回什么情況,每次周期內(nèi)僅僅執(zhí)行一次
任務(wù)鏈
使用 WorkManager 創(chuàng)建工作鏈并將其加入隊列。工作鏈用于指定多個依存任務(wù)并定義這些任務(wù)的運行順序。使用 WorkManager.beginWith(OneTimeWorkRequest 或 WorkManager.beginWith(List)返回WorkContinuation實例,然后通過其 then(OneTimeWorkRequest)或 then(List)添加任務(wù)鏈,最后使用WorkContinuation.enqueue()進行執(zhí)行。
任務(wù)鏈先添加的任務(wù)為后續(xù)任務(wù)的先決條件, 也就是前面任務(wù)成功了后面任務(wù)才會執(zhí)行
重試退避政策
針對worker執(zhí)行返回Result.retry()時處理策略, 使用方法setBackoffCriteria設(shè)置, 有兩個指標,策略和延時時間(允許的最小值,即 10 秒);情況有下面2種
- 線性LINEAR: 每次失敗后重新開啟任務(wù)時間延時為 延時時間次數(shù)倍(延時時間 * n)
- EXPONENTIAL: 每次失敗后重新開啟任務(wù)時間延時為 延時時間的指數(shù)倍(延時時間 * 2^n)
輸入合并器
針對一次性任務(wù),且在工作鏈中,父級工作請求的輸出將作為子級的輸入傳入的場景中使用。輸入中會存在相同關(guān)鍵字,這時,輸入就會存在沖突
WorkManager 提供兩種不同類型的 InputMerger:
- OverwritingInputMerger:會嘗試將所有輸入中的所有鍵添加到輸出中。如果發(fā)生沖突,它會覆蓋先前設(shè)置的鍵。
- ArrayCreatingInputMerger: 會嘗試合并輸入,并在必要時創(chuàng)建數(shù)組。
沖突解決政策
調(diào)度唯一工作時,發(fā)生沖突時要執(zhí)行的操作??梢酝ㄟ^在將工作加入隊列時傳遞一個枚舉來實現(xiàn)此目的。
對于一次性工作,您需要提供一個 ExistingWorkPolicy,有4 個選項。
- REPLACE:用新工作替換現(xiàn)有工作。此選項將取消現(xiàn)有工作。
- KEEP:保留現(xiàn)有工作,并忽略新工作。
- APPEND:將新工作附加到現(xiàn)有工作的末尾。此政策將導(dǎo)致您的新工作到現(xiàn)有工作,在現(xiàn)有工作完成后運行。 現(xiàn)有工作將成為新工作的先決條件。如果現(xiàn)有工作變?yōu)?nbsp;
CANCELLED
或FAILED
狀態(tài),新工作也會變?yōu)?nbsp;CANCELLED
或FAILED
。如果您希望無論現(xiàn)有工作的狀態(tài)如何都運行新工作,請改用APPEND_OR_REPLACE
。 - APPEND_OR_REPLACE: 類似于 APPEND,不過它并不依賴于先決條件工作狀態(tài)。**即使現(xiàn)有工作變?yōu)?nbsp;
CANCELLED
或FAILED
狀態(tài),新工作仍會運行。
約束條件
任務(wù)執(zhí)行的先決條件,使用Contraints.Builder()進行構(gòu)建實例。有一下約束
- NetworkType : 約束運行工作所需的網(wǎng)絡(luò)類型。
- BatteryNotLow : 如果設(shè)置為 true,那么當設(shè)備處于“電量不足模式”時,工作不會運行。
- RequiresCharging : 如果設(shè)置為 true,那么工作只能在設(shè)備充電時運行。
- DeviceIdle:如果設(shè)置為 true,則要求用戶的設(shè)備必須處于空閑狀態(tài),才能運行工作。在運行批量操作時,此約束會非常有用;若是不用此約束,批量操作可能會降低用戶設(shè)備上正在積極運行的其他應(yīng)用的性能。
- StorageNotLow: 如果設(shè)置為 true,那么當用戶設(shè)備上的存儲空間不足時,工作不會運行。
還有以下約束,對于>=24的版本有效:
- setContentUriTriggers方法: 設(shè)置觸發(fā)任務(wù)的uri
- setTriggerContentUpdateDelay方法:設(shè)置觸發(fā)執(zhí)行的延遲時間
- setTriggerMaxContentDelay方法:設(shè)置處罰執(zhí)行的最大延時
3、原理
合理分為幾個部分來說
- 1. 約束檢測: 約束檢測的邏輯以及實現(xiàn)
- 2. 任務(wù)調(diào)度: 3種調(diào)度器, alarm、greedy、JobScheduler; 用于喚起任務(wù)執(zhí)行
- 3. 任務(wù)執(zhí)行流程 :請求的包裝,任務(wù)如何加入調(diào)度器,以及調(diào)度完成后執(zhí)行任務(wù)詳情
這里有需要理解的一個技術(shù)點:SettableFuture,實現(xiàn)了ListenableFuture接口,并且增加了下面接口ListenableFuture。這個類在源碼中頻繁使用
ListenableFuture只有一個方法
void addListener(Runnable listener, Executor executor)
調(diào)用了這個方法,這個類才有使用意義;調(diào)用之后,表示SettableFuture若完成,則使用executor執(zhí)行l(wèi)istener
另外一個讓我覺得有意思的地方,或者說不同之前future的地方是, SettableFuture未實現(xiàn)Runnable接口,也就是其結(jié)果不是來源于自己,來源于調(diào)用下面3個方法
public boolean set(@Nullable V value) // 正常設(shè)置結(jié)果 public boolean setException(Throwable throwable) // 執(zhí)行結(jié)果異常 public boolean setFuture(ListenableFuture<? extends V> future) // 設(shè)置執(zhí)行結(jié)果來源,也就是ListenableFuture的執(zhí)行結(jié)果
3.1 約束檢測
類圖如下
結(jié)合源碼分析,結(jié)論有:
- WorkConstraintsTracker類:使用入口,構(gòu)造器傳入WorkConstraintsCallback為結(jié)果回調(diào),也可以直接調(diào)用方法areAllConstraintsMet來判斷是否滿足約束
- ConstraintController類:一種約束控制器類,通過OnConstraintUpdatedCallback回調(diào)實時結(jié)果給WorkConstraintsTracker, 通過ConstraintListener與ConstraintTracker聯(lián)系
- ConstraintTracker:實際約束值的檢測者,ConstraintListener回調(diào)實時傳遞值與ConstraintController;通過setState觸發(fā)回調(diào)
實際檢測技術(shù)點,也就是ConstraintTracker實現(xiàn)
3.1.1 StorageNotLowTracker類
存儲空間的是否低,根據(jù)系統(tǒng)廣播來處理
Intent.ACTION_DEVICE_STORAGE_OK // 存儲空間夠用 Intent.ACTION_DEVICE_STORAGE_LOW // 存儲空間很低
初步檢測狀態(tài):
@Override public Boolean getInitialState() { Intent intent = mAppContext.registerReceiver(null, getIntentFilter()); if (intent == null || intent.getAction() == null) { return true; } else { switch (intent.getAction()) { case Intent.ACTION_DEVICE_STORAGE_OK: return true; case Intent.ACTION_DEVICE_STORAGE_LOW: return false; default: return null; } } }
實時更新通過接收廣播信息:
public void onBroadcastReceive(Context context, @NonNull Intent intent) { if (intent.getAction() == null) { return; } switch (intent.getAction()) { case Intent.ACTION_DEVICE_STORAGE_OK: setState(true); break; case Intent.ACTION_DEVICE_STORAGE_LOW: setState(false); break; } }
3.1.2 BatteryChargingTracker類
充電狀態(tài),同樣通過廣播來進行處理;
接收廣播根據(jù)版本不同而不同如下:
public IntentFilter getIntentFilter() { IntentFilter intentFilter = new IntentFilter(); if (Build.VERSION.SDK_INT >= 23) { intentFilter.addAction(BatteryManager.ACTION_CHARGING); intentFilter.addAction(BatteryManager.ACTION_DISCHARGING); } else { intentFilter.addAction(Intent.ACTION_POWER_CONNECTED); intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED); } return intentFilter; }
初始狀態(tài):
@Override public Boolean getInitialState() { IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent intent = mAppContext.registerReceiver(null, intentFilter); if (intent == null) { Logger.get().error(TAG, "getInitialState - null intent received"); return null; } return isBatteryChangedIntentCharging(intent); } private boolean isBatteryChangedIntentCharging(Intent intent) { boolean charging; if (Build.VERSION.SDK_INT >= 23) { int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1); charging = (status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL); } else { int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0); charging = (chargePlug != 0); } return charging; }
實時狀態(tài):
@Override public void onBroadcastReceive(Context context, @NonNull Intent intent) { String action = intent.getAction(); if (action == null) { return; } Logger.get().debug(TAG, String.format("Received %s", action)); switch (action) { case BatteryManager.ACTION_CHARGING: setState(true); break; case BatteryManager.ACTION_DISCHARGING: setState(false); break; case Intent.ACTION_POWER_CONNECTED: setState(true); break; case Intent.ACTION_POWER_DISCONNECTED: setState(false); break; } }
3.1.3 BatteryNotLowTracker類
電量是否低,同樣通過廣播實時獲取狀態(tài)
Intent.ACTION_BATTERY_OKAY //電量正常 Intent.ACTION_BATTERY_LOW //電量低
初始狀態(tài):
@Override public Boolean getInitialState() { IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent intent = mAppContext.registerReceiver(null, intentFilter); if (intent == null) { Logger.get().error(TAG, "getInitialState - null intent received"); return null; } int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1); int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); float batteryPercentage = level / (float) scale; return (status == BatteryManager.BATTERY_STATUS_UNKNOWN || batteryPercentage > BATTERY_LOW_THRESHOLD); // 這里是小于15%算是低 }
實時更新:
@Override public void onBroadcastReceive(Context context, @NonNull Intent intent) { if (intent.getAction() == null) { return; } Logger.get().debug(TAG, String.format("Received %s", intent.getAction())); switch (intent.getAction()) { case Intent.ACTION_BATTERY_OKAY: setState(true); break; case Intent.ACTION_BATTERY_LOW: setState(false); break; } }
3.1.4 NetworkStateTracker類
網(wǎng)絡(luò)狀態(tài),這個根據(jù)版本不同使用也不同
- 大于等于24時, ConnectivityManager.registerDefaultNetworkCallback(NetworkCallback networkCallback) 來進行監(jiān)聽網(wǎng)絡(luò)狀態(tài)變化
- 小于24時,通過廣播ConnectivityManager.CONNECTIVITY_ACTION來進行處理
初始狀態(tài):
NetworkState getActiveNetworkState() { NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); boolean isConnected = info != null && info.isConnected(); boolean isValidated = isActiveNetworkValidated(); boolean isMetered = ConnectivityManagerCompat.isActiveNetworkMetered(mConnectivityManager); boolean isNotRoaming = info != null && !info.isRoaming(); return new NetworkState(isConnected, isValidated, isMetered, isNotRoaming); }
實時狀態(tài): 在回調(diào)NetworkCallback或者接收到廣播onReceive時,調(diào)用getActiveNetworkState得到
3.2 任務(wù)調(diào)度器
實現(xiàn)Scheduler接口,調(diào)度器有3個:
- SystemJobScheduler: 使用JobSceuler來完成任務(wù)喚起;小于23版本時使用
- SystemAlarmScheduler: 使用Alarm來完成任務(wù)喚起;大于等于23版本使用
- GreedyScheduler:在進程內(nèi)直接調(diào)用,不同android版本均會建立 其實還有一個androidx.work.impl.background.gcm.GcmScheduler,這個實現(xiàn)需要借助google的GCM推送服務(wù)來實現(xiàn),這里用了反射(國內(nèi)不支持),并且優(yōu)先于SystemAlarmScheduler
調(diào)度器的創(chuàng)建在WorkManagerImpl類
public List<Scheduler> createSchedulers( @NonNull Context context, @NonNull Configuration configuration, @NonNull TaskExecutor taskExecutor) { return Arrays.asList( Schedulers.createBestAvailableBackgroundScheduler(context, this), // 可以被系統(tǒng)喚起調(diào)用手段 new GreedyScheduler(context, configuration, taskExecutor, this)); // 進程內(nèi)調(diào)用手段 }
調(diào)度器被調(diào)用的邏輯在Schedulers
public static void schedule(Configuration configuration,WorkDatabase workDatabase,List<Scheduler> schedulers) { if (schedulers == null || schedulers.size() == 0) { return; } WorkSpecDao workSpecDao = workDatabase.workSpecDao(); List<WorkSpec> eligibleWorkSpecsForLimitedSlots; List<WorkSpec> allEligibleWorkSpecs; workDatabase.beginTransaction(); try { eligibleWorkSpecsForLimitedSlots = workSpecDao.getEligibleWorkForScheduling(configuration.getMaxSchedulerLimit()); allEligibleWorkSpecs = workSpecDao.getAllEligibleWorkSpecsForScheduling(MAX_GREEDY_SCHEDULER_LIMIT); if (eligibleWorkSpecsForLimitedSlots != null && eligibleWorkSpecsForLimitedSlots.size() > 0) { long now = System.currentTimeMillis(); for (WorkSpec workSpec : eligibleWorkSpecsForLimitedSlots) { workSpecDao.markWorkSpecScheduled(workSpec.id, now); // 進行插槽狀態(tài)處理,非默認才會被GreedyScheduler進行調(diào)度 } } workDatabase.setTransactionSuccessful(); } finally { workDatabase.endTransaction(); } if (eligibleWorkSpecsForLimitedSlots != null && eligibleWorkSpecsForLimitedSlots.size() > 0) { WorkSpec[] eligibleWorkSpecsArray = new WorkSpec[eligibleWorkSpecsForLimitedSlots.size()]; eligibleWorkSpecsArray = eligibleWorkSpecsForLimitedSlots.toArray(eligibleWorkSpecsArray); for (Scheduler scheduler : schedulers) { if (scheduler.hasLimitedSchedulingSlots()) { scheduler.schedule(eligibleWorkSpecsArray); } } } if (allEligibleWorkSpecs != null && allEligibleWorkSpecs.size() > 0) { WorkSpec[] enqueuedWorkSpecsArray = new WorkSpec[allEligibleWorkSpecs.size()]; enqueuedWorkSpecsArray = allEligibleWorkSpecs.toArray(enqueuedWorkSpecsArray); for (Scheduler scheduler : schedulers) { if (!scheduler.hasLimitedSchedulingSlots()) { scheduler.schedule(enqueuedWorkSpecsArray); } } } }
GreedyScheduler調(diào)度器處理任務(wù)正在排隊,任務(wù)最大限制是200條;其它調(diào)度器執(zhí)行在排隊且未被調(diào)度器處理的,且和現(xiàn)在調(diào)度器已經(jīng)處理的總和不超過配置中的最大限制,默認是版本23是10,其它版本20
3.2.1 GreedyScheduler調(diào)度器
類圖:
通過流程分析有以下結(jié)論:
- 延時通過DelayedWorkTracker來處理,其實是通過Handler機制處理,在主Handler中進行
- 約束通過WorkConstraintsTracker來進行檢測,通過自身實現(xiàn)回調(diào)WorkConstraintsCallback來處理
- 滿足執(zhí)行條件的任務(wù),通過WorkManagerImpl進行執(zhí)行
- 實現(xiàn)ExecutionListener接口,來檢測已經(jīng)完成的任務(wù),避免重復(fù)執(zhí)行
- 必須在配置的進程中處理,默認為應(yīng)用主進程
3.2.2 SystemJobScheduler調(diào)度器
類圖:
- SystemJobInfoConverter進行數(shù)據(jù)轉(zhuǎn)換JobScheduler需要的入?yún)?,以及其它?shù)據(jù)互相轉(zhuǎn)換
- SystemJobService負責(zé)調(diào)用WorkManagerImpl類方法去執(zhí)行任務(wù);以及任務(wù)執(zhí)行完成的處理
- 約束由JobScheduler自己內(nèi)部進行;而這里只需要調(diào)用方法即可
這個調(diào)度器比較簡單,其實現(xiàn)主要通過了JobScheduler的實現(xiàn),JobScheduler的實現(xiàn)原理這里不做介紹
3.2.3 SystemAlarmScheduler調(diào)度器
這個調(diào)度器實現(xiàn)就比較復(fù)雜了;類圖:
- SystemAlarmService服務(wù),任務(wù)各種情況通過啟動此服務(wù)來處理
- SystemAlarmDispatcher, 任務(wù)各種情況分發(fā)處理節(jié)點,并回傳無任務(wù)處理情況,銷毀服務(wù)
- CommandHander具體處理各種intent事件,以及內(nèi)部任務(wù)調(diào)度完成后收尾(實現(xiàn)了ExecutionListener接口,其意義卻不是任務(wù)已經(jīng)執(zhí)行完成后的處理)
- ConstraintsCommandHandler:處理了約束情況,使用WorkConstraintsTracker檢測當時狀態(tài);事件是由*Proxy類廣播啟動service進而通知的, ConstraintProxyUpdateReceiver控制各種約束廣播是否可用
- Alarms,使用AlramManager機制處理任務(wù)延時,并啟動服務(wù)
- RescheduleReceiver:開機或者時間事件廣播,重啟動WorkManager處理剩余任務(wù)的;這里只有靜態(tài)注冊,非所有版本有效
- DelayMetCommandHandler:進行約束實時更新判斷,并在滿足執(zhí)行時通過WorkManagerImpl執(zhí)行任務(wù);在類圖中沒有表明其實現(xiàn)了WorkTimer.TimeLimitExceededListener(這個是對任務(wù)從調(diào)度狀態(tài)到運行狀態(tài)的一個控制,10分鐘未完成狀態(tài)變化重新處理)
- 使用了PowerManager.WakeLock保證調(diào)度過程中cpu運行
3.3、任務(wù)流程
任務(wù)流程涉及調(diào)度的具體過程,在以下流程圖中就會直接從Schedulers方法直接異步執(zhí)行啟動任務(wù);調(diào)度過程省略;
3.3.1 enqueue流程
WorkManager調(diào)用時,均是先包裝成WorkContinuationImpl然后調(diào)用其方法enqueue執(zhí)行;
WorkContinuationImpl: 包含了一個鏈表,指向了所有依賴的WorkContinuation對象;也記錄了任務(wù)本身的一些信息和流程狀態(tài)
流程圖
- WorkerWrapper:handleResult方法后續(xù)是對不同結(jié)果以及任務(wù)類別,對任務(wù)數(shù)據(jù)進行相應(yīng)處理;其中進行了輸入數(shù)據(jù)的合并;在執(zhí)行中多個關(guān)鍵點對取消進行校驗處理
- WorkForegroundRunnable:啟動前臺服務(wù),這個需要在實現(xiàn)Work中提供通知信息(方法getForegroundInfoAsync);如果有加急處理,也是啟動前臺服務(wù)
- ListenableWorker中,通過setProgressAsync可以設(shè)置進度數(shù)據(jù);成功時,doWork返回的結(jié)果中含有成功數(shù)據(jù)
- EnqueueRunnable:enqueueWorkWithPrerequisites方法中對重復(fù)任務(wù)依據(jù)策略進行了處理
- CancelWorkRunable:iterativelyCancelWorkAndDependents刪除了依賴其的任務(wù)
- 中間大量使用SettableFuture來讓各個Runnable執(zhí)行等待其執(zhí)行的先決條件
3.3.2 cancel流程
取消時,可以通過任務(wù)的id、name、tag或者取消所有;不管是哪種調(diào)用,都是通過標識獲取滿足條件的任務(wù)id集合,并進行取消;其流程也只是在id集合查詢時不同而已
流程圖
- WorkWrapper進行打斷時,其流程也就是任務(wù)執(zhí)行時打斷處理,在執(zhí)行過程中多處對打斷進行了檢測處理
- 取消時,對調(diào)度、執(zhí)行中的任務(wù)分別進行取消;同時同步狀態(tài)到數(shù)據(jù)庫
4、總結(jié)
上面只是大概介紹了常規(guī)使用,以及相關(guān)類圖、流程,代碼細節(jié)并沒有展示。代碼中的一些技術(shù)點需要至少會用
- AlarmMananger
- JobScheduler
- 電量低、內(nèi)存不足、充電狀態(tài)、網(wǎng)絡(luò)狀態(tài)檢測以及實時更新
- SettableFuture
- ......
從代碼架構(gòu)上,也有需要去多思考的地方
- 各個部分的分離抽象
- 廣播、服務(wù)組件的打開關(guān)閉、以及服務(wù)的銷毀、任務(wù)執(zhí)行完畢后的回調(diào)收尾等性能的考量
- 數(shù)據(jù)庫表的設(shè)計
- 不論哪個版本,均有兩個調(diào)度器去執(zhí)行任務(wù),他們有可能去處理同一個任務(wù),這帶來的復(fù)雜度處理邏輯以及優(yōu)勢
- ......
以上就是Android WorkManager使用以及源碼分析的詳細內(nèi)容,更多關(guān)于Android WorkManager的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android實現(xiàn)的數(shù)字格式化用法示例
這篇文章主要介紹了Android實現(xiàn)的數(shù)字格式化用法,結(jié)合實例形式分析了Android數(shù)學(xué)運算中數(shù)字格式化輸出的相關(guān)技巧,需要的朋友可以參考下2016-08-08Android EasyPlayer聲音自動停止、恢復(fù),一鍵靜音等功能
這篇文章主要介紹了Android EasyPlayer聲音自動停止、恢復(fù),一鍵靜音等功能的相關(guān)資料,需要的朋友可以參考下2017-03-03Android為應(yīng)用添加數(shù)字角標的簡單實現(xiàn)
應(yīng)用的角標是用來標記有多少條提醒沒讀,本篇文章主要介紹了Android為應(yīng)用添加角標的簡單實現(xiàn),有興趣的可以了解一下。2017-04-04Compose開發(fā)之動畫藝術(shù)探索及實現(xiàn)示例
這篇文章主要為大家介紹了Compose開發(fā)之動畫藝術(shù)探索及實現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09flutter自定義InheritedProvider實現(xiàn)狀態(tài)管理詳解
這篇文章主要為大家介紹了flutter自定義InheritedProvider實現(xiàn)狀態(tài)管理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11