一. 定義:
作爲 Android Jetpack 中的新組件,WorkManager 負責用來管理後臺任務,說簡單點則和異步任務Task或者 Service 作用一樣,都可以處理異步任務或後臺任務。
核心類介紹
先來介紹下WorkManager中涉及到的想關類
-
Worker
任務的執行者,是一個抽象類,需要繼承它實現要執行的任務。 -
WorkRequest
指定讓哪個 Woker 執行任務,指定執行的環境,執行的順序等。
要使用它的子類 OneTimeWorkRequest 或 PeriodicWorkRequest。 -
WorkManager
管理任務請求和任務隊列,發起的 WorkRequest 會進入它的任務隊列。 -
WorkStatus
包含有任務的狀態和任務的信息,以 LiveData 的形式提供給觀察者,更新相關UI
二、如何使用:
依賴,在 build.gradle 添加如下依賴:
//workmanager
api 'androidx.work:work-runtime:2.2.0'
- 第一步:自定義Work類,繼承Worker類,這裏以上傳文件爲示例
public class UploadFileWorker extends Worker {
public UploadFileWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
Data inputData = getInputData();
String filePath = inputData.getString("file");
String fileUrl = FileUploadManager.upload(filePath);
if (TextUtils.isEmpty(fileUrl)){
return Result.failure();
}else{
Data outputData = new Data.Builder().putString("fileUrl", fileUrl)
.build();
return Result.success(outputData);
}
}
}
- 第二步:定義 WorkRequest
這裏我們使用OneTimeWorkRequest 這個Request,因爲我們的request不是輪循的任務,故使用OneTimeWorkRequest,在MainActiivty中創建OneTimeWorkRequest如下所示:
Data inputData = new Data.Builder()
.putString("file", filePath)
.build();
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.setInputData(inputData)
.build();
UUID uploadRequestId= request.getId();
//每一個request對應一個UUID,通過這個ID,可以監聽該任務的一些狀態及執行結果
- 第三步:將定義好的WorkRequest加入到隊列,如下所示,和Okhttp請求差不多
WorkContinuation workContinuation = WorkManager.getInstance(PublishActivity.this)
.beginWith(request);
workContinuation.enqueue();
//通過返回的LiveData,監聽任務的執行結果及UI更新
workContinuation.getWorkInfosLiveData().observe(PublishActivity.this, new Observer<List<WorkInfo>>() {
@Override
public void onChanged(List<WorkInfo> workInfos) {
//block runing enuqued failed susscess finish
for (WorkInfo workInfo : workInfos) {
WorkInfo.State state = workInfo.getState();
Data outputData = workInfo.getOutputData();
UUID uuid = workInfo.getId();
if (state == WorkInfo.State.FAILED) {
if (uuid.equals(fileUploadUUID)) {
showToast(getString(R.string.file_upload_original_message));
//TODO
}
} else if (state == WorkInfo.State.SUCCEEDED) {
String fileUrl = outputData.getString("fileUrl");
if (uuid.equals(fileUploadUUID)) {
fileUploadUrl = fileUrl;
}
//TODO
}
}
}
});
三、數據交互
在實際開發業務中,執行後臺任務的時候,都會傳遞參數,這裏來具體講解下WorkRequest是如何進行數據傳遞
WorkRequest已經爲我們設計好了,在創建WorkRequest的時候,通過構建一個Data對象,傳入必要的參數,上述案例中需要上傳文件,故需傳遞文件對象的路徑
在UploadFileWorker 中,通過 getInputData(); 方法,將得到傳遞的參數。類似於Bundle的使用
Data inputData = new Data.Builder()
.putString("file", filePath)
.build();
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.setInputData(inputData)
.build();
在UploadFileWorker 中doWork()方法中,執行完異步任務後,需要傳遞參數的時候,也可以通過cData,將需要的參數返回出去,即Result.success(outputData)。
Data outputData = new Data.Builder().putString("fileUrl", fileUrl)
.build();
Result.success(outputData);
如果需要取消一個在隊列中的任務,,每一個Request對應一個ID,所以可以通過 id 實現取消任務
UUID uploadRequestId= request.getId();
WorkManager.getInstance().cancelWorkById(request.id)
到這裏,相信對WorkManager的使用有了基本的瞭解,接下來看WorkManager的高級用法
四、高級特性
4.1 環境約束
WorkManager 允許我們指定任務執行的環境,比如網絡已連接、電量充足時等,在滿足條件的情況下任務纔會執行。
具體設置方法如下:
@SuppressLint("RestrictedApi") Constraints constraints = new Constraints();
//設備存儲空間充足的時候 才能執行 ,>15%
constraints.setRequiresStorageNotLow(true);
//必須在執行的網絡條件下才能好執行,不計流量 ,wifi
constraints.setRequiredNetworkType(NetworkType.UNMETERED);
//設備的充電量充足的才能執行 >15%
constraints.setRequiresBatteryNotLow(true);
//只有設備在充電的情況下 才能允許執行
constraints.setRequiresCharging(true);
//只有設備在空閒的情況下才能被執行 比如息屏,cpu利用率不高
constraints.setRequiresDeviceIdle(true);
//workmanager利用contentObserver監控傳遞進來的這個uri對應的內容是否發生變化,當且僅當它發生變化了
//我們的任務纔會被觸發執行,以下三個api是關聯的
constraints.setContentUriTriggers(null);
//設置從content變化到被執行中間的延遲時間,如果在這期間。content發生了變化,延遲時間會被重新計算
// 這個content就是指 我們設置的setContentUriTriggers uri對應的內容
constraints.setTriggerContentUpdateDelay(0);
//設置從content變化到被執行中間的最大延遲時間 這個content就是指 我們設置的
constraints.setContentUriTriggers uri對應的內容
constraints.setTriggerMaxContentDelay(0);
通過創建Constraints ,設置具體的約束條件,在創建Request的時候,調用.setConstraints(constraints)即可。
OneTimeWorkRequest request = new OneTimeWorkRequest
.Builder(UploadFileWorker.class)
.setInputData(inputData)
.setConstraints(constraints)
// .setConstraints(constraints)
// //設置一個攔截器,在任務執行之前 可以做一次攔截,去修改入參的數據然後返回新的數據交由worker使用
// .setInputMerger(null)
// //當一個任務被調度失敗後,所要採取的重試策略,可以通過BackoffPolicy來執行具體的策略
// .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, TimeUnit.SECONDS)
// //任務被調度執行的延遲時間
// .setInitialDelay(10, TimeUnit.SECONDS)
// //設置該任務嘗試執行的最大次數
// .setInitialRunAttemptCount(2)
// //設置這個任務開始執行的時間
// //System.currentTimeMillis()
// .setPeriodStartTime(0, TimeUnit.SECONDS)
// //指定該任務被調度的時間
// .setScheduleRequestedAt(0, TimeUnit.SECONDS)
// //當一個任務執行狀態編程finish時,又沒有後續的觀察者來消費這個結果,難麼workamnager會在
// //內存中保留一段時間的該任務的結果。超過這個時間,這個結果就會被存儲到數據庫中
// //下次想要查詢該任務的結果時,會觸發workmanager的數據庫查詢操作,可以通過uuid來查詢任務的狀態
// .keepResultsForAtLeast(10, TimeUnit.SECONDS)
.build();
設置完constraints的Request,則加入到隊列中的時候 workContinuation.enqueue();不會立即執行,只有當constraints中的條件滿足的時候,纔會執行該request 。
4.2 強大的生命力
這是 WorkManager 的另一個特點,一旦發起一個任務,任務是可以保證一定會被執行的,就算退出應用,甚至重啓手機都阻止不了他。但可能由於添加了環境約束等原因,它執行的時間是不確定的。
當應用正在運行時,它會在當前的進程中啓用一個子線程執行。應用沒有運行的情況下啓用,它則會自己選擇一種合適的方式在後臺運行。具體是什麼方式和 Android 的版本和依賴環境有關:
4.3 任務鏈:
WorkManager 允許我們按照一定的順序執行任務,比如我想 A、B、C 三個任務按先後順序執行:
則可以使用如下代碼,即可實現:
OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
OneTimeWorkRequest requestB = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
OneTimeWorkRequest requestC = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
WorkManager.getInstance(PublishActivity.this).beginWith(requestA).then(requestB).then(requestC).enqueue();
這樣的話,上一個任務的 outputData 會成爲下一個任務的 inputData。
再更更復雜一點,如果我想這樣:
OneTimeWorkRequest requestA = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
OneTimeWorkRequest requestB = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
OneTimeWorkRequest requestC = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
OneTimeWorkRequest requestD = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
OneTimeWorkRequest requestE = new OneTimeWorkRequest.Builder(UploadFileWorker.class)
.build();
WorkContinuation chainA = WorkManager.getInstance(PublishActivity.this).beginWith(requestA).then(requestB);
WorkContinuation chainB = WorkManager.getInstance(PublishActivity.this).beginWith(requestC).then(requestD);
List<WorkContinuation> chains =new ArrayList<>();
chains.add(chainA);
chains.add(chainB);
WorkContinuation.combine(chains).then(requestE).enqueue();
4.5 任務唯一性
很多情況下,我們希望在任務隊列裏,同一個任務只存在一個,避免任務的重複執行,這時候可以用到 beginUniqueWork 這個方法:
WorkManager.getInstance()
.beginUniqueWork("unique", ExistingWorkPolicy.REPLACE, request)
.enqueue()
需要傳入一個任務的標籤,和重複任務的執行方式,可取值如下:
但這種方式也是隻支持 OneTimeWorkRequest。如果是 PeriodicWorkRequest,我想到的辦法是每次執行之前,根據標籤去取消已有的任務。
4.6 使用場景
很明顯,WorkManager 區別於異步任務,它更像是一個 Service。基本上,WorkManager 能做的,Service 也能做,我並沒有想到有什麼情況是非用 WorkManger 不可的。
但反觀 Service,氾濫的 Service 後臺任務可能是引起 Android 系統卡頓的主要原因,這幾年 Google 也對 Service 也做了一些限制。
總結
WorkManager的基本使用就介紹完了,文章中的示例代碼已上Jetpack
該倉庫爲演示Jetpack的組件的倉庫,分別對Lifecyele、LiveData、ViewModel、Room的介紹和使用
##詳細介紹文章