[译] WorkManager 基础入门,android 小游戏源代码
.setRequiresCharging(true).setRequiresStorageNotLow(true).setRequiresDeviceIdle(true).build()
最后,还记得 Result.retry()
吗?我之前说过,如果 Worker
返回 Result.retry()
,WorkManager 将重新计划工作。你可以在创建新的 WorkRequest
时自定义退避条件。这允许你定义何时应重试运行。
[退避条件](
)由两个属性定义:
[BackoffPolicy](
),默认为指数性的,但是可以设置为线性。
持续时间,默认为 30 秒。
用于对上传工作进行排队的组合代码如下,包括约束,输入和自定义退避策略:
// Create the Constraintsval constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
// Define the inputval imageData = workDataOf(Constants.KEY_IMAGE_URI to imageUriString)
// Bring it all together by creating the WorkRequest; this also sets the back off criteriaval uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>().setInputData(imageData).setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.LINEAR,OneTimeWorkRequest.MIN_BACKOFF_MILLIS,TimeUnit.MILLISECONDS).build()
运行 work
这些都很好,但你还没有真正调度好你的工作去运行。以下是告诉 WorkManager 调度工作所需的一行代码:
WorkManager.getInstance().enqueue(uploadWorkRequest)
你首先需要获取 [WorkManager
](
) 的实例,这是一个负责执行你的工作的单例。调用 [enqueue
](
) 来启动 WorkManager
跟踪和调度工作的整个过程。
在幕后 —— 工作是怎么运行的
那么,WorkManager
能为您做些什么呢?默认情况下,WorkManager
会:
脱离主线程运行你的工作(假设你正在继承
Worker
类,如上面的UploadWorker
所示)。保障 你的工作将会运行(即使你重启设备或应用程序退出,它也不会忘记运行你的工作)
。
根据用户 API 级别的最佳实践运行(如[上一篇文章](
)所述)。
让我们探讨一下 WorkManager 如何确保你的工作脱离主线程运行并保证执行。在幕后,WorkManager 包括以下部分:
内部 TaskExecutor:一个单线程 [
Executor
](
),处理所有排队工作的请求。如果您不熟悉 Executors
,可以在[这里](
)阅读更多相关信息。
WorkManager 数据库:一个本地数据库,可跟踪所有工作的所有信息和状态。这包括工作的当前状态,工作的输入和输出以及对工作的任何约束限制。此数据库使 WorkManager 能够保证你的工作能够完成 —— 如果你的用户的设备重新启动并且工作中断,则可以从数据库中提取工作的所有详细信息,并在设备再次启动时重新启动工作。
WorkerFactory:一个默认工厂,用于创建
Worker
的实例。我们将在以后的博文中介绍为什么以及如何配置它。Default Executor:一个默认的执行程序,运行你的工作,除非你另行指定。这确保在默认情况下,你的工作是同步运行的,并且在主线程之外运行。
这些部分可以被重写以具有不同的行为。
来自:[Working with WorkManager](
) Android 开发者大会展示 2018
当你安排 WorkRequest
:
内部 TaskExecutor 立即将你的
WorkRequest
信息保存到 WorkManager 数据库。稍后,当满足
WorkRequest
的Constraints
时(可以立即发生),Internal TaskExecutor 会告诉WorkerFactory
创建一个Worker
。之后,默认的
Executor
调用你的Worker
的doWork()
方法脱离主线程。
通过这种方式,默认情况下,你的工作都可以保证执行脱离主线程运行。
现在,如果你想使用除默认 Executor
之外的一些其他机制来运行你的工作,也是可以的!对协程(CoroutineWorker
)和 RxJava([RxWorker
](
))的开箱即用支持作为工作的手段。
或者,你可以使用 [ListenableWorker
](
) 准确指定工作的执行方式。Worker
实际上是 ListenableWorker
的一个实现,它默认在默认的 Executor
上运行你的工作,因此是同步的。所以,如果你想要完全控制工作的线程策略或异步运行工作,你可以将 ListenableWorker
子类化(具体细节将在后面的文章中讨论)。
WorkManager 虽然将所有工作信息保存到数据库中有些麻烦,但它还是会做,这使得它成了非常适合需要保障执行的任务。这也是使得 WorkManager 轻松应对对于不需要保障且只需要在后台线程上执行的任务的的原因。例如,假设你已经下载了图像,并且希望根据该图像更改 UI 部分的颜色。这是应该脱离主线程运行的工作,但是,因为它与 UI 直接相关,所以如果关闭应用程序则不需要继续。所以在这样的情况下,不要使用 WorkManager —— 坚持使用像 [Kotlin 协程](
)那样轻量的东西或创建自己的 Executor
。
使用链进行依赖性工作
我们的滤镜示例包含的不仅仅是一个任务 —— 我们想要给多个图像加滤镜,然后压缩并上传。如果要一个接一个地或并行地运行一系列 WorkRequests
,则可以使用 [链](
)。示例图显示了一个链,其中有三个并行运行的滤镜任务,后面是压缩任务和上传任务,按顺序运行:
使用 WorkManager 非常简单。假设你已经用适当的约束创建了所有 WorkRequests,代码如下所示:
WorkManager.getInstance().beginWith(Arrays.asList(filterImageOneWorkRequest,filterImageTwoWorkRequest,filterImageThreeWorkRequest)).then(compressWorkRequest).then(uploadWorkRequest).enqueue()
三个图片滤镜 WorkRequests
并行执行。一旦完成所有滤镜 WorkRequests (并且只有完成所有三个),就会发生 compressWorkRequest
,然后是 uploadWorkRequest
。
链的另一个优点是:一个 WorkRequest
的输出作为下一个 WorkRequest
的输入。因此,假设你正确设置了输入和输出数据,就像我上面的 UploadWorker
示例所做的那样,这些值将自动传递。
为了处理并行的三个滤镜工作请求的输出,可以使用 [InputMerger
](
),特别是 [ArrayCreatingInputMerger
](
)。代码如下:
val compressWorkRequest = OneTimeWorkRequestBuilder<CompressWorker>().setInputMerger(ArrayCreatingInputMerger::class.java).setConstraints(constraints).build()
请注意,InputMerger
是添加到 compressWorkRequest
中的,而不是并行的三个滤镜请求中的。
假设每个滤镜工作请求的输出是映射到图像 URI 的键 “KEY_IMAGE_URI”。添加 ArrayCreatingInputMerger
的作用是并行请求的输出,当这些输出具有匹配的键时,它会创建一个包含所有输出值的数组,映射到单个键。可视化图表如下:
ArrayCreatingInputMerger
功能可视化
因此,compressWorkRequest
的输入将最终成为映射到滤镜图像 URI 数组的 “KEY_IMAGE_URI” 对。
观察你的 WorkRequest 状态
监视工作的最简单方法是使用 [LiveData
](
) 类。如果你不熟悉 LiveData,它是一个生命周期感知的可监视数据持有者 —— [这里](
) 对此有更详细的描述。
调用 [getWorkInfoByIdLiveData
](
) 返回一个 [WorkInfo
](
) 的 LiveData
。WorkInfo
包含输出的数据和表示工作状态的枚举。当工作顺利完成后,它的 [State
](
) 就会是 SUCCEEDED
。因此,例如,你可以通过编写一些监视代码来实现当工作完成时自动显示该图像:
// In your UI (activity, fragment, etc)WorkManager.getInstance().getWorkInfoByIdLiveData(uploadWorkRequest.id).observe(lifecycleOwner, Observer { workInfo ->// Check if the current work's state is "successfully finished"if (workInfo != null && workInfo.state == WorkInfo.State.SUCCEEDED) {displayImage(workInfo.outputData.getString(KEY_IMAGE_URI))}})
有几点需要注意:
每个
WorkRequest
都有一个[唯一的 id](
),该唯一 id 是查找关联 WorkInfo
的一种方法。
WorkInfo
更改时进行监视并被通知的能力是LiveData
提供的功能。
工作有一个由不同 [State
](
) 代表的生命周期。监视 LiveData<WorkInfo>
时,你会看到这些状态;例如,你可能会看到:
“happy path” 或工作状态
工作状态经历的 “happy path” 如下:
BLOCKED
:只有当工作在链中并且不是链中的下一个工作时才会出现这种状态。ENQUEUED
:只要工作是工作链中的下一个并且有资格运行,工作就会进入这个状态。这项工作可能仍在等待Constraint
被满足。RUNNING
:在这种状态时,工作正在运行。对于Worker
,这意味着doWork()
方法已经被调用。SUCCEEDED
:当doWork()
返回Result.success()
时,工作进入这种最终状态。
现在,当工作处于 RUNNING
状态,你可以调用 Result.retry()
。这将会导致工作退回 ENQUEUED
状态。工作也可能随时被 CANCELLED
。
如果工作运行的结果是 Result.failure()
而不是成功。它的状态将会以 FAILED
结束,因此,状态的完整流程图如下所示:
(来自:[Working with WorkManager](
) Android 开发者峰会 2018)
想看精彩的视频讲解,请查看 [WorkManager Android 开发者峰会演讲](
)。
总结
这就是 WorkManager API 的基础知识。使用我们刚刚介绍的代码片段,你现在就可以:
创建包含输入/输出的
Worker
。使用
WorkRequest
、Constraint
、启动输入和退出策略配置Worker
的运行方式。调度
WorkRequest
。了解默认情况下
WorkManager
在线程和保障运行方面的幕后工作。创建复杂链式相互依赖的工作,可以顺序运行和并行运行。
使用
WorkInfo
监视你的WorkRequest
的状态。
想亲自试试 WorkManager 吗?查看 codelab,包含 [Kotlin](
) 和 [Java](
) 代码。
随着我们继续更新本系列,请继续关注有关 WorkManager 主题的更多博客文章。 有什么问题或者你希望我们写到的东西吗?请在评论区告诉我们!
**感谢 [Pietro Maggi](
)**
WorkManager 相关资源
[官方文档](
)
[参考指南](
)
[WorkManager 1.0.0-beta02 Release notes](
)
Codelab:[Kotlin](
) 和 [Java](
).
[源码(AOSP 的一部分)](
)
[Working with WorkManager (Android 开发者峰会 2018) presentation](
)
[Issue Tracker](
)
[StackOverflow 上面的 WorkManager 相关问题](
)
[Google’s Power blog post series](
)
如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](
) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。
[掘金翻译计划](
) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](
) 上的英文分享文章。内容覆盖 [Android](
)、[iOS](
)、[前端](
)、[后端](
)、[区块链](
)、[产品](
)、[设计](
)、[人工智能](
)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](
)、[官方微博](
)、[知乎专栏](
)。
评论