——你可以失望,但不能絕望疹启。累的時候可以慢一點林说,千萬不要后退煎殷,你還沒有拼勁全力。怎么知道沒有奇跡腿箩。
前言
——最近抽空又學(xué)了一個Jetpack組件 —— WorkManager豪直,由于工作繁忙,要學(xué)的東西還有很多珠移,任務(wù)重弓乙,時間緊。雖然只學(xué)到了點皮毛钧惧,但是還是要花點時間做個總結(jié)暇韧。因為人們常說:學(xué)而不思則罔,思而不學(xué)則殆浓瞪。不思不學(xué)則網(wǎng)貸懈玻。所以要想致富,好的學(xué)習(xí)方法是必要的乾颁。也跟大家分享一下所學(xué)的知識涂乌。少走的點彎路艺栈。
一、簡介
(1)是什么
—— WorkManager是Android Jetpack 中管理后臺任務(wù)的組件湾盒。
—— 常見的使用場景:1.向后端服務(wù)發(fā)送日志或分析數(shù)據(jù) 2.定期將應(yīng)用數(shù)據(jù)與服務(wù)器同步
(2)有什么用
—— 使用 WorkManager API 可以輕松地調(diào)度后臺任務(wù)湿右。可延遲運(yùn)行(即不需要立即運(yùn)行)并且在應(yīng)用退出(進(jìn)程未關(guān)閉)或應(yīng)用重啟時能夠可靠運(yùn)行的任務(wù)罚勾。
(3)有什么優(yōu)點
- 1.兼容JobScheduler與BroadcastReceiver 和 AlarmManager
- 2.工作約束滿足多種情況
- 3.可使用一次性或周期性執(zhí)行的任務(wù)
- 4.監(jiān)控和管理計劃任務(wù)
- 5.提供API將任務(wù)鏈接起來
- 6.遵循低電耗模式等省電功能
二诅需、基本使用
(1)添加依賴
implementation android.arch.work:work-runtime:1.0.1
(2)創(chuàng)建后臺任務(wù)(自定義類 繼承 Worker 并重寫doWork())
public static class MyWorker extends Worker {
public MyWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}
@Override
public Result doWork() {
return Result.success();//返回成功
// return Result.failure();//返回失敗
// return Result.retry();//重試
}
}
(3)創(chuàng)建請求
// 對于一次性 WorkRequest,請使用 OneTimeWorkRequest荧库,對于周期性工作,請使用 PeriodicWorkRequest.
// 構(gòu)建一次性請求
// OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MyWorker.class).build();
// 構(gòu)建周期性請求
// PeriodicWorkRequest request = new PeriodicWorkRequest.Builder(MyWorker.class,1, TimeUnit.HOURS).build();
(4)執(zhí)行請求(如果沒有設(shè)置約束條件則會立即執(zhí)行)
WorkManager.getInstance().enqueue(request);
(5)取消和停止工作
WorkManager.getInstance().cancelWorkById(request.getId());
總結(jié):1.創(chuàng)建任務(wù)——2.配置請求——3.執(zhí)行請求
三赵刑、進(jìn)階
(1)進(jìn)階1:構(gòu)建約束條件:
Uri uri = Uri.parse("xxxxx");
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) //指定需要在有網(wǎng)的情況下
.setRequiresBatteryNotLow(true)//指定電量在可接受范圍內(nèi)運(yùn)行
.setRequiresStorageNotLow(true)//指定在存儲量在可接受范圍內(nèi)運(yùn)行
.addContentUriTrigger(uri,true)//當(dāng)Uri發(fā)生變化的時候運(yùn)行
.setRequiresDeviceIdle(true)//當(dāng)設(shè)備處于空閑狀態(tài)時運(yùn)行
.setRequiresCharging(true)//當(dāng)設(shè)備處于充電狀態(tài)時運(yùn)行
.build();
//在請求
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MyWorker.class)
.setConstraints(constraints)//添加約束
.build();
//當(dāng)滿足約束條件后才會執(zhí)行該任務(wù)
WorkManager.getInstance().enqueue(request);
(2)進(jìn)階2:延遲執(zhí)行
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MyWorker.class)
.setInitialDelay(1,TimeUnit.HOURS)//延遲1小時執(zhí)行
.build();
(3)進(jìn)階3:設(shè)置回退/重試的策略 當(dāng)doWork()返回 Result.retry()時啟用 指定重試間隔時長
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MyWorker.class)
//第一個參數(shù):設(shè)置策略模式分衫。
//第二個參數(shù):設(shè)置第一次重試時長
//第三個參數(shù):設(shè)置時間單位
.setBackoffCriteria(BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build();
(4)進(jìn)階4:傳入?yún)?shù)/標(biāo)記請求任務(wù)
Data imageData = new Data.Builder()
.putString(DateKey, "開始執(zhí)行")
.build();
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MyWorker.class)
//傳入?yún)?shù)
.setInputData(imageData)
.build();
@Override
public Result doWork() {
//獲取傳入的參數(shù)
String data = getInputData().getString(DateKey);
LogUtils.e("data:"+data);
//創(chuàng)建輸出結(jié)果
Data outputData = new Data.Builder()
.putString(DateKey,"已經(jīng)開始充電")
.build();
return Result.success(outputData);
}
(5)進(jìn)階5:標(biāo)記請求任務(wù)
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MyWorker.class)
.addTag(TAG)
.build();
//取消使用特定標(biāo)記的所有任務(wù)
// WorkManager.getInstance().cancelAllWorkByTag(TAG);
//會返回 LiveData 和具有該標(biāo)記的所有任務(wù)的狀態(tài)列表
// WorkManager.getInstance().getWorkInfosByTagLiveData(TAG);
(6)進(jìn)階6:監(jiān)聽工作狀態(tài)
WorkManager.getInstance().getWorkInfoByIdLiveData(request1.getId())
.observe(this, new Observer<WorkInfo>() {
@Override
public void onChanged(@Nullable WorkInfo workInfo) {
if (workInfo != null && (workInfo.getState() == WorkInfo.State.SUCCEEDED)){
//獲取成功返回的結(jié)果
tvText.setText(workInfo.getOutputData().getString(DateKey));
}
}
});
(7)進(jìn)階7:鏈接工作:用于指定多個關(guān)聯(lián)任務(wù)并定義這些任務(wù)的運(yùn)行順序(可以執(zhí)行多個任務(wù))
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MyWorker.class).build();
OneTimeWorkRequest request1 = new OneTimeWorkRequest.Builder(MyWorker.class).build();
OneTimeWorkRequest request2 = new OneTimeWorkRequest.Builder(MyWorker.class).build();
OneTimeWorkRequest request3 = new OneTimeWorkRequest.Builder(MyWorker.class).setInputMerger(OverwritingInputMerger.class).build();
OneTimeWorkRequest request4 = new OneTimeWorkRequest.Builder(MyWorker.class).build();
// 為了管理來自多個父級 OneTimeWorkRequest 的輸入,WorkManager 使用 InputMerger般此。
// WorkManager 提供兩種不同類型的 InputMerger:
// OverwritingInputMerger 會嘗試將所有輸入中的所有鍵添加到輸出中蚪战。如果發(fā)生沖突,它會覆蓋先前設(shè)置的鍵铐懊。
// ArrayCreatingInputMerger 會嘗試合并輸入邀桑,并在必要時創(chuàng)建數(shù)組。
WorkManager.getInstance()
//使用beginWith()可以并行執(zhí)行request科乎、request1壁畸、request2
.beginWith(Arrays.asList(request, request1, request2)).
//使用then()可以按順序執(zhí)行任務(wù)
.then(request3)//在執(zhí)行request3
.then(request4)//在執(zhí)行request4
.enqueue();
四、源碼分析
大體流程:
1.初始化時創(chuàng)建了WorkManager任務(wù)執(zhí)行器管理線程:里面創(chuàng)建了一個單線程池管理后臺任務(wù)與拿到主線程的handle執(zhí)行UI更新
2.在Worker封裝了一個線程茅茂,通過繼承方式把我們的后臺任務(wù)交給該線程
3.使用WorkRequest配置該任務(wù)線程的執(zhí)行條件
4.最終將WorkManager與WorkRequest綁定在一起捏萍。實際是把任務(wù)線程及配置信息交給WorkManager處理。
5.也就是調(diào)用了WorkManager任務(wù)執(zhí)行器來運(yùn)行線程與更新UI空闲。
@ 基于依賴implementation android.arch.work:work-runtime:1.0.1 源碼分析
(1)組件的初始化
WorkManager的初始化在ContentProvider中令杈,不需要手動添加。WorkManager是一個抽象類碴倾,它的大部分方法都是交給他的子類WorkManagerImpl實現(xiàn)的逗噩。
/**
* @Function workManager初始化
*/
public class WorkManagerInitializer extends ContentProvider {
@Override
public boolean onCreate() {
// Initialize WorkManager with the default configuration.
WorkManager.initialize(getContext(), new Configuration.Builder().build());
return true;
}
......
}
/**
* @Function WorkManager.initialize()最終使用單例模式創(chuàng)建WorkManagerImpl對象。
*/
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
WorkManagerImpl.initialize(context, configuration);
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
synchronized (sLock) {
...
if (sDelegatedInstance == null) {
context = context.getApplicationContext();
if (sDefaultInstance == null) {
//創(chuàng)建了WorkManagerImpl
sDefaultInstance = new WorkManagerImpl(
context,
configuration,
//創(chuàng)建了WorkManagerTaskExecutor
new WorkManagerTaskExecutor());
}
sDelegatedInstance = sDefaultInstance;
}
}
}
核心類:WorkManagerTaskExecutor :主要是管理后臺線程與UI線程的執(zhí)行跌榔。
//通過該類 我們可以執(zhí)行UI線程上的任務(wù)與后臺任務(wù)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class WorkManagerTaskExecutor implements TaskExecutor {
//獲取達(dá)到UI線程的handler
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
//創(chuàng)建一個Executor 綁定到UI線程上 再通過調(diào)用該Executor可以在UI線程上進(jìn)行操作
private final Executor mMainThreadExecutor = new Executor() {
@Override
public void execute(@NonNull Runnable command) {
postToMainThread(command);
}
};
@Override
public void postToMainThread(Runnable r) {
mMainThreadHandler.post(r);
}
...
//創(chuàng)建了一個單線程池 管理workManager的后臺線程
private final ExecutorService mBackgroundExecutor =
Executors.newSingleThreadExecutor(mBackgroundThreadFactory);
... //省略部分調(diào)用方法
}
接下去我們看下 核心類 :WorkManagerImpl
//按照執(zhí)行順序异雁,我們先看下它的構(gòu)造函數(shù) 做了哪些準(zhǔn)備工作。
public WorkManagerImpl(
@NonNull Context context,
@NonNull Configuration configuration,
@NonNull TaskExecutor workTaskExecutor,
boolean useTestDatabase) {
Context applicationContext = context.getApplicationContext();
// 創(chuàng)建了一個room 數(shù)據(jù)庫用于保存 任務(wù)線程的配置信息
WorkDatabase database = WorkDatabase.create(applicationContext, useTestDatabase);
Logger.setLogger(new Logger.LogcatLogger(configuration.getMinimumLoggingLevel()));
// 創(chuàng)建Scheduler根據(jù)返回一個List<Scheduler>,
//里面包含兩個Scheduler:GreedyScheduler僧须,SystemJobScheduler/SystemAlarmScheduler
List<Scheduler> schedulers = createSchedulers(applicationContext);
//建Processor片迅,Scheduler最后都調(diào)用Processor.startWork()去執(zhí)行Worker中的邏輯,也就是我們重寫的doWork()。
Processor processor = new Processor(
context,
configuration,
workTaskExecutor,
database,
schedulers);
//啟動APP時檢查APP是之前否強(qiáng)制停止退出或有未執(zhí)行完的任務(wù)皆辽,是的話重啟WorkManager柑蛇,保證任務(wù)可以繼續(xù)執(zhí)行芥挣。
internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
}
由于源碼太多這里就不一一摘錄了,小弟不才耻台,文采有限空免。寫不出通俗易懂的句子。大家將就看看大體過程就好盆耽。初始化階段就介紹到這里蹋砚。
回顧一下初始化過程:
1.首先創(chuàng)建了WorkManagerImpl類,并持有WorkManagerTaskExecutor類摄杂,該類是后臺線程與UI線程的主要執(zhí)行者坝咐。
2.在WorkManagerImpl構(gòu)造方法中創(chuàng)建了數(shù)據(jù)庫保存任務(wù)線程的信息,主要用于App重啟時保證任務(wù)可以繼續(xù)執(zhí)行析恢。
3.又創(chuàng)建了Schedulers墨坚,用來滿足不同條件的情況下執(zhí)行特定的任務(wù)。
4.啟動APP時從數(shù)據(jù)庫中獲取任務(wù)列表判斷是否由未執(zhí)行的任務(wù)映挂,并啟動 泽篮。保證在滿足條件的情況下可以繼續(xù)執(zhí)行。
分析到了這里柑船。我們就回發(fā)現(xiàn)這里還缺少一個主要的組成部分帽撑。那就是我們的任務(wù)。如何把我們的后臺任務(wù)交給workManager處理呢鞍时。這就是我們需要收到操作的部分亏拉。也就是我們使用WorkManger的過程。
(2)創(chuàng)建后臺任務(wù):Worker
//這是一個抽象類逆巍,所以需要自定義一個類來繼承該類并重寫 doWork()方法來編寫后臺任務(wù)
public abstract class Worker extends ListenableWorker {
...
//從該方法中可以看出dowork()在一個線程中執(zhí)行专筷。getBackgroundExecutor()則是調(diào)用了單線程池來管理該線程。
@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;
}
}
(3)配置后臺任務(wù)的執(zhí)行條件:WorkRequest
——WorkRequest配置后臺任務(wù)的執(zhí)行條件蒸苇,該類是一個抽象類磷蛹,有WorkManager有兩種具體的實現(xiàn)OneTimeWorkRequest/PeriodicWorkRequest。
new OneTimeWorkRequest.Builder(MyWorker.class)
.setConstraints(constraints)//添加約束
.setInitialDelay(1,TimeUnit.HOURS)//進(jìn)階2:延遲執(zhí)行
.setBackoffCriteria(BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)//進(jìn)階3:退避政策:當(dāng)doWork()返回 Result.retry()時 啟用
.setInputData(imageData)//進(jìn)階4:傳入?yún)?shù)
.addTag(TAG)//進(jìn)階4:標(biāo)記請求任務(wù)
.build();
//創(chuàng)建了配置信息類WorkSpec 溪烤,將執(zhí)行條件和參數(shù)都保存到WorkSpec中
public abstract static class Builder<B extends Builder, W extends WorkRequest> {
...
Builder(@NonNull Class<? extends ListenableWorker> workerClass) {
mId = UUID.randomUUID();
mWorkSpec = new WorkSpec(mId.toString(), workerClass.getName());
addTag(workerClass.getName());
}
public final @NonNull B setBackoffCriteria(
@NonNull BackoffPolicy backoffPolicy,
long backoffDelay,
@NonNull TimeUnit timeUnit) {
mBackoffCriteriaSet = true;
mWorkSpec.backoffPolicy = backoffPolicy;
mWorkSpec.setBackoffDelayDuration(timeUnit.toMillis(backoffDelay));
return getThis();
}
...
public final @NonNull B setConstraints(@NonNull Constraints constraints) {
mWorkSpec.constraints = constraints;
return getThis();
}
...
}
(4)執(zhí)行任務(wù)
// WorkManager.getInstance().enqueue(request1)
@Override
@NonNull
public Operation enqueue(
@NonNull List<? extends WorkRequest> workRequests) {
...
return new WorkContinuationImpl(this, workRequests).enqueue();
}
@Override
public @NonNull Operation enqueue() {
if (!mEnqueued) {
//調(diào)用單線程池執(zhí)行EnqueueRunnable 后面詳細(xì)分析下EnqueueRunnable
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;
}
//該線程會執(zhí)行run()方法 并執(zhí)行兩個重要的方法addToDatabase(), scheduleWorkInBackground();
public class EnqueueRunnable implements Runnable {
...
@Override
public void run() {
try {
if (mWorkContinuation.hasCycles()) {
throw new IllegalStateException(
String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
}
//將后臺任務(wù)及配置信息存到數(shù)據(jù)庫 并返回是否需要執(zhí)行任務(wù)
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));
}
}
//最后會啟用 初始化時創(chuàng)建的GreedyScheduler味咳,SystemJobScheduler/SystemAlarmScheduler等調(diào)度類來執(zhí)行工作.
@VisibleForTesting
public void scheduleWorkInBackground() {
WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
Schedulers.schedule(
workManager.getConfiguration(),
workManager.getWorkDatabase(),
workManager.getSchedulers());
}
更詳細(xì)的代碼就不貼了,大家要是腦補(bǔ)不了檬嘀。在按流程仔細(xì)看一遍源碼會了解的更深槽驶。
本想畫個圖加深一下印象,結(jié)果發(fā)現(xiàn)是個手殘黨 鸳兽,對不住大家 掂铐。
五、內(nèi)容推薦
- 《CSDN》《簡書》
- 《Android Jetpack架構(gòu)組件之Navigation入門》
- 《Android Jetpack架構(gòu)組件之Room入門及源碼分析》
- 《Android Jetpack架構(gòu)組件之LiveData》
- 《Android Jetpack架構(gòu)組件之Lifecycle源碼分析》
若您發(fā)現(xiàn)文章中存在錯誤或不足的地方,希望您能指出全陨!