使用 Kotlin API 实践 WorkManager

WorkManager 提供了一系列 API 能够更加便捷地规划异步任务,即便在应用被关闭以后或者设备重启以后,仍然须要保证当即执行的或者推迟执行的任务被正常处理。对于 Kotlin 开发者,WorkManager 为协程提供了最佳的支持。在本文中,我将经过实践 WorkManager codelab 为你们展现 WorkManager 中与协程相关的基本操做。那么让咱们开始吧!android

WorkManager 基础

当您须要某个任务保持运行状态,即便用户切换到别的界面或者用户将应用切换到后台,甚至设备重启的时候仍然不影响任务状态,那么很是推荐使用 WorkManager。相似的应用场景包括:git

  • 上传日志或者报告数据
  • 使用滤镜处理图片的同时保存图片
  • 经过网络按期同步本地数据

若是您的即时任务能够在用户脱离某个做用域时结束,好比切换到其它界面,咱们建议您仍是直接使用 Kotlin 协程。github

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

添加 work-runtime-ktx 依赖:网络

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

首先实现咱们本身的 Worker 类。咱们会在这里实现真正须要在后台执行业务的代码。您能够扩展 Worker 类,而且复写 doWork() 方法。因为这个类很是重要,咱们会在后边内容中进行详细介绍。这里是它最初的实现代码。app

/* 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 做为参数传入。异步

Kotlin 小贴士 : 要建立输入数据,咱们能够使用 workDataOf 函数,它会帮咱们建立数据构建器,而且填充键值对,而后为咱们建立数据。ide

/* 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)

咱们使用 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 服务是否成功,若是失败则告知是否须要重试。ui

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

可是若是我但愿进行异步操做怎么办?

咱们来将上面的示例操做变得复杂一点,好比我但愿在数据库中存储全部进行模糊化处理的文件的 Uri。

因此我建立了:

  • 一个简单的 BlurredImage 实体
  • 一个用于插入和获取图片的 DAO 类
  • 数据库

相关的实现代码请 点击这里

若是您须要执行异步操做,好比在数据库中存储数据或者发起网络请求,在 Kotlin 中,咱们推荐使用 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 和文档:

相关文章
相关标签/搜索