Android WorkManager使用以及源碼分析
1、前言
WorkManager 是適合用于持久性工作的推薦解決方案。如果工作始終要通過應(yīng)用重啟和系統(tǒng)重新啟動(dòng)來調(diào)度,便是持久性的工作。由于大多數(shù)后臺(tái)處理操作都是通過持久性工作完成的,因此 WorkManager 是適用于后臺(tái)處理操作的主要推薦 API。 WorkManager 可處理三種類型的持久性工作:
- 立即執(zhí)行:必須立即開始且很快就完成的任務(wù),可以加急。
- 長時(shí)間運(yùn)行:運(yùn)行時(shí)間可能較長(有可能超過 10 分鐘)的任務(wù)。
- 可延期執(zhí)行:延期開始并且可以定期運(yùn)行的預(yù)定任務(wù)。
2、使用
2.1、引用
implementation "androidx.work:work-runtime:2.7.1" //基礎(chǔ)使用 implementation "androidx.work:work-multiprocess:2.7.1" //跨進(jìn)程時(shí)引用
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ǔn)教幚頃r(shí),多個(gè)上流執(zhí)行結(jié)果合并,作為下流輸入數(shù)據(jù)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 5, TimeUnit.MINUTES) // 重試策略
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) // 加急處理
.addTag("test").addTag("huahua") // 標(biāo)識(shí)
.setInputData(data) // 輸入數(shù)據(jù)
.setInitialDelay(5, TimeUnit.SECONDS) // 執(zhí)行延時(shí)時(shí)間
.setConstraints(new Constraints.Builder().setRequiresBatteryNotLow(true).build()) // 約束,部分約束只對(duì)高版本有效
.build()
);
執(zhí)行周期性任務(wù)
WorkManager.getInstance(this).enqueue(
new PeriodicWorkRequest.Builder(Test.class, 2, TimeUnit.HOURS) // 執(zhí)行周期性任務(wù),周期2小時(shí)
.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í)行延時(shí)時(shí)間
.setConstraints(new Constraints.Builder().setRequiresBatteryNotLow(true).build())
.build()
);擁有名字的任務(wù)
WorkManager.getInstance(this).enqueueUniqueWork("test", ExistingWorkPolicy.KEEP,
new OneTimeWorkRequest.Builder(Test.class).build()); // 此種方法會(huì)對(duì)重復(fù)名字的任務(wù)進(jìn)行處理
監(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類,實(shí)現(xiàn)doWork()方法,此方法是實(shí)現(xiàn)任務(wù)的主體;doWork()返回的 Result會(huì)通知 WorkManager 服務(wù)工作是否成功,以及工作失敗時(shí)是否應(yīng)重試工作。
- Result.success():工作成功完成。
- Result.failure():工作失敗。
- Result.retry():工作失敗,應(yīng)根據(jù)其重試政策在其他時(shí)間嘗試。
配置初始化 不同的版本初始化不同,但是都是通過Provider來進(jìn)行的,2.6之前是WorkManagerInitializer, 2.6之后是InitializationProvider;這里按照2.7.1的版本來說,老版本有需要留言回復(fù);有兩種處理方案
- 移除默認(rèn)Provider
- 按照Provider流程來進(jìn)行
按照提供初始化流程處理
1.首先注意一個(gè)字符串配置:這個(gè)很重要,不建議修改
<string name="androidx_startup" translatable="false">androidx.startup</string>
2.提供實(shí)現(xiàn)Initializer的類,這個(gè)類是被調(diào)用的關(guān)鍵
3.移除默認(rèn)實(shí)現(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>默認(rèn)實(shí)現(xiàn)Initializer類WorkManagerInitializer,使用的默認(rèn)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.進(jìn)行注冊(cè)自定義實(shí)現(xiàn); 在meta-data數(shù)據(jù)處理,key為你需要調(diào)用的初始化類,value必須為R.sting.androidx_startup這個(gè)字符串的值; 如下
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge" >
<meta-data
android:name="androidx.work.WorkManagerInitializer" // 這里為你的實(shí)現(xiàn)的Initializer
android:value="androidx.startup" /> // 這里必須與R.sting.androidx_startup保持一致
</provider>2.3 重要概念
任務(wù)標(biāo)識(shí)
- id : 通過WorkRequest對(duì)象getStringId獲取,每次添加一次任務(wù),就會(huì)得到唯一的id
- name :任務(wù)名字,暫時(shí)一次性任務(wù)才可以有; 一個(gè)任務(wù)最多一個(gè)名字,而一個(gè)名字可以對(duì)應(yīng)多個(gè)任務(wù); 同名字任務(wù)可以通過定義不同名字沖突來解決
- tag: 一個(gè)任務(wù)可以擁有多個(gè)tag,一個(gè)tag可以對(duì)應(yīng)多個(gè)任務(wù)
任務(wù)類型
- 一次性任務(wù): 僅僅執(zhí)行一次, 如果結(jié)果返回retry時(shí),按照重試退避政策,進(jìn)行重試,直至成功
- 周期性任務(wù): 按照周期執(zhí)行,無論結(jié)果返回什么情況,每次周期內(nèi)僅僅執(zhí)行一次
任務(wù)鏈
使用 WorkManager 創(chuàng)建工作鏈并將其加入隊(duì)列。工作鏈用于指定多個(gè)依存任務(wù)并定義這些任務(wù)的運(yùn)行順序。使用 WorkManager.beginWith(OneTimeWorkRequest 或 WorkManager.beginWith(List)返回WorkContinuation實(shí)例,然后通過其 then(OneTimeWorkRequest)或 then(List)添加任務(wù)鏈,最后使用WorkContinuation.enqueue()進(jìn)行執(zhí)行。
任務(wù)鏈先添加的任務(wù)為后續(xù)任務(wù)的先決條件, 也就是前面任務(wù)成功了后面任務(wù)才會(huì)執(zhí)行
重試退避政策
針對(duì)worker執(zhí)行返回Result.retry()時(shí)處理策略, 使用方法setBackoffCriteria設(shè)置, 有兩個(gè)指標(biāo),策略和延時(shí)時(shí)間(允許的最小值,即 10 秒);情況有下面2種
- 線性LINEAR: 每次失敗后重新開啟任務(wù)時(shí)間延時(shí)為 延時(shí)時(shí)間次數(shù)倍(延時(shí)時(shí)間 * n)
- EXPONENTIAL: 每次失敗后重新開啟任務(wù)時(shí)間延時(shí)為 延時(shí)時(shí)間的指數(shù)倍(延時(shí)時(shí)間 * 2^n)
輸入合并器
針對(duì)一次性任務(wù),且在工作鏈中,父級(jí)工作請(qǐng)求的輸出將作為子級(jí)的輸入傳入的場景中使用。輸入中會(huì)存在相同關(guān)鍵字,這時(shí),輸入就會(huì)存在沖突
WorkManager 提供兩種不同類型的 InputMerger:
- OverwritingInputMerger:會(huì)嘗試將所有輸入中的所有鍵添加到輸出中。如果發(fā)生沖突,它會(huì)覆蓋先前設(shè)置的鍵。
- ArrayCreatingInputMerger: 會(huì)嘗試合并輸入,并在必要時(shí)創(chuàng)建數(shù)組。
沖突解決政策
調(diào)度唯一工作時(shí),發(fā)生沖突時(shí)要執(zhí)行的操作??梢酝ㄟ^在將工作加入隊(duì)列時(shí)傳遞一個(gè)枚舉來實(shí)現(xiàn)此目的。
對(duì)于一次性工作,您需要提供一個(gè) ExistingWorkPolicy,有4 個(gè)選項(xiàng)。
- REPLACE:用新工作替換現(xiàn)有工作。此選項(xiàng)將取消現(xiàn)有工作。
- KEEP:保留現(xiàn)有工作,并忽略新工作。
- APPEND:將新工作附加到現(xiàn)有工作的末尾。此政策將導(dǎo)致您的新工作到現(xiàn)有工作,在現(xiàn)有工作完成后運(yùn)行。 現(xiàn)有工作將成為新工作的先決條件。如果現(xiàn)有工作變?yōu)?nbsp;
CANCELLED或FAILED狀態(tài),新工作也會(huì)變?yōu)?nbsp;CANCELLED或FAILED。如果您希望無論現(xiàn)有工作的狀態(tài)如何都運(yùn)行新工作,請(qǐng)改用APPEND_OR_REPLACE。 - APPEND_OR_REPLACE: 類似于 APPEND,不過它并不依賴于先決條件工作狀態(tài)。**即使現(xiàn)有工作變?yōu)?nbsp;
CANCELLED或FAILED狀態(tài),新工作仍會(huì)運(yùn)行。
約束條件
任務(wù)執(zhí)行的先決條件,使用Contraints.Builder()進(jìn)行構(gòu)建實(shí)例。有一下約束
- NetworkType : 約束運(yùn)行工作所需的網(wǎng)絡(luò)類型。
- BatteryNotLow : 如果設(shè)置為 true,那么當(dāng)設(shè)備處于“電量不足模式”時(shí),工作不會(huì)運(yùn)行。
- RequiresCharging : 如果設(shè)置為 true,那么工作只能在設(shè)備充電時(shí)運(yùn)行。
- DeviceIdle:如果設(shè)置為 true,則要求用戶的設(shè)備必須處于空閑狀態(tài),才能運(yùn)行工作。在運(yùn)行批量操作時(shí),此約束會(huì)非常有用;若是不用此約束,批量操作可能會(huì)降低用戶設(shè)備上正在積極運(yùn)行的其他應(yīng)用的性能。
- StorageNotLow: 如果設(shè)置為 true,那么當(dāng)用戶設(shè)備上的存儲(chǔ)空間不足時(shí),工作不會(huì)運(yùn)行。
還有以下約束,對(duì)于>=24的版本有效:
- setContentUriTriggers方法: 設(shè)置觸發(fā)任務(wù)的uri
- setTriggerContentUpdateDelay方法:設(shè)置觸發(fā)執(zhí)行的延遲時(shí)間
- setTriggerMaxContentDelay方法:設(shè)置處罰執(zhí)行的最大延時(shí)
3、原理
合理分為幾個(gè)部分來說
- 1. 約束檢測: 約束檢測的邏輯以及實(shí)現(xiàn)
- 2. 任務(wù)調(diào)度: 3種調(diào)度器, alarm、greedy、JobScheduler; 用于喚起任務(wù)執(zhí)行
- 3. 任務(wù)執(zhí)行流程 :請(qǐng)求的包裝,任務(wù)如何加入調(diào)度器,以及調(diào)度完成后執(zhí)行任務(wù)詳情
這里有需要理解的一個(gè)技術(shù)點(diǎn):SettableFuture,實(shí)現(xiàn)了ListenableFuture接口,并且增加了下面接口ListenableFuture。這個(gè)類在源碼中頻繁使用
ListenableFuture只有一個(gè)方法
void addListener(Runnable listener, Executor executor)
調(diào)用了這個(gè)方法,這個(gè)類才有使用意義;調(diào)用之后,表示SettableFuture若完成,則使用executor執(zhí)行l(wèi)istener
另外一個(gè)讓我覺得有意思的地方,或者說不同之前future的地方是, SettableFuture未實(shí)現(xiàn)Runnable接口,也就是其結(jié)果不是來源于自己,來源于調(diào)用下面3個(gè)方法
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)實(shí)時(shí)結(jié)果給WorkConstraintsTracker, 通過ConstraintListener與ConstraintTracker聯(lián)系
- ConstraintTracker:實(shí)際約束值的檢測者,ConstraintListener回調(diào)實(shí)時(shí)傳遞值與ConstraintController;通過setState觸發(fā)回調(diào)
實(shí)際檢測技術(shù)點(diǎn),也就是ConstraintTracker實(shí)現(xiàn)
3.1.1 StorageNotLowTracker類
存儲(chǔ)空間的是否低,根據(jù)系統(tǒng)廣播來處理
Intent.ACTION_DEVICE_STORAGE_OK // 存儲(chǔ)空間夠用 Intent.ACTION_DEVICE_STORAGE_LOW // 存儲(chǔ)空間很低
初步檢測狀態(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;
}
}
}實(shí)時(shí)更新通過接收廣播信息:
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ìn)行處理;
接收廣播根據(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;
}實(shí)時(shí)狀態(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類
電量是否低,同樣通過廣播實(shí)時(shí)獲取狀態(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%算是低
}實(shí)時(shí)更新:
@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),這個(gè)根據(jù)版本不同使用也不同
- 大于等于24時(shí), ConnectivityManager.registerDefaultNetworkCallback(NetworkCallback networkCallback) 來進(jìn)行監(jiān)聽網(wǎng)絡(luò)狀態(tài)變化
- 小于24時(shí),通過廣播ConnectivityManager.CONNECTIVITY_ACTION來進(jìn)行處理
初始狀態(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);
} 實(shí)時(shí)狀態(tài): 在回調(diào)NetworkCallback或者接收到廣播onReceive時(shí),調(diào)用getActiveNetworkState得到
3.2 任務(wù)調(diào)度器
實(shí)現(xiàn)Scheduler接口,調(diào)度器有3個(gè):
- SystemJobScheduler: 使用JobSceuler來完成任務(wù)喚起;小于23版本時(shí)使用
- SystemAlarmScheduler: 使用Alarm來完成任務(wù)喚起;大于等于23版本使用
- GreedyScheduler:在進(jìn)程內(nèi)直接調(diào)用,不同android版本均會(huì)建立 其實(shí)還有一個(gè)androidx.work.impl.background.gcm.GcmScheduler,這個(gè)實(shí)現(xiàn)需要借助google的GCM推送服務(wù)來實(shí)現(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)); // 進(jìn)程內(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); // 進(jìn)行插槽狀態(tài)處理,非默認(rèn)才會(huì)被GreedyScheduler進(jìn)行調(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ù)正在排隊(duì),任務(wù)最大限制是200條;其它調(diào)度器執(zhí)行在排隊(duì)且未被調(diào)度器處理的,且和現(xiàn)在調(diào)度器已經(jīng)處理的總和不超過配置中的最大限制,默認(rèn)是版本23是10,其它版本20
3.2.1 GreedyScheduler調(diào)度器
類圖:

