查看原文
其他

使用 Kotlin API 实践 WorkManager

Android Android 开发者 2021-08-05
WorkManager 提供了一系列 API 可以更加便捷地规划异步任务,即使在应用被关闭之后或者设备重启之后,仍然需要保证立即执行的或者推迟执行的任务被正常处理。对于 Kotlin 开发者,WorkManager 为协程提供了最佳的支持。在本文中,我将通过实践 WorkManager codelab 为大家展示 WorkManager 中与协程相关的基本操作。那么让我们开始吧!

  • WorkManager
    https://developer.android.google.cn/topic/libraries/architecture/workmanager
  • WorkManager codelab
    https://developer.android.google.cn/codelabs/android-workmanager#0



WorkManager 基础


当您需要某个任务保持运行状态,即使用户切换到别的界面或者用户将应用切换到后台,甚至设备重启的时候仍然不影响任务状态,那么非常推荐使用 WorkManager。类似的应用场景包括:
  • 上传日志或者报告数据

  • 使用滤镜处理图片的同时保存图片

  • 通过网络定期同步本地数据


如果您的即时任务可以在用户脱离某个作用域时结束,比如切换到其它界面,我们建议您还是直接使用 Kotlin 协程。

在这个 WorkManager codelab 教程中,我们会对图片进行模糊化处理,并且将处理后的数据存储在磁盘上。我们看一下这个过程中需要哪些操作。


添加 work-runtime-ktx 依赖:
// 获取最新的版本号 https://developer.android.google.cn/jetpack/androidx/releases/workdef work_version = "2.5.0"implementation "androidx.work:work-runtime-ktx:$work_version"


首先实现我们自己的 Worker 类。我们会在这里实现真正需要在后台执行业务的代码。您可以扩展 Worker 类,并且复写 doWork() 方法。由于这个类非常重要,我们会在后边内容中进行详细介绍。这里是它最初的实现代码。

/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */
class BlurWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {
override fun doWork(): Result { val resourceUri = inputData.getString(KEY_IMAGE_URI)
return try { if (resourceUri.isNullOrEmpty()) { Timber.e("Invalid input uri") throw IllegalArgumentException("Invalid input uri") }
val outputData = blurAndWriteImageToFile(resourceUri) Result.success(outputData) } catch (throwable: Throwable) { Timber.e(throwable, "Error applying blur") Result.failure() } }}


接下来,创建我们的 work 请求,在本例中,我们希望整个操作仅运行一次,所以我们使用 OneTimeWorkRequest.Builder,将需要模糊化处理的图片的 Uri 作为参数传入。

  • OneTimeWorkRequest.Builder
    https://developer.android.google.cn/reference/androidx/work/OneTimeWorkRequest.Builder


Kotlin 小贴士: 要创建输入数据,我们可以使用 workDataOf 函数,它会帮我们创建数据构建器,并且填充键值对,然后为我们创建数据。
/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */
val blurBuilder = OneTimeWorkRequestBuilder<BlurWorker>()val data = workDataOf(KEY_IMAGE_URI to imageUri.toString())blurBuilder.setInputData(data)

  • workDataOf
    https://developer.android.google.cn/reference/kotlin/androidx/work/package-summary#workdataof


我们使用 WorkManager 类将上面所做的工作添加到计划队列并且运行。我们可以提供需要执行的任务和这些任务的限制条件。
/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */
val workManager = WorkManager.getInstance(application)val continuation = workManager.beginUniqueWork(blurBuilder.build())// 执行任务continuation.enqueue()


使 Worker 开始执行任务


当您使用 Worker 的时候,WorkManager 会在后台线程中自动调用 Worker.doWork()。doWork() 返回的 Result 会告知 WorkManager 服务是否成功,如果失败则告知是否需要重试。


  • Worker
    https://developer.android.google.cn/reference/androidx/work/Worker?hl=en

Worker.doWork() 属于同步调用 -- 您的后台操作需要以阻塞的方式执行,并且所有任务需要在整个 doWork() 函数结束的时候完成。如果您在 doWork() 里调用异步的 API 然后返回结果,那么您回调函数的执行可能会出现问题。


但是如果我希望进行异步操作怎么办?


我们来将上面的示例操作变得复杂一点,比如我希望在数据库中存储所有进行模糊化处理的文件的 Uri。

所以我创建了:
  • 一个简单的 BlurredImage 实体
  • 一个用于插入和获取图片的 DAO 类
  • 数据库

相关的实现代码请详见:

https://github.com/googlecodelabs/android-workmanager/pull/213


如果您需要执行异步操作,比如在数据库中存储数据或者发起网络请求,在 Kotlin 中,我们推荐使用 CoroutineWorker。

  • CoroutineWorker

    https://developer.android.google.cn/reference/kotlin/androidx/work/CoroutineWorker


CoroutineWorker 通过使用 Kotlin 协程来执行异步任务。

doWork() 方法是一个 suspend 方法。也就是说我们这里可以调用可挂起的 dao 函数。
/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */
class BlurWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
override suspend fun doWork(): Result { val resourceUri = inputData.getString(KEY_IMAGE_URI)
return try { if (resourceUri.isNullOrEmpty()) { Timber.e("Invalid input uri") throw IllegalArgumentException("Invalid input uri") }
val outputData = blurAndWriteImageToFile(resourceUri) // 将 uri 存储到数据库 val imageDao = ImagesDatabase.getDatabase(applicationContext).blurredImageDao() imageDao.insert(BlurredImage(resourceUri))
Result.success(outputData) } catch (throwable: Throwable) { Timber.e(throwable, "Error applying blur") Result.failure() } }...}

doWork() 默认使用 Dispatchers.Default。您可以将其替换为您所需的 Dispatcher。在这里,我们不需要这么做,因为 Room 已经将数据插入操作放在另外的 Dispatcher 中完成了。更多相关内容可以参考 Room Kotlin API。

开始使用 CoroutineWorker 来执行异步任务吧,即使用户关闭应用也可以确保任务完成。

如果您希望了解更多关于 WorkManager 的内容,请关注未来的相关文章。在那之前,可以访问我们的 codelab 和文档:


  • WorkManager 文档
    https://developer.android.google.cn/topic/libraries/architecture/workmanager/
  • Codelab | 使用 WorkManager
    https://developer.android.google.cn/codelabs/android-workmanager#0
  • Codelab | WorkManager 进阶
    https://developer.android.google.cn/codelabs/android-adv-workmanager#0



 点击屏末 | 阅读原文 | 即刻了解 WorkManager 使用入门



推荐阅读





    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存