一. WorkManager介绍
WorkManager是Google推出的组件, 用于解决应用在退出或者设备重启后仍需要需要运行任务的问题.
如何管理后台工作
WorkManager内部会根据设备的API级别自动选择底层作业的调度服务. 下面上一张官方图, 图中清晰说明了WorkManager在各个版本的API时选择的调度服务.目前最低可支持API 14
和直接在应用中使用线程的区别
首先WorkManager的作用并不是取代线程在Android中的工作. Google在官方的文档专门为后台任务做出了定义
Google 将后台任务具体分为了四种Immediate Tasks, Exact Task, Expedited Task, Deferred Task
Immediate Task
当任务需要在用户操作APP时就完成,则可归类为Imeediate Task. 推荐在APP中使用Kotlin协程或Java的线程来执行任务
Exact Task
当任务需要在精确的时间运行时,则可归类为Exact Task. 推荐使用AlarmManager
Expedited Task & Deferred Task
除以上情景之外, 如果任务需要尽可能快开始时,则可归类为Expedited Task, 如果不需要则归类为Deferred Task. 推荐使用WorkManager
从WorkManager 2.7.0版本开始可以使用setExpedited()来申明Worker为加急任务. 对应上面的Expedited Task. 需要同时重写Worker中的getForegroundInfoAsync方法
OneTimeWorkRequestBuilder<T>().apply {
setInputData(inputData)
setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
}.build()
二. WorkManager使用
Worker & CoroutineWorker
Worker类作用为定义任务所执行的工作. 继承Worker类在doWork()方法中编写所需要执行的任务(如果想要使用Kotlin协程可以使用CoroutineWorker
doWork()方法返回值通知WorkManager任务执行的结果
Result.success()任务执行成功Result.failure()任务执行失败Result.retry()任务需重新执行
@WorkerThread
public abstract @NonNull Result doWork();
WorkRequests
WorkRequest类作用为定义工作Worker的运行方式(例如: Worker运行所需要满足的约束条件, 为Worker传递数据, Worker调度信息配置等).
WorkManager提供了两种WorkRequest的实现
- OneTimeWorkRequest(一次性工作)
- PeriodicWorkRequest(定期工作)
// 传递给Worker的参数
val data = Data.Builder().putString(DownloadWorker.KEY_NAME, downloadContent).build()
// Worker执行的约束条件
val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
// 创建WorkRequest
OneTimeWorkRequestBuilder<DownloadWorker>()
.addTag(DownloadWorker.TAG)
.setInputData(data)
.setConstraints(constraints)
.build()
WorkManager
WorkManager作用为管理Work. 例如加入任务,取消任务,以及监听任务的执行
加入任务
mWorkManager.beginUniqueWork(
DownloadWorker.TAG,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequestBuilder<DownloadWorker>()
.addTag(DownloadWorker.TAG)
.setInputData(data)
.setConstraints(constraints)
//此设置需要在Worker中重写getForegroundInfo
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.build()
).enqueue()
监听Worker的执行
mWorkManager.getWorkInfosByTagLiveData(DownloadWorker.TAG)
.observe(this) {
if (it.isNotEmpty()) {
val workInfo = it[0]
when(workInfo.state) {
WorkInfo.State.SUCCEEDED,
WorkInfo.State.BLOCKED,
WorkInfo.State.ENQUEUED,
WorkInfo.State.RUNNING,
WorkInfo.State.CANCELLED
}
}
}
获取到的任务结果为List<WorkInfo>, List[0]代表当前最新
三. 实践
创建Worker模拟任务执行
class DownloadWorker : CoroutineWorker {
private lateinit var mNotificationBuilder: NotificationCompat.Builder
constructor(appContext: Context, params: WorkerParameters) : super(appContext, params)
//Worker执行任务
override suspend fun doWork(): Result {
val data = fakeDownload()
showSuccessNotification()
val outData = Data.Builder().putString(OUTPUT_KEY, data).build()
return Result.success(outData)
}
//创建ForegroundInfo Worker将会作为前台服务运行
override suspend fun getForegroundInfo(): ForegroundInfo {
val context = applicationContext
return ForegroundInfo(
START_DOWNLOAD_NOTIFICATION_ID,
createNotification(context) {
setContentTitle("Start Download")
setSmallIcon(R.drawable.ic_launcher_foreground)
setContentText("Start Download ${inputData.getString(INPUT_KEY)}")
priority = NotificationCompat.PRIORITY_DEFAULT
val cancel = WorkManager.getInstance(context).createCancelPendingIntent(id)
//设置cancelWork按钮
addAction(R.drawable.icon_cancel, "Cancel", cancel)
mNotificationBuilder = this
}
)
}
private suspend fun fakeDownload(): String {
Log.i(TAG, "Thread:${Thread.currentThread().name}")
for (i in 0..100) {
delay(100L)
mNotificationBuilder.setContentText("Start Download ${inputData.getString(INPUT_KEY)} $i%")
notifyNotification(applicationContext, START_DOWNLOAD_NOTIFICATION_ID, mNotificationBuilder.build())
}
Log.i(TAG, "Download Succeed")
return "Download Succeed"
}
private fun showSuccessNotification() {
notifyNotification(applicationContext, DOWNLOAD_SUCCEED_NOTIFICATION_ID) {
setContentTitle("Download Succeed")
setSmallIcon(R.drawable.ic_launcher_foreground)
setContentText("Download ${inputData.getString(INPUT_KEY)} Succeed")
priority = NotificationCompat.PRIORITY_DEFAULT
setAutoCancel(true)
val intent = Intent(applicationContext, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(applicationContext, 0, intent, PendingIntent.FLAG_IMMUTABLE)
setContentIntent(pendingIntent)
}
}
}
创建WorkRequest
创建WorkRequest 设置执行条件,参数传递, 并加入任务队列
private fun enqueueDownloadWork() {
val downloadContent = "复仇者联盟"
val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
val data = Data.Builder().putString(DownloadWorker.INPUT_KEY, downloadContent).build()
mWorkManager.beginUniqueWork(
DownloadWorker.TAG,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequestBuilder<DownloadWorker>()
.addTag(DownloadWorker.TAG)
.setInputData(data)
.setConstraints(constraints)
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)//
.build()
).enqueue()
}
监听任务执行情况
通过先前加入任务时设置的TAG监听Worker的执行情况
mWorkManager.getWorkInfosByTagLiveData(DownloadWorker.TAG)
.observe(this) {
if (it.isNotEmpty()) {
val workInfo = it[0]
when(workInfo.state) {
WorkInfo.State.SUCCEEDED -> {
mBinding.download.setText("重新下载")
mBinding.tips.visibility = View.VISIBLE
mBinding.tips.text = workInfo.outputData.getString(DownloadWorker.OUTPUT_KEY)
mBinding.download.setOnClickListener {
enqueueDownloadWork()
}
}
WorkInfo.State.BLOCKED,
WorkInfo.State.ENQUEUED,
WorkInfo.State.RUNNING, -> {
mBinding.tips.text = "正在下载"
mBinding.download.text = "取消下载"
mBinding.download.setOnClickListener {
mWorkManager.cancelUniqueWork(DownloadWorker.TAG)
}
}
WorkInfo.State.CANCELLED -> {
mBinding.tips.visibility = View.GONE
mBinding.download.text = "开始下载"
mBinding.download.setOnClickListener {
enqueueDownloadWork()
}
}
}
}
}
完整项目地址
WorkManagerDemo 谢谢大家观看, 如有错误还请帮忙指出.