通過流程分析有以下結(jié)論:
- 延時(shí)通過DelayedWorkTracker來處理,其實(shí)是通過Handler機(jī)制處理,在主Handler中進(jìn)行
- 約束通過WorkConstraintsTracker來進(jìn)行檢測,通過自身實(shí)現(xiàn)回調(diào)WorkConstraintsCallback來處理
- 滿足執(zhí)行條件的任務(wù),通過WorkManagerImpl進(jìn)行執(zhí)行
- 實(shí)現(xiàn)ExecutionListener接口,來檢測已經(jīng)完成的任務(wù),避免重復(fù)執(zhí)行
- 必須在配置的進(jìn)程中處理,默認(rèn)為應(yīng)用主進(jìn)程
3.2.2 SystemJobScheduler調(diào)度器
類圖:

- SystemJobInfoConverter進(jìn)行數(shù)據(jù)轉(zhuǎn)換JobScheduler需要的入?yún)ⅲ约捌渌鼣?shù)據(jù)互相轉(zhuǎn)換
- SystemJobService負(fù)責(zé)調(diào)用WorkManagerImpl類方法去執(zhí)行任務(wù);以及任務(wù)執(zhí)行完成的處理
- 約束由JobScheduler自己內(nèi)部進(jìn)行;而這里只需要調(diào)用方法即可
這個(gè)調(diào)度器比較簡單,其實(shí)現(xiàn)主要通過了JobScheduler的實(shí)現(xiàn),JobScheduler的實(shí)現(xiàn)原理這里不做介紹
3.2.3 SystemAlarmScheduler調(diào)度器
這個(gè)調(diào)度器實(shí)現(xiàn)就比較復(fù)雜了;類圖:

