WorkManager是干啥的胆敞。
WorkManager is the recommended library for persistent work. Scheduled work is guaranteed to execute sometime after its [Constraints](https://developer.android.google.cn/reference/androidx/work/Constraints)
are met. WorkManager allows observation of work status and the ability to create complex chains of work.
從上面可以知道:workmanager是一個(gè)google 推薦的library 用來(lái)執(zhí)行work的卧波,他有一些特點(diǎn)
- persistent 穩(wěn)定(開機(jī)之后 條件滿足后自動(dòng)執(zhí)行)
- Constraints 有約束條件
- observation of work status 狀態(tài)可以被監(jiān)聽
- create complex chains of work 可以合并鏈?zhǔn)降慕M合工作
WorkManager 的使用
使用從 添加依賴跪削、任務(wù)創(chuàng)建和開啟育谬、組合任務(wù)、任務(wù)監(jiān)聽和任務(wù)取消介紹任務(wù)的使用纳胧。
- 添加依賴
app 的build.gradle 添加依賴
def work_version = "2.7.1"
// (Java only)
implementation "androidx.work:work-runtime:$work_version"
- 普通任務(wù)創(chuàng)建和 開啟
1 創(chuàng)建任務(wù) 創(chuàng)建一個(gè)class CWorker 繼承與Worker,任務(wù)都是在 doWork里面執(zhí)行挂捅,return 可以是Result.success Result.failer,Result.retry.
public class CWork extends Worker {
public CWork(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Result.success();
}
}
2 開啟任務(wù)
開啟任務(wù)需要生成一個(gè)WorkRequest ,然后workmanager執(zhí)行enqueue就開始執(zhí)行了护盈。
// 普通任務(wù) 開啟
private void playCommon(){
// 1 生成 WorkRequest
OneTimeWorkRequest aWorkRequest = new OneTimeWorkRequest.Builder(AWork.class).build();
// 2 workManager enqueue
WorkManager.getInstance(this).enqueue(aWorkRequest);
}
- 延時(shí)任務(wù)
workrequest 添加initialDelay參數(shù)就會(huì)延時(shí)執(zhí)行
private void playDelay(){
// 參數(shù)1 是時(shí)長(zhǎng) 參數(shù)2 是單位
OneTimeWorkRequest aWorkRequest = new OneTimeWorkRequest.Builder(AWork.class)
.setInitialDelay(1000, TimeUnit.MILLISECONDS) // 延時(shí)時(shí)間執(zhí)行
.build();
WorkManager.getInstance(this).enqueue(aWorkRequest);
}
- 條件任務(wù)
任務(wù)可以添加約束條件挟纱,例如網(wǎng)絡(luò)約束(無(wú)網(wǎng)絡(luò),聯(lián)網(wǎng)腐宋,wifi紊服,計(jì)費(fèi)流量等)、保持電量充足的時(shí)候執(zhí)行胸竞、在充電的時(shí)候執(zhí)行欺嗤、storage不足的時(shí)候不執(zhí)行、待機(jī)下執(zhí)行等約束條件撤师。
不滿足條件的時(shí)候work 就會(huì)被block剂府,滿足條件就會(huì)執(zhí)行。
private void playConstraints(){
@SuppressLint("IdleBatteryChargingConstraints") Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.NOT_REQUIRED)//設(shè)置網(wǎng)絡(luò)情況 例如 wifi 聯(lián)網(wǎng) 計(jì)費(fèi)流量等
.setRequiresBatteryNotLow(true)//設(shè)置在電量不足的情況下不執(zhí)行
.setRequiresCharging(false)//在充電時(shí)執(zhí)行
.setRequiresStorageNotLow(true)//設(shè)置在storage不足的情況下不執(zhí)行
.setRequiresDeviceIdle(false)//在待機(jī)情況下執(zhí)行 非待機(jī)下不執(zhí)行
.build();
OneTimeWorkRequest aWorkRequest = new OneTimeWorkRequest.Builder(AWork.class)
.setConstraints(constraints) // 設(shè)置條件
.build();
WorkManager instance = WorkManager.getInstance(this);
instance.enqueue(aWorkRequest);
}
- retry 任務(wù)
retry 任務(wù)需要work 的dowork 方法返回值Result 的retry剃盾。才會(huì)再次執(zhí)行腺占,而且是當(dāng)retry的時(shí)候執(zhí)行了新的work,這個(gè)times 的值非static 的時(shí)候每次都是初始值。
public class RetryWork extends Worker {
private String TAG = "RetryWork";
public static int times = 0;
public RetryWork(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@SuppressLint("RestrictedApi")
@NonNull
@Override
public Result doWork() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (times >= 2) {
return Result.success();
}else {
times++;
Log.d(TAG,"times :"+times);
return Result.retry();
}
}
}
retry 的時(shí)候 setBackoffCriteria 可以設(shè)置retry 的時(shí)間和類型痒谴,
BackoffPolicy.LINEAR 是線性每次間隔時(shí)間一樣衰伯,BackoffPolicy.EXPONENTIAL 每次間隔時(shí)間會(huì)遞增。
@RequiresApi(api = Build.VERSION_CODES.O)
private void playRetry(){
// 1 work return Result.retry()
OneTimeWorkRequest aWorkRequest = new OneTimeWorkRequest.Builder(RetryWork.class)
.setBackoffCriteria(BackoffPolicy.LINEAR, Duration.ofSeconds(10)) // 重試設(shè)置10s后重試 線性間隔
// .setBackoffCriteria(BackoffPolicy.EXPONENTIAL,Duration.ofSeconds(10))// 重試設(shè)置10s后重試 線性間隔
.build();
WorkManager instance = WorkManager.getInstance(this);
instance.enqueue(aWorkRequest);
}
- 周期任務(wù)
PeriodicWorkRequest 和 OnetimeWorkRequest 類型相反积蔚,是周期性任務(wù)意鲸。
周期任務(wù)區(qū)分為 設(shè)置 flex 時(shí)間和 不設(shè)置flex 時(shí)間兩種。
1 不設(shè)置flex時(shí)間 每次間隔這段時(shí)間執(zhí)行一次
三參數(shù) (AWork.class,30,TimeUnit.MINUTES)
參數(shù)1 work
參數(shù)2 時(shí)間間隔
參數(shù)3 時(shí)間單位
2 設(shè)置flex時(shí)間(AWork.class,30,TimeUnit.MINUTES,15,TimeUnit.MINUTES)
第四個(gè)參數(shù)和第五個(gè)參數(shù)設(shè)置flex時(shí)間
flex時(shí)間 需要少于 間隔時(shí)間。
[ before flex | flex ][ before flex | flex ]...
間隔時(shí)間被分為 before flex 和flex 時(shí)間段怎顾,任務(wù)的執(zhí)行在flex時(shí)間段里面读慎。
// 周期任務(wù)
private void playPeriod(){
//
// PeriodicWorkRequest aWorkRequest = new PeriodicWorkRequest.Builder(AWork.class,30,TimeUnit.MINUTES)
// .build();
// [ before flex | flex ][ before flex | flex ]...
// [ cannot run work | can run work ][ cannot run work | can run work ]...
PeriodicWorkRequest aWorkRequest = new PeriodicWorkRequest.Builder(AWork.class,30,TimeUnit.MINUTES,15,TimeUnit.MINUTES)
.build();
WorkManager instance = WorkManager.getInstance(this);
instance.enqueue(aWorkRequest);
}
- 任務(wù)添加標(biāo)記
任務(wù)添加標(biāo)記,此標(biāo)記用來(lái)搜索標(biāo)記取消等可以使用槐雾。
private void playTag(){
// 參數(shù)1 是時(shí)長(zhǎng) 參數(shù)2 是單位
OneTimeWorkRequest aWorkRequest = new OneTimeWorkRequest.Builder(AWork.class)
.addTag("tag") // 標(biāo)記 主要用來(lái)查詢
.build();
WorkManager.getInstance(this).enqueue(aWorkRequest);
}
- 任務(wù)參數(shù)
1 給任務(wù)傳參數(shù)
setInputData 給任務(wù)傳參數(shù)
work 里面通過 getInputData 獲取參數(shù)
2 從任務(wù)傳出結(jié)果帶參數(shù)
Result.success(data);或者 Result.failer(data) 可以設(shè)置結(jié)果夭委。
@NonNull
@Override
public Result doWork() {
Data inputData = getInputData();
String param = inputData.getString("param1");
Log.d(TAG,"doWork start param:"+param);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Data.Builder builder = new Data.Builder();
@SuppressLint("RestrictedApi")
Data data = builder.put("key", "work A").build();
Log.d(TAG,"doWork end");
return Result.success(data);
}
// 傳參
private void playParam(){
// 1 傳入 work 2 work 傳出結(jié)果
Data data = new Data.Builder()
.putString("param","input")
.build();
OneTimeWorkRequest aWorkRequest = new OneTimeWorkRequest.Builder(AWork.class)
.setInputData(data) // 傳遞的數(shù)據(jù)
.build();
WorkManager instance = WorkManager.getInstance(this);
instance.enqueue(aWorkRequest);
instance.getWorkInfoByIdLiveData(aWorkRequest.getId()).observe(this, new Observer<WorkInfo>() {
@Override
public void onChanged(WorkInfo workInfo) {
workInfo.getOutputData();
}
});
}
- 任務(wù)進(jìn)度設(shè)置
work 里面設(shè)置進(jìn)度 setProgressAsync 設(shè)置
@NonNull
@Override
public Result doWork() {
setProgressAsync(new Data.Builder().putInt(PROGRESS, 0).build());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
setProgressAsync(new Data.Builder().putInt(PROGRESS, 100).build());
Log.d(TAG,"doWork end");
return Result.success();
}
LiveData 監(jiān)聽里面獲取到進(jìn)度
//set play progress
private void setPlayProgress(){
···
instance.getWorkInfoByIdLiveData(aWorkRequest.getId()).observe(this, new Observer<WorkInfo>() {
@Override
public void onChanged(WorkInfo workInfo) {
Data progress = workInfo.getProgress();// 進(jìn)度
}
});
}
- unique 任務(wù)
特殊任務(wù),同樣的名字的特殊任務(wù)會(huì)有沖突募强,可以設(shè)置沖突的解決方式株灸。
ExistingWorkPolicy (REPLACE,KEEP擎值,APPEND慌烧,APPEND_OR_REPLACE)
private void playUnique(){
WorkManager instance = WorkManager.getInstance(this);
OneTimeWorkRequest aWorkRequest = new OneTimeWorkRequest.Builder(AWork.class)
.build();
// ExistingWorkPolicy
// ?REPLACE: 用新工作替換現(xiàn)有工作。此選項(xiàng)將取消現(xiàn)有工作鸠儿。
// ?KEEP: 保留現(xiàn)有工作屹蚊,并忽略新工作。
// ?APPEND: 將新工作附加到現(xiàn)有工作的末尾捆交。此選項(xiàng)會(huì)將新工作鏈接到現(xiàn)有工作淑翼,并在現(xiàn)有工作完成后運(yùn)行。并且現(xiàn)有工作將成為新工作的先決條件品追。如果現(xiàn)有工作為CANCELLED或者FAILED狀態(tài)玄括,新工作也會(huì)變?yōu)镃ANCELLED或者FAILED狀態(tài)。
// ?APPEND_OR_REPLACE: 此選項(xiàng)類似于APPEND肉瓦,不過它不依賴于現(xiàn)有工作的狀態(tài)遭京。即使現(xiàn)有工作為CANCELLED或者FAILED狀態(tài),新工作仍舊會(huì)運(yùn)行泞莉。
WorkContinuation uniqye_a = instance.beginUniqueWork("uniqye_A", ExistingWorkPolicy.APPEND, aWorkRequest);
uniqye_a.enqueue();
}
組合任務(wù)
1 通過 workmanager 的方法 beginWith (works).then(works)進(jìn)行哪雕,里面的works 可以是單參數(shù)和list。
/**
* 2 合并A和B 然后執(zhí)行C
*/
private void multipleWork(){
OneTimeWorkRequest aWork = new OneTimeWorkRequest.Builder(AWork.class)
.addTag("mul") // 設(shè)置 TAG// .setExpedited()
.build();
OneTimeWorkRequest BWork = new OneTimeWorkRequest.Builder(BWork.class)
.addTag("mul") // 設(shè)置 TAG// .setExpedited()
.build();
OneTimeWorkRequest CWork = new OneTimeWorkRequest.Builder(CWork.class)
.addTag("mul") // 設(shè)置 TAG// .setExpedited()
.build();
WorkManager instance = WorkManager.getInstance(this);
instance.cancelAllWork();
instance.beginWith(Arrays.asList(aWork,BWork)).then(CWork).enqueue();
instance.getWorkInfosByTagLiveData("mul").observe(this, new Observer<List<WorkInfo>>() {
@Override
public void onChanged(List<WorkInfo> workInfos) {
for (WorkInfo workInfo : workInfos) {
Log.d(TAG,"onChanged workInfo:"+workInfo);
}
}
});
}
2 組合任務(wù)的組合
通過 WorkContinuation 復(fù)雜組合任務(wù)
/**
* 2 任務(wù)1 合并A和B 然后執(zhí)行C
* 任務(wù)2 合并D和E 然后執(zhí)行F
* 任務(wù)1 和2 同時(shí)
*/
@SuppressLint("EnqueueWork")
private void multipleWork2(){
OneTimeWorkRequest aWork = new OneTimeWorkRequest.Builder(AWork.class)
.addTag("mul") // 設(shè)置 TAG// .setExpedited()
.build();
OneTimeWorkRequest BWork = new OneTimeWorkRequest.Builder(BWork.class)
.addTag("mul") // 設(shè)置 TAG// .setExpedited()
.build();
OneTimeWorkRequest CWork = new OneTimeWorkRequest.Builder(CWork.class)
.addTag("mul") // 設(shè)置 TAG// .setExpedited()
.build();
OneTimeWorkRequest dWork = new OneTimeWorkRequest.Builder(AWork.class)
.addTag("mul") // 設(shè)置 TAG// .setExpedited()
.build();
OneTimeWorkRequest eWork = new OneTimeWorkRequest.Builder(BWork.class)
.addTag("mul") // 設(shè)置 TAG// .setExpedited()
.build();
OneTimeWorkRequest fWork = new OneTimeWorkRequest.Builder(CWork.class)
.addTag("mul") // 設(shè)置 TAG// .setExpedited()
.build();
WorkManager instance = WorkManager.getInstance(this);
instance.cancelAllWork();
WorkContinuation work1 = instance.beginWith(Arrays.asList(aWork, BWork)).then(CWork);
WorkContinuation work2 = instance.beginWith(Arrays.asList(eWork, dWork)).then(fWork);
WorkContinuation.combine(Arrays.asList(work1,work2)).enqueue();
}
搜索任務(wù)
通過 id,tag鲫趁,uniquename,斯嚎,searchRequest (組合搜索 id,tag,state,uniquename)
private void searchWork(){
// 1 id
WorkManager instance = WorkManager.getInstance(this);
ListenableFuture<WorkInfo> workInfoById = instance.getWorkInfoById();
WorkInfo workInfo= workInfoById.get();
// 2 通過tag
instance.getWorkInfosByTag("");
// 3 通過uniqueName
instance.getWorkInfosForUniqueWork("");
// 4 通過組合條件
WorkQuery.Builder query = WorkQuery.Builder.fromIds(Arrays.asList(UUID.fromString("ss")));
WorkQuery.Builder.fromTags(Arrays.asList("tag1","tag2","tag3"));
WorkQuery.Builder.fromStates(Arrays.asList(WorkInfo.State.BLOCKED,WorkInfo.State.FAILED));
WorkQuery.Builder.fromUniqueWorkNames(Arrays.asList("name1","name2"));
// 上面是 單獨(dú)某一個(gè)類型去查詢
// 下面是根據(jù) && 類型去添加
// query.addIds();
// query.addTags();
// query.addStates();
// query.addUniqueWorkNames();
instance.getWorkInfos(query.build());
}
WorkQuery搜索是通過搜索數(shù)據(jù)庫(kù)搜索的
query.addIds addTags addStates 等方法都是&& 條件搜索的。
public static SupportSQLiteQuery workQueryToRawQuery(@NonNull WorkQuery querySpec) {
List<Object> arguments = new ArrayList<>();
StringBuilder builder = new StringBuilder("SELECT * FROM workspec");
String conjunction = " WHERE";
List<WorkInfo.State> states = querySpec.getStates();
if (!states.isEmpty()) {
List<Integer> stateIds = new ArrayList<>(states.size());
for (WorkInfo.State state : states) {
stateIds.add(WorkTypeConverters.stateToInt(state));
}
builder.append(conjunction)
.append(" state IN (");
bindings(builder, stateIds.size());
builder.append(")");
arguments.addAll(stateIds);
conjunction = " AND";
}
List<UUID> ids = querySpec.getIds();
if (!ids.isEmpty()) {
List<String> workSpecIds = new ArrayList<>(ids.size());
for (UUID id : ids) {
workSpecIds.add(id.toString());
}
builder.append(conjunction)
.append(" id IN (");
bindings(builder, ids.size());
builder.append(")");
arguments.addAll(workSpecIds);
conjunction = " AND";
}
List<String> tags = querySpec.getTags();
if (!tags.isEmpty()) {
builder.append(conjunction)
.append(" id IN (SELECT work_spec_id FROM worktag WHERE tag IN (");
bindings(builder, tags.size());
builder.append("))");
arguments.addAll(tags);
conjunction = " AND";
}
List<String> uniqueWorkNames = querySpec.getUniqueWorkNames();
if (!uniqueWorkNames.isEmpty()) {
builder.append(conjunction)
.append(" id IN (SELECT work_spec_id FROM workname WHERE name IN (");
bindings(builder, uniqueWorkNames.size());
builder.append("))");
arguments.addAll(uniqueWorkNames);
conjunction = " AND";
}
builder.append(";");
return new SimpleSQLiteQuery(builder.toString(), arguments.toArray());
}
監(jiān)聽任務(wù)
添加搜索的任務(wù)
//
private void addListener(){
WorkManager instance = WorkManager.getInstance(this);
// 通過查詢 liveData 然后 observer
instance.getWorkInfoByIdLiveData("").observe(this, new Observer<WorkInfo>() {
@Override
public void onChanged(WorkInfo workInfo) {
}
});
instance.getWorkInfosByTagLiveData("tag").observe(this, new Observer<List<WorkInfo>>() {
@Override
public void onChanged(List<WorkInfo> workInfos) {
}
});
}
取消任務(wù)
根據(jù) id tag uniquename 等取消
// cancel
private void cancelWork(){
WorkManager instance = WorkManager.getInstance(this);
// 通過查詢 liveData 然后 observer
instance.cancelAllWork();
instance.cancelAllWorkByTag();
instance.cancelWorkById();
instance.cancelUniqueWork();
}
工作原理
WorkManager 任務(wù)的穩(wěn)定性和條件滿足的 原理兩方面去分析
開機(jī)啟動(dòng)自啟動(dòng)任務(wù)
bootComplate 廣播接收
<receiver
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>
public class RescheduleReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
try {
WorkManagerImpl workManager = WorkManagerImpl.getInstance(context);
final PendingResult pendingResult = goAsync();
workManager.setReschedulePendingResult(pendingResult);
} catch (IllegalStateException e) {
}
} else {
Intent reschedule = CommandHandler.createRescheduleIntent(context);
context.startService(reschedule);
}
}
}
WorkManagerImpl 里面 初始化
public static @NonNull WorkManagerImpl getInstance(@NonNull Context context) {
synchronized (sLock) {
WorkManagerImpl instance = getInstance();
if (instance == null) {
Context appContext = context.getApplicationContext();
if (appContext instanceof Configuration.Provider) {
initialize(
appContext,
((Configuration.Provider) appContext).getWorkManagerConfiguration());
instance = getInstance(appContext);
} else {
}
}
return instance;
}
}
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
···
if (sDelegatedInstance == null) {
context = context.getApplicationContext();
if (sDefaultInstance == null) { // 初始化
sDefaultInstance = new WorkManagerImpl(
context,
configuration,
new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
}
sDelegatedInstance = sDefaultInstance;
}
}
}
public WorkManagerImpl(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
@NonNull WorkDatabase database) {
···
List<Scheduler> schedulers =
createSchedulers(applicationContext, configuration, workTaskExecutor);
Processor processor = new Processor(
context,
configuration,
workTaskExecutor,
database,
schedulers);
internalInit(context, configuration, workTaskExecutor, database, schedulers, processor); // 初始化
}
private void internalInit(···) {
···
// Checks for app force stops.
mWorkTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));
}
調(diào)用到 ForceStopRunnable run
@Override
public void run() {
try {
if (!multiProcessChecks()) {
return;
}
while (true) {
···
try {
forceStopRunnable();
break;
} catch (Exception exception) {
mRetryCount++;
···
}
} finally {
mWorkManager.onForceStopRunnableCompleted();
}
}
@VisibleForTesting
public void forceStopRunnable() {
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."); // 調(diào)用到這里
Schedulers.schedule(
mWorkManager.getConfiguration(),
mWorkManager.getWorkDatabase(),
mWorkManager.getSchedulers());
}
}
Schedulers 的schedule 這里
public static void schedule(
@NonNull Configuration configuration,
@NonNull WorkDatabase workDatabase,
List<Scheduler> schedulers) {
···
WorkSpecDao workSpecDao = workDatabase.workSpecDao();
List<WorkSpec> eligibleWorkSpecsForLimitedSlots;
List<WorkSpec> allEligibleWorkSpecs;
workDatabase.beginTransaction();
try {
// Enqueued workSpecs when scheduling limits are applicable.
eligibleWorkSpecsForLimitedSlots = workSpecDao.getEligibleWorkForScheduling(
configuration.getMaxSchedulerLimit());
// Enqueued workSpecs when scheduling limits are NOT applicable.
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);
}
}
workDatabase.setTransactionSuccessful();
} finally {
workDatabase.endTransaction();
}
if (eligibleWorkSpecsForLimitedSlots != null
&& eligibleWorkSpecsForLimitedSlots.size() > 0) {
WorkSpec[] eligibleWorkSpecsArray =
new WorkSpec[eligibleWorkSpecsForLimitedSlots.size()];
eligibleWorkSpecsArray =
eligibleWorkSpecsForLimitedSlots.toArray(eligibleWorkSpecsArray);
// Delegate to the underlying schedulers.
for (Scheduler scheduler : schedulers) {
if (scheduler.hasLimitedSchedulingSlots()) { // SystemJobScheduler
scheduler.schedule(eligibleWorkSpecsArray);
}
}
}
if (allEligibleWorkSpecs != null && allEligibleWorkSpecs.size() > 0) {
WorkSpec[] enqueuedWorkSpecsArray = new WorkSpec[allEligibleWorkSpecs.size()];
enqueuedWorkSpecsArray = allEligibleWorkSpecs.toArray(enqueuedWorkSpecsArray);
// Delegate to the underlying schedulers.
for (Scheduler scheduler : schedulers) {
if (!scheduler.hasLimitedSchedulingSlots()) { // 這里調(diào)用的時(shí)GreedyScheduler 的schedule 方法 GreedyScheduler 是在 WoekManager 的createSchedulers 添加的
scheduler.schedule(enqueuedWorkSpecsArray);
}
}
}
}
eligibleWorkSpecsForLimitedSlots 列表是有限制的 或者周期性的worklist沒執(zhí)行的list挨厚。
他會(huì)進(jìn)入到 SystemJobScheduler 里面去執(zhí)行 堡僻。
schedule{
scheduleInternal(workSpec, jobId);
}
//mJobScheduler (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE),
scheduleInternal{
int result = mJobScheduler.schedule(jobInfo);
}
到條件后會(huì)調(diào)用到 SystemJobService 的 onStartJob
@Override
public boolean onStartJob(@NonNull JobParameters params) {
···
// We don't need to worry about the case where JobParams#isOverrideDeadlineExpired()
// returns true. This is because JobScheduler ensures that for PeriodicWork, constraints
// are actually met irrespective.
Logger.get().debug(TAG, String.format("onStartJob for %s", workSpecId));
mJobParameters.put(workSpecId, params);
}
WorkerParameters.RuntimeExtras runtimeExtras = null;
if (Build.VERSION.SDK_INT >= 24) {
runtimeExtras = new WorkerParameters.RuntimeExtras();
if (params.getTriggeredContentUris() != null) {
runtimeExtras.triggeredContentUris =
Arrays.asList(params.getTriggeredContentUris());
}
if (params.getTriggeredContentAuthorities() != null) {
runtimeExtras.triggeredContentAuthorities =
Arrays.asList(params.getTriggeredContentAuthorities());
}
if (Build.VERSION.SDK_INT >= 28) {
runtimeExtras.network = params.getNetwork();
}
}
// It is important that we return true, and hang on this onStartJob() request.
// The call to startWork() may no-op because the WorkRequest could have been picked up
// by the GreedyScheduler, and was already being executed. GreedyScheduler does not
// handle retries, and the Processor notifies all Schedulers about an intent to reschedule.
// In such cases, we rely on SystemJobService to ask for a reschedule by calling
// jobFinished(params, true) in onExecuted(...);
// For more information look at b/123211993
mWorkManagerImpl.startWork(workSpecId, runtimeExtras);
return true;
}
GreedyScheduler schedule 方法 沒有延時(shí) 沒有限制條件的直接 startwork 。 帶延時(shí)直接延時(shí)處理疫剃。有限制條件的放到 限制條件的列表里面钉疫,后面條件任務(wù)里面會(huì)說(shuō)明。
@Override
public void schedule(@NonNull WorkSpec... workSpecs) {
if (mInDefaultProcess == null) {
checkDefaultProcess();
}
if (!mInDefaultProcess) {
Logger.get().info(TAG, "Ignoring schedule request in a secondary 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.
Set<WorkSpec> constrainedWorkSpecs = new HashSet<>();
Set<String> constrainedWorkSpecIds = new HashSet<>();
for (WorkSpec workSpec : workSpecs) {
long nextRunTime = workSpec.calculateNextRunTime();
long now = System.currentTimeMillis();
if (workSpec.state == WorkInfo.State.ENQUEUED) {
if (now < nextRunTime) {
// Future work
if (mDelayedWorkTracker != null) {
mDelayedWorkTracker.schedule(workSpec);
}
} else 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 {
mWorkManagerImpl.startWork(workSpec.id);
}
}
}
synchronized (mLock) {
if (!constrainedWorkSpecs.isEmpty()) {
mConstrainedWorkSpecs.addAll(constrainedWorkSpecs);
mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);
}
}
}
條件任務(wù)例如網(wǎng)絡(luò)低電量等條件任務(wù)
workmanager 的依賴?yán)锩?manifest 里面添加的有各種條件的廣播接收receiver巢价。
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy"
···>
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
</intent-filter>
</receiver>
<receiver
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>
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$StorageNotLowProxy"
··· >
<intent-filter>
<action android:name="android.intent.action.DEVICE_STORAGE_LOW" />
<action android:name="android.intent.action.DEVICE_STORAGE_OK" />
</intent-filter>
</receiver>
<receiver
android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$NetworkStateProxy"
··· >
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
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);
}
···
static Intent createConstraintsChangedIntent(@NonNull Context context) {
Intent intent = new Intent(context, SystemAlarmService.class);
intent.setAction(ACTION_CONSTRAINTS_CHANGED);
return intent;
}
SystemAlarmService 里面
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if (mIsShutdown) {
Logger.get().info(TAG,
"Re-initializing SystemAlarmDispatcher after a request to shut-down.");
// Destroy the old dispatcher to complete it's lifecycle.
mDispatcher.onDestroy();
// Create a new dispatcher to setup a new lifecycle.
initializeDispatcher(); // 初始化 dispatcher
// Set mIsShutdown to false, to correctly accept new commands.
mIsShutdown = false;
}
if (intent != null) {
mDispatcher.add(intent, startId); / / 檢測(cè)
}
// If the service were to crash, we want all unacknowledged Intents to get redelivered.
return Service.START_REDELIVER_INTENT;
}
SystemAlarmDispatcher 里面
public boolean add(@NonNull final Intent intent, final int startId) {
···
(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(); // 檢測(cè)
}
}
return true;
}
@MainThread
@SuppressWarnings("FutureReturnValueIgnored")
private void processCommand() {
assertMainThread();
PowerManager.WakeLock processCommandLock =
WakeLocks.newWakeLock(mContext, PROCESS_COMMAND_TAG);
try {
processCommandLock.acquire();
// Process commands on the background thread.
mWorkManager.getWorkTaskExecutor().executeOnBackgroundThread(new Runnable() {
@Override
public void run() {
synchronized (mIntents) {
mCurrentIntent = mIntents.get(0);
}
if (mCurrentIntent != null) {
final String action = mCurrentIntent.getAction();
final int startId = mCurrentIntent.getIntExtra(KEY_START_ID,
DEFAULT_START_ID);
···
final PowerManager.WakeLock wakeLock = WakeLocks.newWakeLock(
mContext,
String.format("%s (%s)", action, startId));
try {
···
wakeLock.acquire();
mCommandHandler.onHandleIntent(mCurrentIntent, startId,
SystemAlarmDispatcher.this); // handler 處理message
} catch (Throwable throwable) {
···
} finally {
···
wakeLock.release();
···
postOnMainThread(
new DequeueAndCheckForCompletion(SystemAlarmDispatcher.this));
}
}
}
});
} finally {
processCommandLock.release();
}
}
CommonHandler onHandleIntent 再調(diào)用到 ConstraintsCommandHandler 的 onHandleIntent
@WorkerThread
void onHandleIntent(
@NonNull Intent intent,
int startId,
@NonNull SystemAlarmDispatcher dispatcher) {
String action = intent.getAction();
if (ACTION_CONSTRAINTS_CHANGED.equals(action)) { //條件變化 走到這里
handleConstraintsChanged(intent, startId, dispatcher);
} else if (ACTION_RESCHEDULE.equals(action)) {
handleReschedule(intent, startId, dispatcher);
} else {
Bundle extras = intent.getExtras();
if (!hasKeys(extras, KEY_WORKSPEC_ID)) {
Logger.get().error(TAG,
String.format("Invalid request for %s, requires %s.",
action,
KEY_WORKSPEC_ID));
} else {
if (ACTION_SCHEDULE_WORK.equals(action)) {
handleScheduleWorkIntent(intent, startId, dispatcher);
} else if (ACTION_DELAY_MET.equals(action)) {
handleDelayMet(intent, startId, dispatcher);
} else if (ACTION_STOP_WORK.equals(action)) {
handleStopWork(intent, dispatcher);
} else if (ACTION_EXECUTION_COMPLETED.equals(action)) {
handleExecutionCompleted(intent, startId);
} else {
Logger.get().warning(TAG, String.format("Ignoring intent %s", intent));
}
}
}
}
ConstraintsCommandHandler 里面
@WorkerThread
void handleConstraintsChanged() {
List<WorkSpec> candidates = mDispatcher.getWorkManager().getWorkDatabase()
.workSpecDao()
.getScheduledWork();
···
ConstraintProxy.updateAll(mContext, candidates);
// This needs to be done to populate matching WorkSpec ids in every constraint controller.
mWorkConstraintsTracker.replace(candidates);
List<WorkSpec> eligibleWorkSpecs = new ArrayList<>(candidates.size());
// Filter candidates should have already been scheduled.
long now = System.currentTimeMillis();
for (WorkSpec workSpec : candidates) {
String workSpecId = workSpec.id;
long triggerAt = workSpec.calculateNextRunTime();
if (now >= triggerAt && (!workSpec.hasConstraints()
|| mWorkConstraintsTracker.areAllConstraintsMet(workSpecId))) {
eligibleWorkSpecs.add(workSpec);
}
}
for (WorkSpec workSpec : eligibleWorkSpecs) {
String workSpecId = workSpec.id;
Intent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId); // ACTION_DELAY_MET
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)); // 里面 mDispatcher 牲阁。add
}
mWorkConstraintsTracker.reset();
}
}
//SystemAlarmDispatcher 里面
AddRunnable(@NonNull SystemAlarmDispatcher dispatcher,
@NonNull Intent intent,
int startId) {
mDispatcher = dispatcher;
mIntent = intent;
mStartId = startId;
}
@Override
public void run() {
mDispatcher.add(mIntent, mStartId);
}
又回到 dispacher 里面了 mDispatcher.add(mIntent, mStartId);
然后又到 commonHandler 里面的 handleConstraintsChanged
handleDelayMet(intent, startId, dispatcher);
}
之后DelayMetCommonHandler 里面處理
@WorkerThread
void handleProcessWork() {
mWakeLock = WakeLocks.newWakeLock(
mContext,
String.format("%s (%s)", mWorkSpecId, mStartId));
Logger.get().debug(TAG,
String.format("Acquiring wakelock %s for WorkSpec %s", mWakeLock, mWorkSpecId));
mWakeLock.acquire();
WorkSpec workSpec = mDispatcher.getWorkManager()
.getWorkDatabase()
.workSpecDao()
.getWorkSpec(mWorkSpecId);
// This should typically never happen. Cancelling work should remove alarms, but if an
// alarm has already fired, then fire a stop work request to remove the pending delay met
// command handler.
if (workSpec == null) {
stopWork();
return;
}
// Keep track of whether the WorkSpec had constraints. This is useful for updating the
// state of constraint proxies when onExecuted().
mHasConstraints = workSpec.hasConstraints();
if (!mHasConstraints) {
onAllConstraintsMet(Collections.singletonList(mWorkSpecId)); // 沒有限制 直接就work
} else {
// Allow tracker to report constraint changes
mWorkConstraintsTracker.replace(Collections.singletonList(workSpec)); // 判斷限制
}
}
WorkConstraintsTracker replace 方法
public void replace(@NonNull Iterable<WorkSpec> workSpecs) {
synchronized (mLock) {
for (ConstraintController<?> controller : mConstraintControllers) {
controller.setCallback(null);
}
for (ConstraintController<?> controller : mConstraintControllers) {
controller.replace(workSpecs);
}
for (ConstraintController<?> controller : mConstraintControllers) {
controller.setCallback(this);
}
}
}
ConstaintController
public void setCallback(@Nullable OnConstraintUpdatedCallback callback) {
if (mCallback != callback) {
mCallback = callback;
updateCallback(mCallback, mCurrentValue);
}
}
private void updateCallback(
@Nullable OnConstraintUpdatedCallback callback,
@Nullable T currentValue) {
// We pass copies of references (callback, currentValue) to updateCallback because public
// APIs on ConstraintController may be called from any thread, and onConstraintChanged() is
// called from the main thread.
if (mMatchingWorkSpecIds.isEmpty() || callback == null) {
return;
}
if (currentValue == null || isConstrained(currentValue)) {
callback.onConstraintNotMet(mMatchingWorkSpecIds); // 符合條件 callback 回去
} else {
callback.onConstraintMet(mMatchingWorkSpecIds);
}
}
滿足條件后 會(huì)掉到 WorkConstraintsTracker 里面 遍歷獲取條件滿足的 work之后 然后在回調(diào)到 DelayMetCommonHandler 里面的 onAllConstraintsMet 里面
// WorkConstraintsTracker
@Override
public void onConstraintMet(@NonNull List<String> workSpecIds) {
synchronized (mLock) {
List<String> unconstrainedWorkSpecIds = new ArrayList<>();
for (String workSpecId : workSpecIds) { // 遍歷尋找所有的滿足條件的work
if (areAllConstraintsMet(workSpecId)) {
Logger.get().debug(TAG, String.format("Constraints met for %s", workSpecId));
unconstrainedWorkSpecIds.add(workSpecId);
}
}
if (mCallback != null) {
mCallback.onAllConstraintsMet(unconstrainedWorkSpecIds); // 鬼掉到commonHandler 里面
}
}
}
// DelayMetCommonHandler 里面開啟工作
@Override
public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
// WorkConstraintsTracker will call onAllConstraintsMet with list of workSpecs whose
// constraints are met. Ensure the workSpecId we are interested is part of the list
// before we call Processor#startWork().
if (!workSpecIds.contains(mWorkSpecId)) {
return;
}
synchronized (mLock) {
if (mCurrentState == STATE_INITIAL) {
mCurrentState = STATE_START_REQUESTED;
Logger.get().debug(TAG, String.format("onAllConstraintsMet for %s", mWorkSpecId));
// Constraints met, schedule execution
// Not using WorkManagerImpl#startWork() here because we need to know if the
// processor actually enqueued the work here.
boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);
if (isEnqueued) {
// setup timers to enforce quotas on workers that have
// been enqueued
mDispatcher.getWorkTimer()
.startTimer(mWorkSpecId, WORK_PROCESSING_TIME_IN_MS, this);
} else {
// if we did not actually enqueue the work, it was enqueued before
// cleanUp and pretend this never happened.
cleanUp();
}
} else {
Logger.get().debug(TAG, String.format("Already started work for %s", mWorkSpecId));
}
}
}