?? 簡介:
??WorkManager 是一個(gè) API,它可以很合理的安排可延遲的異步任務(wù),即使應(yīng)用程序退出或者設(shè)備重新啟動(dòng),這些異步任務(wù)也有望運(yùn)行.WorkManager API是所有以前Android 后臺(tái)調(diào)度api 的合適的且推薦的替代品,包括FirebaseJobDispatcher.GcmNetworkManager,和 Job Scheduler.WorkManager 在現(xiàn)在,一致的 api 中合并了其前輩的功能,該 API 可以兼容到 API 14,同事考慮了電池的壽命.
雖然 Service也可以實(shí)現(xiàn),但是消耗電量大,而且并不允許長時(shí)間后臺(tái)執(zhí)行.PASS 掉.
特點(diǎn)
- ?? 不需要及時(shí)完成的任務(wù).比如 發(fā)送日志,同步用戶數(shù)據(jù)等
- ?? 保證任務(wù)一定會(huì)被執(zhí)行.即使 app不在運(yùn)行或者重啟設(shè)備.
- ?? 兼容范圍廣.最低到API level 14.并且不需要安裝 google paly service.
?? 原理:

?? 使用方法:
先看看幾個(gè)關(guān)鍵的類.
?? Worker : 任務(wù)的執(zhí)行者,是一個(gè)抽象類,需要集成它來實(shí)現(xiàn)要執(zhí)行的任務(wù).
?? WorkRequest : 指定哪個(gè) Worker 執(zhí)行任務(wù),可以向WorkRequest中添加細(xì)節(jié),指定執(zhí)行環(huán)境,執(zhí)行順序,ID 等.WorkRequest是個(gè)抽象類,在代碼中是,使用其子類,OneTimeWorkRequest 或者PeriodicWorkRequest.
?? WorkManager :對WorkRequest進(jìn)行排隊(duì)和管理.
?? WorkStatus :包含任務(wù)的狀態(tài)和信息,以 LIiveData 的形式提供給觀察者.
1,build.gradle 添加依賴
// Java
implementation "androidx.work:work-runtime:2.5.0"
// kotlin workmanager
implementation "androidx.work:work-runtime-ktx:2.5.0"
2,使用 Work 類定義任務(wù)
class MyWoker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
override fun doWork(): Result {
//耗時(shí)任務(wù)在 doWork()中執(zhí)行
return Result.success()
}
}
doWork() 有 3 種返回類型
- 執(zhí)行成功 Result.success()
- 執(zhí)行失敗 Result.failure()
- 重新執(zhí)行 Result.retry()
3, 使用 WorkRequest 分配任務(wù)
??WorkRequest是一個(gè)抽象類,它有兩種實(shí)現(xiàn)方式,OneTimeWorkRequest 和 PeriodicWorkRequest,分別對應(yīng)一次性任務(wù)和周期性任務(wù).
?? 定義WorkRequest
OneTimeWorkRequest :
var build = OneTimeWorkRequest.Builder(MyWoker::class.java).build()
PeriodicWorkRequest :
var build = PeriodicWorkRequest.Builder(MyWoker::class.java, 15, TimeUnit.MINUTES).build()
??以下的代碼示例中,我會(huì)以O(shè)neTimeWorkRequest寫 Demo.
??PeriodicWorkRequest使用和OneTimeWorkRequest沒有太大區(qū)別,需要注意的是,間隔時(shí)間不能少于 15 分鐘.
?? 設(shè)置任務(wù)的觸發(fā)條件:
??比如,我們在設(shè)備處于充電,網(wǎng)絡(luò)連接的情況下觸發(fā)任務(wù),通過.setConstraints(constraints)方法設(shè)置觸發(fā)條件
var constraints = Constraints.Builder()
.setRequiresCharging(true)
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
var build = OneTimeWorkRequest.Builder(MyWoker::class.java)
.setConstraints(constraints)
.build()
??看下圖還有很多其他的條件,具體使用什么條件,根據(jù)需求定吧.