- SystemAlarmService服務(wù),任務(wù)各種情況通過啟動(dòng)此服務(wù)來處理
- SystemAlarmDispatcher, 任務(wù)各種情況分發(fā)處理節(jié)點(diǎn),并回傳無任務(wù)處理情況,銷毀服務(wù)
- CommandHander具體處理各種intent事件,以及內(nèi)部任務(wù)調(diào)度完成后收尾(實(shí)現(xiàn)了ExecutionListener接口,其意義卻不是任務(wù)已經(jīng)執(zhí)行完成后的處理)
- ConstraintsCommandHandler:處理了約束情況,使用WorkConstraintsTracker檢測當(dāng)時(shí)狀態(tài);事件是由*Proxy類廣播啟動(dòng)service進(jìn)而通知的, ConstraintProxyUpdateReceiver控制各種約束廣播是否可用
- Alarms,使用AlramManager機(jī)制處理任務(wù)延時(shí),并啟動(dòng)服務(wù)
- RescheduleReceiver:開機(jī)或者時(shí)間事件廣播,重啟動(dòng)WorkManager處理剩余任務(wù)的;這里只有靜態(tài)注冊(cè),非所有版本有效
- DelayMetCommandHandler:進(jìn)行約束實(shí)時(shí)更新判斷,并在滿足執(zhí)行時(shí)通過WorkManagerImpl執(zhí)行任務(wù);在類圖中沒有表明其實(shí)現(xiàn)了WorkTimer.TimeLimitExceededListener(這個(gè)是對(duì)任務(wù)從調(diào)度狀態(tài)到運(yùn)行狀態(tài)的一個(gè)控制,10分鐘未完成狀態(tài)變化重新處理)
- 使用了PowerManager.WakeLock保證調(diào)度過程中cpu運(yùn)行
3.3、任務(wù)流程
任務(wù)流程涉及調(diào)度的具體過程,在以下流程圖中就會(huì)直接從Schedulers方法直接異步執(zhí)行啟動(dòng)任務(wù);調(diào)度過程省略;
3.3.1 enqueue流程
WorkManager調(diào)用時(shí),均是先包裝成WorkContinuationImpl然后調(diào)用其方法enqueue執(zhí)行;
WorkContinuationImpl: 包含了一個(gè)鏈表,指向了所有依賴的WorkContinuation對(duì)象;也記錄了任務(wù)本身的一些信息和流程狀態(tài)
流程圖

