阅读 572

Android WorkManager入门与实践

一. WorkManager介绍

WorkManager是Google推出的组件, 用于解决应用在退出或者设备重启后仍需要需要运行任务的问题.

如何管理后台工作

WorkManager内部会根据设备的API级别自动选择底层作业的调度服务. 下面上一张官方图, 图中清晰说明了WorkManager在各个版本的API时选择的调度服务.目前最低可支持API 14

image.png

和直接在应用中使用线程的区别

首先WorkManager的作用并不是取代线程在Android中的工作. Google在官方的文档专门为后台任务做出了定义

Google 将后台任务具体分为了四种Immediate Tasks, Exact Task, Expedited Task, Deferred Task

task-category-tree.svg

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]代表当前最新

三. 实践

device-2021-11-21-143555.gif

创建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()                     }                 }             }         }     }


作者:amosgole
链接:https://juejin.cn/post/7032920470328442910
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


文章分类
ANDROID
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