Android WorkManager的使用

1. 簡介

當咱們想要實現不是很緊急可是須要必須完成的處理時會比較難辦,若是使用service會產生額外的電量消耗,若是使用Broadcast比較難實現以及還須要設置觸發條件。爲了解決這個問題谷歌在Android 5.0時推出了JobScheduler,來替換此前的方案。 做爲更進一步在Android Jetpack上推出了WorkManager, 它會根據系統的版本用不一樣的方法去實現上述需求。java

2. WorkManager的簡單介紹

WorkManager 是一個 Android 庫, 它在工做的觸發器 (如適當的網絡狀態和電池條件) 知足時, 優雅地運行可推遲的後臺工做。WorkManager 儘量使用框架 JobScheduler , 以幫助優化電池壽命和批處理做業。在 Android 6.0 (API 級 23) 下面的設備上, 若是 WorkManager 已經包含了應用程序的依賴項, 則嘗試使用Firebase JobDispatcher 。不然, WorkManager 返回到自定義 AlarmManager 實現, 以優雅地處理您的後臺工做。android

根據官方的敘述,WorkManager會根據限制條件自動進行後臺任務。若是系統版本在6.0以上則會使用JobScheduler,若是是一下則會使用AlarmManager+BroadCastReceivergit

3. 使用方法

3.1 添加依賴

在module的build.gradle中添加以下依賴。github

implementation "androidx.work:work-runtime-ktx:2.3.3"
複製代碼

3.2 建立後臺處理

咱們首先須要建立須要被執行的任務。咱們須要繼承Worker類,而且須要重寫doWork方法。緩存

class CustomWorker(context: Context, workerParameters: WorkerParameters) :
    Worker(context, workerParameters) {
    override fun doWork(): Result {
        Log.d("CustomWorker", "Worker is active in " + inputData.getString("data"))
        return Result.success()
    }
}
複製代碼

inputData.getString(key)是獲取外部的傳值。使用方法跟intent傳值相似。 咱們須要返回的值是Result的枚舉值。一共有以下幾種。網絡

  1. Result.success() 表示任務執行成功。
  2. Result.retry() 表示任務執行失敗,須要再次嘗試。能夠在後面講述的退避規則BackoffPolicy的設定方法進行嘗試。
  3. Result.failure() 表示任務執行失敗,但再也不進行嘗試。

3.3 建立觸發時的限制條件

咱們須要經過Constrains來設置觸發時的限制條件。框架

val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .setRequiresBatteryNotLow(true)
            .setRequiresStorageNotLow(true)
            .setRequiresCharging(true)
            .setRequiresDeviceIdle(true)
            .build()
複製代碼
3.3.1 setRequiredNetworkType

設置須要的網絡類型。一共有以下幾種。ide

枚舉值 狀態說明
NOT_REQUIRED 不須要網絡
CONNECTED 任何可用網絡
UNMETERED 須要不計量網絡,如WiFi
NOT_ROAMING 須要非漫遊網絡
METERED 須要計量網絡,如4G
3.3.2 setRequiresBatteryNotLow

設置手機電量的狀態,這裏的條件是執行任務時手機電量不能較低。gradle

3.3.3 setRequiresStorageNotLow

設置手機內存空間的狀態,這裏的條件是執行任務時手機內存空間不能較低。優化

3.3.4 setRequiresCharging

設置手機的充電狀態, 這裏的條件是手機執行任務時手機是處於充電的狀態。

3.3.5 setRequiresDeviceIdle

設置手機的狀態, 這裏的條件是手機執行任務時手機是出於空閒狀態。

3.3.6 addContentUriTrigger(uri:Uri, triggerForDescendants:Boolean)

添加觸發器,即當指定的URI中有內容更新時會觸發任務。值得注意的是這裏只能在OneTimeWorkRequest中設置。

3.4 建立WorkRequest

一共有兩種WorkRequest。一種是隻執行一次的OneTimeWorkRequest,還有一種是按期執行的PeriodicWorkRequest

val oneTimeWorkRequest =
OneTimeWorkRequestBuilder<CustomWorker>().setConstraints(constraints)
    .setBackoffCriteria(
        BackoffPolicy.LINEAR,
        OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
        TimeUnit.MILLISECONDS
    )
    .setInputData(inputData)
    .build()

val periodicWorkRequest =
PeriodicWorkRequestBuilder<CustomWorker>(10, TimeUnit.MILLISECONDS)
    .setConstraints(constraints)
    .setBackoffCriteria(
        BackoffPolicy.LINEAR,
        PeriodicWorkRequest.MIN_BACKOFF_MILLIS,
        TimeUnit.MILLISECONDS
    ).build()
複製代碼

PeriodicWorkRequest的構造器中還須要添加執行的週期,上述代碼是每10秒去確認條件是否執行。可是週期任務最少間隔是15分鐘,因此雖然是寫了10秒,可是會在15分鐘後會被執行。

3.4.1 setConstraints

咱們要在WorkRequest中設置Constraints(即上面講過的限制條件)。

3.4.2 setBackoffCriteria