- WorkerWrapper:handleResult方法后續(xù)是對(duì)不同結(jié)果以及任務(wù)類別,對(duì)任務(wù)數(shù)據(jù)進(jìn)行相應(yīng)處理;其中進(jìn)行了輸入數(shù)據(jù)的合并;在執(zhí)行中多個(gè)關(guān)鍵點(diǎn)對(duì)取消進(jìn)行校驗(yàn)處理
- WorkForegroundRunnable:啟動(dòng)前臺(tái)服務(wù),這個(gè)需要在實(shí)現(xiàn)Work中提供通知信息(方法getForegroundInfoAsync);如果有加急處理,也是啟動(dòng)前臺(tái)服務(wù)
- ListenableWorker中,通過setProgressAsync可以設(shè)置進(jìn)度數(shù)據(jù);成功時(shí),doWork返回的結(jié)果中含有成功數(shù)據(jù)
- EnqueueRunnable:enqueueWorkWithPrerequisites方法中對(duì)重復(fù)任務(wù)依據(jù)策略進(jìn)行了處理
- CancelWorkRunable:iterativelyCancelWorkAndDependents刪除了依賴其的任務(wù)
- 中間大量使用SettableFuture來讓各個(gè)Runnable執(zhí)行等待其執(zhí)行的先決條件
3.3.2 cancel流程
取消時(shí),可以通過任務(wù)的id、name、tag或者取消所有;不管是哪種調(diào)用,都是通過標(biāo)識(shí)獲取滿足條件的任務(wù)id集合,并進(jìn)行取消;其流程也只是在id集合查詢時(shí)不同而已
流程圖

