Android Jetpack架構組件-WorkManager使用篇


在這裏插入圖片描述

一. 定義:

作爲 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()

需要傳入一個任務的標籤,和重複任務的執行方式,可取值如下:
image.png

但這種方式也是隻支持 OneTimeWorkRequest。如果是 PeriodicWorkRequest,我想到的辦法是每次執行之前,根據標籤去取消已有的任務。

4.6 使用場景

很明顯,WorkManager 區別於異步任務,它更像是一個 Service。基本上,WorkManager 能做的,Service 也能做,我並沒有想到有什麼情況是非用 WorkManger 不可的。

但反觀 Service,氾濫的 Service 後臺任務可能是引起 Android 系統卡頓的主要原因,這幾年 Google 也對 Service 也做了一些限制。

總結

WorkManager的基本使用就介紹完了,文章中的示例代碼已上Jetpack

該倉庫爲演示Jetpack的組件的倉庫,分別對Lifecyele、LiveData、ViewModel、Room的介紹和使用

##詳細介紹文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章