?? 設(shè)置任務(wù)延遲:
??通過
.setInitialDelay(10,TimeUnit.SECONDS) 設(shè)置延時(shí)條件
var build = OneTimeWorkRequest.Builder(MyWoker::class.java)
.setConstraints(constraints)
.setInitialDelay(10,TimeUnit.SECONDS)
.build()
?? 設(shè)置指數(shù)退避策略:
??假如Work線程執(zhí)行異常,在 doWork()方法中返回 Result.retry(),系統(tǒng)會(huì)有默認(rèn)的的指數(shù)退避策略來重試任務(wù).也可以通過.setBackoffCriteria(BackoffPolicy.LINEAR,OneTimeWorkRequest.MIN_BACKOFF_MILLIS,TimeUnit.MINUTES)來定義指數(shù)退避策略.BackoffPolicy有兩個(gè)值LINEAR(每次重試的時(shí)間線性增加,比如第一次10分鐘,第二次就是20分鐘)、EXPONENTIAL(每次重試時(shí)間指數(shù)增加)。
var build = OneTimeWorkRequest.Builder(MyWoker::class.java)
.setConstraints(constraints)
.setInitialDelay(10,TimeUnit.SECONDS)
.setBackoffCriteria(BackoffPolicy.LINEAR,OneTimeWorkRequest.MIN_BACKOFF_MILLIS,TimeUnit.MINUTES)
.build()
?? 為任務(wù)設(shè)置 tag 標(biāo)簽:
??設(shè)置 tag 標(biāo)簽后,可以根據(jù) tag 來跟蹤任務(wù)的狀態(tài):WorkManager.getInstance(this).getWorkInfosByTag(),也可以取消任務(wù): WorkManager.getInstance(this).cancelAllWorkByTag().
var build = OneTimeWorkRequest.Builder(MyWoker::class.java)
.setConstraints(constraints)
.setInitialDelay(10,TimeUnit.SECONDS)
.setBackoffCriteria(BackoffPolicy.LINEAR,OneTimeWorkRequest.MIN_BACKOFF_MILLIS,TimeUnit.MINUTES)
.addTag("myTag")
.build()
4, 將任務(wù)交給系統(tǒng),加入任務(wù)隊(duì)列
WorkManager.getInstance(this).enqueue(build)
?? 觀察任務(wù)狀態(tài):
??觀察任務(wù)的狀態(tài)的方法如下:

??我們可以通過 LivaData 在任務(wù)狀態(tài)發(fā)生變化的的時(shí)候接收通知:
WorkManager.getInstance(this)
.getWorkInfosByTagLiveData("myTag")
.observe(this,
{
Log.e("TestActivity", "workInfo: $it")
})
?? 取消任務(wù):
??取消任務(wù)的方法如下:

??與觀察任務(wù)類似,可以根據(jù) id 或者 tag 來取消某個(gè)任務(wù)或者取消所有任務(wù).
WorkManager.getInstance(this).cancelAllWorkByTag("myTag")
5, WorkManager 和 Worker 之間的參數(shù)傳遞
??WorkManager通過setInputData()方法給 Worker 傳遞參數(shù).數(shù)據(jù)的傳遞通過 Data 對象來完成.
???? Data 只能用于傳遞一些小的數(shù)據(jù)類型,切數(shù)據(jù)不能超過 10KB.
var inputData = Data.Builder().putString("nameS", "張三").build()
var build = OneTimeWorkRequest.Builder(MyWoker::class.java)
.setInputData(inputData)
//......
.addTag("myTag")
.build()
??Woker 通過 getInputData()方法接受數(shù)據(jù),并在任務(wù)完成后,向 WorkManager 返回?cái)?shù)據(jù).
class MyWoker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
override fun doWork(): Result {
inputData.getString("nameS")?.let { Log.e("mmm", "MyWoker收到的信息: $it") }
var outData = Data.Builder().putString("nameM", "我已經(jīng)回復(fù)張三了").build()
return Result.success(outData)
}
}
??WorkManager通過 LiveData 得到從 Worker 返回?cái)?shù)據(jù)
WorkManager.getInstance(this)
.getWorkInfosByTagLiveData("myTag")
.observe(this, {
for (workInfo in it) {
Log.d("mmm", workInfo.toString())
if (WorkInfo.State.SUCCEEDED.isFinished) {
val outputData = workInfo.outputData
val nameM = outputData.getString("nameM")
nameM?.let { it1 ->
Log.e("mmm", "TestActivity收到的信息: $it1")
WorkManager.getInstance(this@TestActivity).cancelAllWorkByTag("myTag")
}
}
}
})
?? 執(zhí)行結(jié)果截圖:

6, 任務(wù)鏈
?? 如果有一系列的任務(wù)需要按照順序執(zhí)行,那么可以利用WorkManager.beginWith().then().then()....enqueue()的方式構(gòu)建任務(wù).beginWith()可與傳遞單個(gè)任務(wù),也可以傳遞任務(wù)集合.

單個(gè)任務(wù):
WorkManager.getInstance(this)
.beginWith(build)
.then(build)
.then(build)
.enqueue()
多個(gè)任務(wù):(集合)
?? WorkContinuation.combine()方法將任務(wù)鏈組合起來用
var buildOne = WorkManager.getInstance(this)
.beginWith(buildOne)
.then(buildTwo)
var buildTwo = WorkManager.getInstance(this)
.beginWith(buildThree)
.then(buildFour)
var list = mutableListOf<WorkContinuation>()
list.add(buildOne)
list.add(buildTwo)
WorkContinuation
.combine(list)
.then(buildFive)
.enqueue()
ps:任務(wù)鏈的東西,我沒試過