- WorkWrapper進(jìn)行打斷時(shí),其流程也就是任務(wù)執(zhí)行時(shí)打斷處理,在執(zhí)行過程中多處對(duì)打斷進(jìn)行了檢測處理
- 取消時(shí),對(duì)調(diào)度、執(zhí)行中的任務(wù)分別進(jìn)行取消;同時(shí)同步狀態(tài)到數(shù)據(jù)庫
4、總結(jié)
上面只是大概介紹了常規(guī)使用,以及相關(guān)類圖、流程,代碼細(xì)節(jié)并沒有展示。代碼中的一些技術(shù)點(diǎn)需要至少會(huì)用
- AlarmMananger
- JobScheduler
- 電量低、內(nèi)存不足、充電狀態(tài)、網(wǎng)絡(luò)狀態(tài)檢測以及實(shí)時(shí)更新
- SettableFuture
- ......
從代碼架構(gòu)上,也有需要去多思考的地方
- 各個(gè)部分的分離抽象
- 廣播、服務(wù)組件的打開關(guān)閉、以及服務(wù)的銷毀、任務(wù)執(zhí)行完畢后的回調(diào)收尾等性能的考量
- 數(shù)據(jù)庫表的設(shè)計(jì)
- 不論哪個(gè)版本,均有兩個(gè)調(diào)度器去執(zhí)行任務(wù),他們有可能去處理同一個(gè)任務(wù),這帶來的復(fù)雜度處理邏輯以及優(yōu)勢(shì)
- ......
以上就是Android WorkManager使用以及源碼分析的詳細(xì)內(nèi)容,更多關(guān)于Android WorkManager的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Android 通過API獲取數(shù)據(jù)庫中的圖片文件方式
- Android 動(dòng)態(tài)添加view或item并獲取數(shù)據(jù)的實(shí)例
- Android WorkManager實(shí)現(xiàn)后臺(tái)定時(shí)任務(wù)流程詳解
- Android?Jetpack庫重要組件WorkManager的使用
- Android開發(fā)Jetpack組件WorkManager用例詳解
- Android使用Kotlin API實(shí)踐WorkManager
- Android WorkManager淺談
- android kotlin集成WorkManager實(shí)現(xiàn)定時(shí)獲取數(shù)據(jù)的步驟
相關(guān)文章
Android開發(fā)自學(xué)筆記(六):聲明權(quán)限和Activity
這篇文章主要介紹了Android開發(fā)自學(xué)筆記(六):聲明權(quán)限和Activity,本文是上一篇的補(bǔ)充,需要的朋友可以參考下2015-04-04
Android啟動(dòng)頁設(shè)置及動(dòng)態(tài)權(quán)限跳轉(zhuǎn)問題解決
在我遇到這個(gè)實(shí)際問題之前,我一直認(rèn)為啟動(dòng)頁的作用是美化產(chǎn)品,提升軟件逼格。但實(shí)際上,它更重要的是起到了一個(gè)攔截器的作用,這篇文章主要介紹了Android啟動(dòng)頁設(shè)置以及動(dòng)態(tài)權(quán)限跳轉(zhuǎn),需要的朋友可以參考下2022-04-04
Flutter使用JsBridge方式處理Webview與H5通信的方法
這篇文章主要介紹了Flutter使用JsBridge方式處理Webview與H5通信的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
Android使用 Spinner控件實(shí)現(xiàn)下拉框功能
Spinner是android的一種控件,用它我們可以實(shí)現(xiàn)下拉框。下面通過實(shí)例代碼給大家介紹Android使用 Spinner控件實(shí)現(xiàn)下拉框功能,感興趣的朋友一起看看吧2018-08-08
Android自定義一個(gè)圖形單點(diǎn)移動(dòng)縮小的效果
本文通過實(shí)例代碼給大家介紹了android 自定義圖形單點(diǎn)移動(dòng)縮小效果,代碼簡單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的的朋友參考下吧2017-08-08
源碼淺析Android中內(nèi)存泄漏檢測工具Leakcanary的使用
大名鼎鼎的 Leakcanary 想必作為 Android 開發(fā)都多多少少接觸過,新版本的 Leakcanary 也用 Kotlin 重寫了一遍,最近詳細(xì)查看了下源碼,就來和大家簡單分享一下2023-04-04
Android開發(fā)中怎樣調(diào)用系統(tǒng)Email發(fā)送郵件(多種調(diào)用方式)
在Android中調(diào)用其他程序進(jìn)行相關(guān)處理,幾乎都是使用的Intent,所以,Email也不例外,所謂的調(diào)用Email,只是說Email可以接收Intent并做這些事情2013-06-06

