當咱們想要實現不是很緊急可是須要必須完成的處理時會比較難辦,若是使用service
會產生額外的電量消耗,若是使用Broadcast
比較難實現以及還須要設置觸發條件。爲了解決這個問題谷歌在Android 5.0
時推出了JobScheduler
,來替換此前的方案。 做爲更進一步在Android Jetpack
上推出了WorkManager
, 它會根據系統的版本用不一樣的方法去實現上述需求。java
WorkManager 是一個 Android 庫, 它在工做的觸發器 (如適當的網絡狀態和電池條件) 知足時, 優雅地運行可推遲的後臺工做。WorkManager 儘量使用框架 JobScheduler , 以幫助優化電池壽命和批處理做業。在 Android 6.0 (API 級 23) 下面的設備上, 若是 WorkManager 已經包含了應用程序的依賴項, 則嘗試使用Firebase JobDispatcher 。不然, WorkManager 返回到自定義 AlarmManager 實現, 以優雅地處理您的後臺工做。
android
根據官方的敘述,WorkManager
會根據限制條件自動進行後臺任務。若是系統版本在6.0以上則會使用JobScheduler
,若是是一下則會使用AlarmManager+BroadCastReceiver
。git
在module的build.gradle
中添加以下依賴。github
implementation "androidx.work:work-runtime-ktx:2.3.3"
複製代碼
咱們首先須要建立須要被執行的任務。咱們須要繼承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
的枚舉值。一共有以下幾種。網絡
Result.success()
表示任務執行成功。Result.retry()
表示任務執行失敗,須要再次嘗試。能夠在後面講述的退避規則BackoffPolicy
的設定方法進行嘗試。Result.failure()
表示任務執行失敗,但再也不進行嘗試。咱們須要經過Constrains
來設置觸發時的限制條件。框架
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.setRequiresStorageNotLow(true)
.setRequiresCharging(true)
.setRequiresDeviceIdle(true)
.build()
複製代碼
設置須要的網絡類型。一共有以下幾種。ide
枚舉值 | 狀態說明 |
---|---|
NOT_REQUIRED | 不須要網絡 |
CONNECTED | 任何可用網絡 |
UNMETERED | 須要不計量網絡,如WiFi |
NOT_ROAMING | 須要非漫遊網絡 |
METERED | 須要計量網絡,如4G |
設置手機電量的狀態,這裏的條件是執行任務時手機電量不能較低。gradle
設置手機內存空間的狀態,這裏的條件是執行任務時手機內存空間不能較低。優化
設置手機的充電狀態, 這裏的條件是手機執行任務時手機是處於充電的狀態。
設置手機的狀態, 這裏的條件是手機執行任務時手機是出於空閒狀態。
添加觸發器,即當指定的URI中有內容更新時會觸發任務。值得注意的是這裏只能在OneTimeWorkRequest
中設置。
一共有兩種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分鐘後會被執行。
咱們要在WorkRequest
中設置Constraints
(即上面講過的限制條件)。
咱們還能夠設置退避條件。 退避條件有兩個屬性:
BackoffPolicy
, 默認爲是指數性的,可是能夠設置成線性。咱們能夠在WorkRequest
中經過setInputData
方法給Worker
傳值。使用方法跟Intent
類似,以下。
val inputData = Data.Builder().putString("data", "MainActivity").build()
複製代碼
最後咱們能夠經過WorkManager
去執行上面制定的各類需求。
WorkManager.getInstance(this).enqueue(oneTimeWorkRequest)
複製代碼
咱們還能夠經過LiveData
對Worker
的執行狀況進行監視。當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
會被掛起。
當WorkRequest
入列了之後,WorkManager
會爲它分配一個work ID
,咱們能夠經過work ID
進行任務的取消或中止。
WorkManager.getInstance(this).cancelWorkById(workRequest.id)
複製代碼
WorkManager並不必定能結束任務,由於有些任務可能已經執行完畢 除了上述方法之外還有其餘結束任務的方法:
cancelAllWork()
: 取消全部的任務cancelAllWorkByTag(tag:String)
: 取消一組帶有相同標籤的任務cancelUniqueWoork(uniqueWorkName:String)
: 取消惟一任務當咱們須要按順序或者同時執行WorkRequest
時,咱們能夠用鏈式調用的方法執行。
val workRequest1 = OneTimeWorkRequestBuilder<CustomWorker>().build()
val workRequest2 = OneTimeWorkRequestBuilder<CustomWorker>().build()
val workRequest3 = OneTimeWorkRequestBuilder<CustomWorker>().build()
// 會同時執行。
WorkManager.getInstance().beginWith(workRequest1,workRequest2,workRequest3).enqueue()
複製代碼
咱們可使用beginWith-then
方法進行順序執行。若是順序執行的途中有一個失敗了,以後的任務將不會被執行。
// 順序執行
WorkManager.getInstance().beginWith(workRequest1).then(workRequest2).then(workRequest3).enqueue()
複製代碼
咱們可使用combine
操做符進行組合執行。
// 組合執行
val chain1 = WorkManager.getInstance()
.beginWith(workRequest1)
.then(workRequest2)
val chain2 = WorkManager.getInstance()
.beginWith(workRequest3)
.then(workRequest4)
WorkContinuation
.combine(chain1, chain2)
.then(workRequest5)
.enqueue()
複製代碼
惟一鏈,如其名就是同一時間內在執行隊列中農不能存在相同名稱的任務。
val workRequest = OneTimeWorkRequestBuilder<CustomWorker>().build()
WorkManager.getInstance(this).beginUniqueWork("TAG", ExistingWorkPolicy.REPLACE, workRequest)
複製代碼
注意的是最有一個參數WorkRequest
可變長度的參數。 第一個參數就是任務名字"TAG"。 第二個參數是已存在任務時的執行策略。枚舉值以下。
public enum ExistingWorkPolicy {
REPLACE, // 替換
KEEP, // 保持,不作任何操做
APPEND // 添加到已有的任務中
}
複製代碼
REPLACE
: 存在相同名稱的任務而且被掛起,則取消和刪除現有的任務,而後替換新的任務。KEEP
: 存在相同名稱的任務而且被掛起,則不作任何操做。APPEND
: 存在相同名稱的任務而且被掛起,會把新任務添加到緩存中,都隊列中的全部任務都被執行完畢時,新任務會被設爲第一任務。github: github.com/HyejeanMOON…