咱們還能夠設置退避條件。 退避條件有兩個屬性:

  1. BackoffPolicy, 默認爲是指數性的,可是能夠設置成線性。
  2. 持續時間, 默認爲30秒。
3.4.3 setInputData

咱們能夠在WorkRequest中經過setInputData方法給Worker傳值。使用方法跟Intent類似,以下。

val inputData = Data.Builder().putString("data", "MainActivity").build()
複製代碼

3.5 經過WorkManager執行

最後咱們能夠經過WorkManager去執行上面制定的各類需求。

WorkManager.getInstance(this).enqueue(oneTimeWorkRequest)
複製代碼

3.6 監視Worker執行狀況

咱們還能夠經過LiveDataWorker的執行狀況進行監視。當Worker被執行時,監視處會受到通知。

WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest.id).observe(this,
    Observer {
        count++
        txtId.text = "Work status is changed! $count"
        Toast.makeText(this, "Work status is changed!",Toast.LENGTH_LONG).show()
    })
複製代碼

WorkInfo的源代碼以下:

private @NonNull UUID mId;
    private @NonNull State mState;
    private @NonNull Data mOutputData;
    private @NonNull Set<String> mTags;
    private @NonNull Data mProgress;
    private int mRunAttemptCount;

    /** * @hide */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public WorkInfo(
            @NonNull UUID id,
            @NonNull State state,
            @NonNull Data outputData,
            @NonNull List<String> tags,
            @NonNull Data progress,
            int runAttemptCount) {
        mId = id;
        mState = state;
        mOutputData = outputData;
        mTags = new HashSet<>(tags);
        mProgress = progress;
        mRunAttemptCount = runAttemptCount;
    }
複製代碼

首先看一下State

public enum State {

    ENQUEUED,//加入隊列
    RUNNING,//運行中
    SUCCEEDED,//已成功
    FAILED,//失敗
    BLOCKED,//掛起
    CANCELLED;//取消

    public boolean isFinished() {
        return (this == SUCCEEDED || this == FAILED || this == CANCELLED);
    }
}
複製代碼

BLOCKED的狀況是,當約束狀況沒有被所有知足是Worker會被掛起。

3.7 結束任務

WorkRequest入列了之後,WorkManager會爲它分配一個work ID,咱們能夠經過work ID進行任務的取消或中止。

WorkManager.getInstance(this).cancelWorkById(workRequest.id)
複製代碼

WorkManager並不必定能結束任務,由於有些任務可能已經執行完畢 除了上述方法之外還有其餘結束任務的方法:

  1. cancelAllWork(): 取消全部的任務
  2. cancelAllWorkByTag(tag:String): 取消一組帶有相同標籤的任務
  3. cancelUniqueWoork(uniqueWorkName:String): 取消惟一任務

3.8 鏈式調用

當咱們須要按順序或者同時執行WorkRequest時,咱們能夠用鏈式調用的方法執行。

3.8.1並行執行
val workRequest1 = OneTimeWorkRequestBuilder<CustomWorker>().build()
val workRequest2 = OneTimeWorkRequestBuilder<CustomWorker>().build()
val workRequest3 = OneTimeWorkRequestBuilder<CustomWorker>().build()

// 會同時執行。
WorkManager.getInstance().beginWith(workRequest1,workRequest2,workRequest3).enqueue()
複製代碼
3.8.2順序執行

咱們可使用beginWith-then方法進行順序執行。若是順序執行的途中有一個失敗了,以後的任務將不會被執行。

// 順序執行
WorkManager.getInstance().beginWith(workRequest1).then(workRequest2).then(workRequest3).enqueue()
複製代碼
3.8.3 組合執行

咱們可使用combine操做符進行組合執行。

// 組合執行
val chain1 = WorkManager.getInstance()
    .beginWith(workRequest1)
    .then(workRequest2)
val chain2 = WorkManager.getInstance()
    .beginWith(workRequest3)
    .then(workRequest4)
WorkContinuation
    .combine(chain1, chain2)
    .then(workRequest5)
    .enqueue()
複製代碼
3.8.4 惟一鏈

惟一鏈,如其名就是同一時間內在執行隊列中農不能存在相同名稱的任務。

val workRequest = OneTimeWorkRequestBuilder<CustomWorker>().build()
WorkManager.getInstance(this).beginUniqueWork("TAG", ExistingWorkPolicy.REPLACE, workRequest)
複製代碼

注意的是最有一個參數WorkRequest可變長度的參數。 第一個參數就是任務名字"TAG"。 第二個參數是已存在任務時的執行策略。枚舉值以下。

public enum ExistingWorkPolicy {
    REPLACE, // 替換
    KEEP, // 保持,不作任何操做
    APPEND // 添加到已有的任務中
}
複製代碼
  1. REPLACE: 存在相同名稱的任務而且被掛起,則取消和刪除現有的任務,而後替換新的任務。
  2. KEEP: 存在相同名稱的任務而且被掛起,則不作任何操做。
  3. APPEND: 存在相同名稱的任務而且被掛起,會把新任務添加到緩存中,都隊列中的全部任務都被執行完畢時,新任務會被設爲第一任務。

github: github.com/HyejeanMOON…

相關文章
相關標籤/搜索