一、为什么使用WorkManager
如果工作始终要通过应用重启和系统重新启动来调度,则称之为永久性的工作。大多数应用都有在后台执行任务的需求,即通过持久性工作能够完成。Android为后台任务提供了多种解决方案,例如JobScheduler
,Loader
,Service
等,但是这些Api若是使用不当,则可能导致耗电等系列问题。
WorkManager
是适用于持久性工作的推荐解决方案,其可以处理三种类型的永久性工作:
- 立即执行:必须立即开始且很快就完成的任务,可以加急
- 长时间运行:运行时间可能较长(有可能超过 10 分钟)的任务。
- 可延期执行:延期开始并且可以定期运行的预定任务。
WorkManager
最低能兼容API Level 14,并且不需要你的设备安装有Google Play Services。
WorkManager
能依据设备的情况,选择不同的执行方案。在API Level 23+,通过JobScheduler
来完成任务;而在API Level 23以下的设备中,通过AlarmManager
和Broadcast Receivers
组合完成任务。但无论采用哪种方案,任务最终都是交由Executor
来完成。
WorkManager
无缝集成了Coroutines
和RxJava
,可以很方便插入异步API。WorkManager
的一系列特性使其适用于需要可靠运行的工作,即使用户导航离开屏幕、退出应用或重启设备也不影响工作执行,例如:
- 向后端服务发送日志或分析数据
- 定期将应用数据与服务器同步
- 退出应用后还应继续执行的未完成任务
WorkManager
不适用于那些可在应用进程结束时安全终止的进程内后台工作
二、基本使用
使用WorkManager
前,先将相关库导入项目中
dependencies {
def work_version = "xxx"
// (Java only)
implementation "androidx.work:work-runtime:$work_version"
// Kotlin + coroutines
implementation "androidx.work:work-runtime-ktx:$work_version"
// optional - RxJava2 support
implementation "androidx.work:work-rxjava2:$work_version"
// optional - GCMNetworkManager support
implementation "androidx.work:work-gcm:$work_version"
// optional - Test helpers
androidTestImplementation "androidx.work:work-testing:$work_version"
// optional - Multiprocess support
implementation "androidx.work:work-multiprocess:$work_version"
}
WorkManager
的使用流程主要为:
- 创建一个后台任务
Worker
- 定义
WorkRequest
,配置运行任务的方式和时间 - 将任务提交给系统处理
- 观察
Worker
的进度或状态
2.1 核心类
Worker
类
Work
定义工作,即指定需要执行的任务,可以继承抽象的Worker
类。doWork()
方法在 WorkManager
提供的后台线程上异步运行任务,在其中实现具体逻辑
class UploadWorker(appContext: Context, workerParams: WorkerParameters):
Worker(appContext, workerParams) {
override fun doWork(): Result {
//......
//@return 任务的执行情况,成功,失败,还是需要重新执行
return Result.success()
}
}
根据doWork()
返回值判断任务执行情况:
Worker.Result.SUCCESS
:任务执行成功。Worker.Result.FAILURE
:任务执行失败。Worker.Result.RETRY
:任务失败需要重新执行。需要配合WorkRequest.Builder
里面的setBackoffCriteria()
函数使用
WorkRequest
类
定义完工作,需要使用WorkManager
服务进行调度才能使得工作执行。WorkRequest
及其子类则定义了工作运行方式和时间,例如指定任务应该运行的环境,任务的输入参数,任务只有在有网的情况下执行等等。
WorkRequest
是抽象类,其有两个子类:
OneTimeWorkRequest
(任务只执行一遍)PeriodicWorkRequest
(任务周期性的执行)
其中WorkRequest.Builder
为创建WorkRequest
对象的帮助类;Constraints
为指定任务运行的限制条件;Constraint.Builder
来创建Constraints
val uploadWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<UploadWorker>()
.build()
WorkManager
类
用于排队和管理工作请求,将WorkRequest
对象传递到WorkManager
的任务队列
WorkManager
.getInstance(myContext)
.enqueue(uploadWorkRequest)
WorkInfo
类
工作在整个生命周期内会经历一系列State
更改,而WorkInfo
用于包含其特定的任务信息与状态。
工作状态分为:ENQUEUED
、RUNNING
、SUCCEEDED
、FAILED
、BLOCKED
与CANCELLED
。
-
一次性工作的状态
对于一次性工作请求,初始状态为
ENQUEUED
。ENQUEUED
状态下,工作满足Constraints
和初始延迟计时要求后立即运行,工作状态转为RUNNING
状态,然后根据结果转为SUCCEEDED
、FAILED
。如果结果是retry
,重回到ENQUEUED
状态。在此过程中可随时取消工作,状态变更为CANCELLED
。SUCCEEDED
、FAILED
和CANCELLED
均表示此工作的终止状态。处于此状态时,WorkInfo.State.isFinished()
返回true
。 -
定期工作的状态
成功和失败状态仅适用于一次性工作和链式工作,定期工作只有一个终止状态
CANCELLED
-
链式工作的状态
关于链式工作,其多了个
BLOCKED
状态。关于链式工作状态,可直接参看链接和工作状态
可以使用LiveData
或Future
保存WorkInfo
对象,监听任务状态。
public abstract @NonNull LiveData<WorkInfo> getWorkInfoByIdLiveData(@NonNull UUID id);
public abstract @NonNull ListenableFuture<WorkInfo> getWorkInfoById(@NonNull UUID id);
public abstract @NonNull LiveData<List<WorkInfo>> getWorkInfosByTagLiveData( @NonNull String tag);
......
2.2 WorkRequest定义
先定义一个Worker
,继承Worker
类,实现doWork()
方法,编写需要在任务中执行的代码
class TestWorker(context:Context,workerParameters: WorkerParameters):Worker(context,workerParameters) {
override fun doWork(): Result {
Log.d("TestWorker","doWork ${System.currentTimeMillis()}")
return Result.success()
}
}
工作是通过WorkRequest
在WorkManager
中进行定义和配置才能运行
任务触发约束配置
可以通过WorkRequest
设置任务触发条件,如设备处于充电、网络已连接等环境才触发任务
var constraints: Constraints = Constraints.Builder()
.setRequiresCharging(true)
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
var workRequest = OneTimeWorkRequestBuilder<TestWorker>().setConstraints(constraints).build()
关于WorkManager
的约束如下所示
NetworkType |
约束运行工作所需的网络类型。例如 Wi-Fi (UNMETERED )。 |
---|---|
BatteryNotLow |
如果设置为 true ,那么当设备处于“电量不足模式”时,工作不会运行。 |
RequiresCharging |
如果设置为 true ,那么工作只能在设备充电时运行。 |
DeviceIdle |
如果设置为 true ,则要求用户的设备必须处于空闲状态,才能运行工作。在运行批量操作时,此约束会非常有用;若是不用此约束,批量操作可能会降低用户设备上正在积极运行的其他应用的性能。 |
StorageNotLow |
如果设置为 true ,那么当用户设备上的存储空间不足时,工作不会运行。 |
如果在工作运行时不再满足某个约束,WorkManager
将停止工作器。系统将在满足所有约束后重试工作。
任务调度周期配置
WorkRequest
是抽象类,其有两个子类:OneTimeWorkRequest
(任务只执行一遍)与PeriodicWorkRequest
(任务周期性的执行)
调度一次性工作:
如果无需额外配置,可使用静态方法from
val workRequest = OneTimeWorkRequest.from(TestWorker::class.java)
对于复杂的工作,则可以使用构建器
val workRequest = OneTimeWorkRequestBuilder<TestWorker>()
//...... 添加额外配置
.build()
调度定期工作:
有时可能需要定期运行某些工作,例如定期备份数据,定期上传日志等,可用PeriodicWorkRequest
//工作运行时间间隔设置为1小时
val workRequest = PeriodicWorkRequestBuilder<TestWorker>(1,TimeUnit.HOURS).build()
时间间隔定义为两次重复执行之间的最短时间,可以定义的最短重复间隔是 15 分钟。
如果需要运行的工作对运行时间敏感,可以将PeriodicWorkRequest
配置为在每个时间间隔的灵活时间段内运行
灵活时间段从 repeatInterval - flexInterval
开始,一直到间隔结束
//在每小时的最后 15 分钟内运行的定期工作
val workRequest = PeriodicWorkRequestBuilder<TestWorker>(1,TimeUnit.HOURS,15,TimeUnit.MINUTES).build()
其中间隔必须大于或等于 PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS
,而灵活间隔必须大于或等于 PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS
延迟工作配置
如果工作没有约束,或者当工作加入队列时所有约束都得到了满足,那么系统可能会选择立即运行该工作。如果您不希望工作立即运行,可以将工作指定为在经过一段最短初始延迟时间后再启动。
可以通过setInitialDelay()
方法,延后任务的执行
var workRequest = OneTimeWorkRequestBuilder<TestWorker>().setConstraints(constraints)
.setInitialDelay(10,TimeUnit.MINUTES)//符合触发条件后,延迟10 minutes执行
.build()
定期工作只有首次运行时会延迟。
工作重试与退避配置
假如Worker
线程的执行出现了异常,比如服务器宕机,你可能希望过一段时间,重试该任务,那么你可以在Worker
的doWork()
方法中返回Result.retry()
,系统会有默认的指数退避策略来帮你重试任务
- 退避延迟时间指定了首次尝试后重试工作前的最短等待时间。此值不能超过 10 秒(或 MIN_BACKOFF_MILLIS)。
- 退避政策定义了在后续重试过程中,退避延迟时间随时间以怎样的方式增长。WorkManager 支持 2 个退避政策,即
LINEAR
和EXPONENTIAL
。
默认政策是EXPONENTIAL
,延迟时间为10S
workRequest = OneTimeWorkRequestBuilder<TestWorker>()
.setBackoffCriteria(BackoffPolicy.LINEAR,OneTimeWorkRequest.MIN_BACKOFF_MILLIS,TimeUnit.MILLISECONDS)
.build()
示例中,最短退避延迟时间设置为允许的最小值,即 10 秒。由于政策为 LINEAR
,每次尝试重试时,重试间隔都会增加约 10 秒。例如,第一次运行以 Result.retry()
结束并在 10 秒后重试;然后,如果工作在后续尝试后继续返回 Result.retry()
,那么接下来会在 20 秒、30 秒、40 秒后重试,以此类推。如果退避政策设置为 EXPONENTIAL
,那么重试时长序列将接近 20、40、80 秒,以此类推。
工作标记配置
每个工作请求都有一个唯一标识符,该标识符可用于在以后标识该工作,以便取消工作或观察其进度
如果有一组在逻辑上相关的工作,对这些工作项进行标记可能也会很有帮助。通过标记,您一起处理一组工作请求。
例如,WorkManager.cancelAllWorkByTag(String)
会取消带有特定标记的所有工作请求,WorkManager.getWorkInfosByTag(String)
会返回一个 WorkInfo 对象列表,该列表可用于确定当前工作状态。
var workRequest = OneTimeWorkRequestBuilder<TestWorker>()
.addTag("者文静斋")
.build()
可以向单个工作请求添加多个标记。这些标记在内部以一组字符串的形式进行存储。您可以使用 WorkInfo.getTags() 获取与 WorkRequest
关联的标记集。
从 Worker
类中,您可以通过 ListenableWorker.getTags() 检索其标记集。
数据交互配置
在实际开发业务中,执行后台任务的时候,都会传递参数。WorkManager
和Worker
之间的参数传递。数据的传递通过Data
对象来完成。Worker
类可通过调用 Worker.getInputData()
访问输入参数
class TestWorker(context:Context,workerParameters: WorkerParameters):Worker(context,workerParameters) {
override fun doWork(): Result {
val message = inputData.getString("个人公众号")
Log.d("TestWorker","doWork $message ${System.currentTimeMillis()}")
return Result.success()
}
}
var workRequest = OneTimeWorkRequestBuilder<TestWorker>()
.setInputData(workDataOf("者文公众号" to "者文静斋"))
.addTag("者文静斋")
.build()
同样,可以使用Data
类输出返回值。可以在任务执行完成后,向WorkManager
传递数据,即Result.success(outputData)
class TestWorker(context:Context,workerParameters: WorkerParameters):Worker(context,workerParameters) {
override fun doWork(): Result {
val message = inputData.getString("个人公众号")
Log.d("TestWorker","doWork $message ${System.currentTimeMillis()}")
val output: Data = workDataOf("output" to "do work success")
return Result.success(output)
}
}
WorkManager
通过LiveData
的WorkInfo.getOutputData()
,得到Worker
传递过来的数据
注意:Data
只能用于传递一些小的基本类型数据,且数据最大不能超过10kb
加急工作
WorkManager 2.7.0
引入了加急工作的概念,系统必须先为加急作业分配应用执行时间,然后才能运行作业。执行时间并非无限制,而是受配额限制。如果您的应用使用其执行时间并达到分配的配额,在配额刷新之前,您无法再执行加急工作。
可以调用 setExpedited()
来声明 WorkRequest
应该使用加急作业,以尽可能快的速度运行。
var workRequest = OneTimeWorkRequestBuilder<TestWorker>()
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.build()
配额不足时:
OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST
:导致作业作为普通工作请求运行OutOfQuotaPolicy.DROP_WORK_REQUEST
:配额不足时取消请求
以 Android 12 为目标平台的应用在后台运行时无法再启动前台服务,但一些特殊情况除外。如果应用在后台运行时尝试启动前台服务,并且前台服务不符合任何特殊情况,则系统会抛出 ForegroundServiceStartNotAllowedException
。
对于Android12之后,可以使用WorkManager
的加急作业替代;对于Android 12之前,WorkManager
会在其平台上运行前台服务。
2.3 管理工作
定义完Worker
和WorkRequest
,需要将工作加入队列。将工作加入队列的最简单方法是调用 WorkManager
enqueue()
方法,然后传递要运行的 WorkRequest
。
......
WorkManager.getInstance(this).enqueue(workRequest)
唯一工作
将工作加入队列时需小心谨慎,避免重复。例如,应用可能会每 24 小时尝试将其日志上传到后端服务。如果不谨慎,即使作业只需运行一次,您最终也可能会多次将同一作业加入队列。为了实现此目标,可以将工作调度为唯一工作。
唯一工作可确保同一时刻只有一个具有特定名称的工作实例。唯一名称由开发者指定,仅与一个工作实例相关联。
唯一工作可用于一次性工作,也可用于定期工作。创建方法为
- [
WorkManager.enqueueUniqueWork()
](developer.android.google.cn/reference/a…, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest))(用于一次性工作) - [
WorkManager.enqueueUniquePeriodicWork()
](developer.android.google.cn/reference/a…, androidx.work.ExistingPeriodicWorkPolicy, androidx.work.PeriodicWorkRequest))(用于定期工作)
参数 | 含义 |
---|---|
uniqueWorkName |
唯一标识工作请求的String |
existingWorkPolicy |
此 enum 可告知 WorkManager :如果已有使用该名称且尚未完成的唯一工作链,应执行什么操作 |
work |
要调度的WorkRequest |
调度唯一工作时,您必须告知 WorkManager
在发生冲突时要执行的操作,其处理冲突的策略选项有4个:
-
REPLACE
:用新工作替换现有工作。此选项将取消现有工作 -
KEEP
:保留现有工作,并忽略新工作。 -
APPEND
:将新工作附加到现有工作的末尾。此政策将导致您的新工作链接到现有工作,在现有工作完成后运行。现有工作将成为新工作的先决条件。如果现有工作变为
CANCELLED
或FAILED
状态,新工作也会变为CANCELLED
或FAILED
。如果您希望无论现有工作的状态如何都运行新工作,请改用APPEND_OR_REPLACE
。 -
APPEND_OR_REPLACE
函数类似于APPEND
,不过它并不依赖于先决条件工作状态。即使现有工作变为CANCELLED
或FAILED
状态,新工作仍会运行
val sendLogsWorkRequest =
PeriodicWorkRequestBuilder<SendLogsWorker>(24, TimeUnit.HOURS)
.setConstraints(Constraints.Builder()
.setRequiresCharging(true)
.build()
)
.build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"sendLogs",
ExistingPeriodicWorkPolicy.KEEP,
sendLogsWorkRequest
)
上述代码在 sendLogs
作业已处于队列中的情况下运行,系统会保留现有的作业,并且不会添加新的作业。
观察任务状态
任务提交给系统后,通过WorkInfo
获知任务状态。WorkInfo
包含了任务的id
,tag
以及Worker
对象传递过来的outputData
,以及任务当前的状态。
// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>
// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>
// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>
该查询会返回 WorkInfo
对象的 ListenableFuture
,该值包含工作的 id
、其标记、其当前的 State
以及通过 Result.success(outputData)
设置的任何输出数据。
利用每个方法的 LiveData
变种,您可以通过注册监听器来观察 WorkInfo
的变化。
//在某项工作成功完成后向用户显示消息
workManager.getWorkInfoByIdLiveData(syncWorker.id)
.observe(viewLifecycleOwner) { workInfo ->
if(workInfo?.state == WorkInfo.State.SUCCEEDED) {
Snackbar.make(requireView(),
R.string.work_completed, Snackbar.LENGTH_SHORT)
.show()
}
}
WorkManager 2.4.0
及更高版本支持使用 WorkQuery
对象对已加入队列的作业进行复杂查询。WorkQuery
支持按工作的标记、状态和唯一工作名称的组合进行查询。
val workQuery = WorkQuery.Builder
.fromTags(listOf("syncTag"))
.addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
.addUniqueWorkNames(listOf("preProcess", "sync")
)
.build()
val workInfos: ListenableFuture<List<WorkInfo>> = workManager.getWorkInfos(workQuery)
WorkQuery
中的每个组件(标记、状态或名称)与其他组件都是 AND
逻辑关系。组件中的每个值都是 OR
逻辑关系。WorkQuery
也适用于等效的 LiveData 方法 getWorkInfosLiveData()
。
取消和停止任务
取消任务
若不再需要运行先前加入队列的工作,可按照工作的 name
、id
或与其关联的 tag
取消工作。
// by id
workManager.cancelWorkById(syncWorker.id)
// by name
workManager.cancelUniqueWork("sync")
// by tag
workManager.cancelAllWorkByTag("syncTag")
如果工作已经完成,系统不会执行任何操作。否则,工作的状态会更改为 CANCELLED
,之后就不会运行这个工作。
停止任务
正在运行的 Worker
可能会由于以下几种原因而停止运行:
- 明确要求取消它
- 如果是唯一工作,明确地将
ExistingWorkPolicy
为REPLACE
的新WorkRequest
加入到了队列中。旧的WorkRequest
会立即被视为已取消。 - 工作约束条件已不再满足
- 系统出于某种原因指示您的应用停止工作。如果超过 10 分钟的执行期限,可能会发生这种情况。该工作会调度为在稍后重试。
上述情况,工作器会停止。可通过onStopped()
回调或isStopped()
属性了解工作器何时停止,进行相关资源释放操作
-
onStopped()
回调工作器停止后,
WorkManager
会立即调用ListenableWorker.onStopped()
。替换此方法可关闭可能保留的所有资源。 -
isStopped()
属性可以调用
ListenableWorker.isStopped()
方法以检查工作器是否已停止。若工作器执行长时间运行操作或重复操作,应经常检查此属性,用作停止工作的信号。
任务进度更新与观察
WorkManager 2.3.0-alpha01
为设置和观察工作器的中间进度添加了一流的支持。
ListenableWorker
支持setProgressAsync()
API,允许保留中间进度。开发者能可通过界面观察到的中间进度。进度由 Data
类型表示够设置
更新进度
在 Kotlin
中,您可以使用 CoroutineWorker
对象的 setProgress()
扩展函数来更新进度信息。
class ProgressWorker(context: Context, parameters: WorkerParameters) :
CoroutineWorker(context, parameters) {
companion object {
const val Progress = "Progress"
private const val delayDuration = 1L
}
override suspend fun doWork(): Result {
val firstUpdate = workDataOf(Progress to 0)
val lastUpdate = workDataOf(Progress to 100)
setProgress(firstUpdate)
delay(delayDuration)
setProgress(lastUpdate)
return Result.success()
}
}
观察进度
可以使用 getWorkInfoBy…()
或 getWorkInfoBy…LiveData()
方法,并引用 WorkInfo
。
WorkManager.getInstance(applicationContext)
// requestId is the WorkRequest id
.getWorkInfoByIdLiveData(requestId)
.observe(observer, Observer { workInfo: WorkInfo? ->
if (workInfo != null) {
val progress = workInfo.progress
val value = progress.getInt(Progress, 0)
// Do something with progress information
}
})
2.4 任务链
可以使用 WorkManager
创建工作链并将其加入队列。工作链用于指定多个依存任务并定义这些任务的运行顺序,尤其是需要以特定顺序运行多个任务时。
创建任务链可以使用WorkManager.beginWith(OneTimeWorkRequest)
或 WorkManager.beginWith(List)
等方法
WorkManager.getInstance(this)
.beginWith(workA)
.then(workB)
.then(workC)
.enqueue()
任务会按照设置的顺序依次执行A、B、C。WorkManager
在执行过程中,遇到其中一个Work
不成功,会停止执行。比如代码执行到WorkB
返回FAILURE
状态,代码结束,WorkC
不执行
WorkManager.getInstance(this)
.beginWith(listof(plantName1, plantName2, plantName3) // 三个对象将并行作业
.then(cache)// 执行完3个plantName后,再执行cache
.then(upload) //...
.enqueue()
输入合并器
链接 OneTimeWorkRequest
实例时,父级工作请求的输出将作为子级的输入传入。上面的示例中,plantName1
、plantName2
和 plantName3
的输出将作为 cache
请求的输入传入。
WorkManger
使用InputMerger
管理多个父级工作请求输入。
WorkManager 提供两种不同类型的 InputMerger
:
OverwritingInputMerger
会尝试将所有输入中的所有键添加到输出中。如果发生冲突,它会覆盖先前设置的键。ArrayCreatingInputMerger
会尝试合并输入,并在必要时创建数组。
OverwritingInputMerger
是默认的合并方法。如果合并过程中存在键冲突,键的最新值将覆盖生成的输出数据中的所有先前版本。
例如,如果每种植物的输入都有一个与其各自变量名称("plantName1"
、"plantName2"
和 "plantName3"
)匹配的键,传递给 cache
工作器的数据将具有三个键值对。
如果存在冲突,那么最后一个工作器将在争用中“取胜”,其值将传递给 cache
。
假设我们要保留所有植物名称工作器的输出,则应使用 ArrayCreatingInputMerger
val cache: OneTimeWorkRequest = OneTimeWorkRequestBuilder<PlantWorker>()
.setInputMerger(ArrayCreatingInputMerger::class)
.setConstraints(constraints)
.build()
ArrayCreatingInputMerger
将每个键与数组配对。如果每个键都是唯一的,您会得到一系列一元数组
如果存在任何键冲突,所有对应的值会分组到一个数组中
2.5 自定义WorkManager
默认情况下,应用启动时,WorkManager
使用适合大多数应用的合理选项自动进行配置。如果需要进一步控制 WorkManager
管理和调度工作的方式,可以通过自行初始化 WorkManager
来自定义 WorkManager
配置。
按需初始化
通过按需初始化,可以在仅需要WorkManager
时创建该组件,不用每次应用启动都创建,提高应用启动性能。
如需提供自己的配置,必须先移除默认初始化程序,使用合并规则 tools:node="remove"
更新 AndroidManifest.xml
。
从 WorkManager 2.6
开始,应用启动功能在WorkManager
内部使用。若需提供自定义初始化程序,需移除androidx.startup
节点
<!-- 如果您不在应用中使用应用启动功能,则可以将其彻底移除 -->
<provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" tools:node="remove">
</provider>
<!-- 仅移除 WorkManagerInitializer 节点 -->
<provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge">
<!-- If you are using androidx.startup to initialize other components -->
<meta-data android:name="androidx.work.WorkManagerInitializer" android:value="androidx.startup" tools:node="remove" />
</provider>
实现Configguration.Provider
让您的 Application
类实现 Configuration.Provider
接口,并提供您自己的 Configuration.Provider.getWorkManagerConfiguration
() 实现。
class App : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration() =
Configuration.Builder()
.setWorkerFactory(RenameWorkerFactory())
.setMinimumLoggingLevel(Log.VERBOSE)
.build()
}
如需查看可用自定义的完整列表,请参阅 Configuration.Builder()
参考文档
2.6 自定义WorkerFactory
使用WorkManager
时,需要定义一个Worker
的子类。在某个时刻,WorkManager
会通过WorkerFactory
反射实例化你定义的Worker
。默认的WorkerFactory
创建Worker
需要两个参数:
Application’s Context
WorkerParameters
实际开发中,可能需要为Worker
构造函数添加其他参数,满足我们的功能需求,例如需要引用Retrofit
服务跟远程服务器进行通讯,这时就需要自定义WorkerFactory
。
创建自定义WorkerFactory
的步骤其实就是在自定义WorkManager
步骤基础上加上对自定义的WorkerFactory
的设置
class MyWorkerFactory(private val service: UpvoteStoryHttpApi) : WorkerFactory() {
override fun createWorker( appContext: Context, workerClassName: String, workerParameters: WorkerParameters ): ListenableWorker? {
return UpvoteStoryWorker(appContext, workerParameters, DesignerNewsService)
}
}
class MyApplication : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration(): Configuration =
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.DEBUG)
.setWorkerFactory(MyWorkerFactory(DesignerNewsService))
.build()
...
}
DelegatingWorkerFactory
使用
实际开发中可能会有多个Worker
类,示例中直接返回某个特定Worker
实例的操作就不是很适用,可以通过DelegatingWorkerFactory
解决。
可以使用DelegatingWorkerFactory
,将其设置到我们的WorkerFactory
中,从而支持多个多个工厂。这种情况下,如果一个WorkerFactory
没有创建Worker
,DelegatingFactory
会去找到下一个WorkerFactory
class MyWorkerFactory(private val service: DesignerNewsService) : WorkerFactory() {
override fun createWorker( appContext: Context, workerClassName: String, workerParameters: WorkerParameters ): ListenableWorker? {
return when(workerClassName) {
UpvoteStoryWorker::class.java.name ->
ConferenceDataWorker(appContext, workerParameters, service)
else ->
// 返回 null,这样基类就可以代理到默认的 WorkerFactory
null
}
}
}
上述情况,工厂会检查是否知道如何处理作为参数传入的workerClassName
,如果不知道,就返回null
,而 DelegatingWorkerFactory
便会去寻找下一个注册的工厂。如果没有任何被注册的工厂知道如何处理某个类,那么它将回退到使用反射的默认工厂。
同时我们需要修改一下前述的WorkManager
配置
class MyApplication : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration(): Configuration {
val myWorkerFactory = DelegatingWorkingFactory()
myWorkerFactory.addFactory(MyWorkerFactory(service))
// 在这里添加您应用中可能会使用的其他 WorkerFactory
return Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.INFO)
.setWorkerFactory(myWorkerFactory)
.build()
}
...
}
2.7 线程处理
WorkManager
提供了四种不同类型的工作基元:
-
Worker
最简单的实现,
WorkManager
会在后台线程中自动运行该基元(您可以将它替换掉) -
CoroutineWorker
Kotlin
用户建议实现。CoroutineWorker
实例公开了后台工作的一个挂起函数。默认情况下,这些实例运行默认的Dispatcher
,也可以自行自定义 -
RxWorker
RxJava
建议实现。如果很多异步代码是用RxJava
建模的,则应使用RxWorker
。与所有RxJava
概念一样,可以自由选择所需的线程处理策略 -
ListenableWorker
Worker
、CoroutineWorker
和RxWorker
的基类,专为需要与基于回调的异步 API(例如FusedLocationProviderClient
)进行交互并且不使用RxJava
的Java
开发者而设计
Worker
中线程处理
使用 Worker
时,WorkManager
会自动在后台线程中调用 Worker.doWork()
。该后台线程来自WorkManager
的Configuration
中指定的 Executor
。你可以通过自定义的方式修改Executor
WorkManager.initialize(
context,
Configuration.Builder()
// Uses a fixed thread pool of size 8 threads.
.setExecutor(Executors.newFixedThreadPool(8))
.build())
Worker.doWork()
是同步调用,应以阻塞方式完成整个后台工作,并在方法退出时完成工作。如果在doWork()
中调用异步API并返回Result
,回调可能无法正常允许,这种情况,考虑使用ListenableWorker
class DownloadWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): ListenableWorker.Result {
repeat(100) {
if (isStopped) {
break
}
try {
downloadSynchronously("https://www.google.com")
} catch (e: IOException) {
return ListenableWorker.Result.failure()
}
}
return ListenableWorker.Result.success()
}
}
CoroutineWorker
中线程处理
WorkManager
为协程提供了一流的支持。CoroutineWorker
中包含了doWork
的挂起版本。
class CoroutineDownloadWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
val data = downloadSynchronously("https://www.google.com")
saveData(data)
return Result.success()
}
}
CoroutineWorker.doWork()
是一个“挂起”函数。此代码不同于 Worker
,不会在 Configuration
中指定的 Executor
中运行,而是默认为 Dispatchers.Default
。您可以提供自己的 CoroutineContext
来自定义这个行为。
class CoroutineDownloadWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
withContext(Dispatchers.IO) {
val data = downloadSynchronously("https://www.google.com")
saveData(data)
return Result.success()
}
}
}
CoroutineWorker
通过取消协程并传播取消信号来自动处理停工情况。
可以使用 RemoteCoroutineWorker
(ListenableWorker
的实现)将工作器绑定到特定进程。详细操作可参看WorkManagerMultiProcessSample
ListenableWorker
中线程处理
如果需要处理基于回调的异步操作,这种情况无法依靠Worker
来完成,因为器无法以阻塞方式完成工作,这时就需要ListenableWorker
抽象方法 ListenableWorker.startWork()
会返回一个将使用操作的 Result
设置的 ListenableFuture
,其是一个Future
,用于提供附加监听器和传播异常的功能。
class CallbackWorker(
context: Context,
params: WorkerParameters
) : ListenableWorker(context, params) {
override fun startWork(): ListenableFuture<Result> {
return CallbackToFutureAdapter.getFuture { completer ->
val callback = object : Callback {
var successes = 0
override fun onFailure(call: Call, e: IOException) {
completer.setException(e)
}
override fun onResponse(call: Call, response: Response) {
++successes
if (successes == 100) {
completer.set(Result.success())
}
}
}
//添加取消监听器
completer.addCancellationListener(cancelDownloadsRunnable, executor)
repeat(100) {
downloadAsynchronously("https://example.com", callback)
}
callback
}
}
}
三、原理解析
关于WorkManager
原理解析,基于2.7.1
版本
关于WorkMannager
的原理机制,可以参看下图:
WorkManager
的原理,简单总结为:
WorkManager
的初始化是在app冷启动后,包含了Configuration
,WorkManagerTaskExecutor
,WorkDatabase
,Schedulers
,Processor
等的初始化过程Schedulers
有两个,其中GreedyScheduler
执行没有任何约束的非周期性的任务;SystemJobScheduler/GcmBasedScheduler/SystemAlarmScheduler
:执行周期性或者有约束性的任务- 对于非约束条件,执行
enqueue
后,调用到WorkContinuationImpl
的enqueue
方法,其将持有的EnqueueRunnable
对象将任务添加到db
,并交给Schedulers
去调度,交由GreedyScheduler
处理 GreedyScheduler
经过一系列判断后,调用WorkManager
的startWork()
方法,将持有的StartWorkRunnable
对象会将任务交给Processor
去处理,执行startWork()
方法。Processor
创建一个WorkerWrapper
对象,由它去调用Worker
的startWork()
方法,执行我们自定义worker
的任务,并返回相应的result
。之后对db进行更新,执行下一个任务。- 对于有约束条件的周期性任务,是根据
build version
交由SystemJobScheduler
或SystemAlarmScheduler
进行处理。
3.1 WorkManager初始化
关于WorkManager
的初始化在自定义WorkManager
部分其实已经点明了。从 WorkManager 2.6
开始,应用启动功能在WorkManager
内部使用。
直接看一下WorkManagerInitializer
/** * Initializes {@link androidx.work.WorkManager} using {@code androidx.startup}. */
public final class WorkManagerInitializer implements Initializer<WorkManager> {
@NonNull
@Override
public WorkManager create(@NonNull Context context) {
// Initialize WorkManager with the default configuration.
Logger.get().debug(TAG, "Initializing WorkManager with default configuration.");
WorkManager.initialize(context, new Configuration.Builder().build());
return WorkManager.getInstance(context);
}
......
}
WorkManager
是个单例,初始化之前会创建一个默认的Configuration
,设置管理和调度工作的方式。看一下initialize()
方法
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
WorkManagerImpl.initialize(context, configuration);
}
WorkManager
是个抽象类,真正构造方法在子类WorkManagerImpl
实现
/** * Initializes the singleton instance of {@link WorkManagerImpl}. You should only do this if * you want to use a custom {@link Configuration} object and have disabled * WorkManagerInitializer. * * @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
synchronized (sLock) {
if (sDelegatedInstance != null && sDefaultInstance != null) {
throw new IllegalStateException("//......");
}
if (sDelegatedInstance == null) {
context = context.getApplicationContext();
if (sDefaultInstance == null) {
sDefaultInstance = new WorkManagerImpl(
context,
configuration,
new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
}
sDelegatedInstance = sDefaultInstance;
}
}
}
initialize()
方法初始化一个WorkManagerImpl
对象,并传入一个WorkManagerTaskExecutor
对象,用来执行WorkManager
的任务。
WorkManagerTaskExecutor
可自行点击源码查看,其创建了一个 SerialExecutor
对象,里面初始化了一个ArrayDeque
双端队列,用于保证后期创建的任务顺序执行。其中Executor
是 Configuration
中创建的固定大小线程池。
跟踪上述WorkManagerImpl
的构造函数,最后走到下面这个构造函数
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public WorkManagerImpl( @NonNull Context context, @NonNull Configuration configuration, @NonNull TaskExecutor workTaskExecutor, @NonNull WorkDatabase database) {
Context applicationContext = context.getApplicationContext();
Logger.setLogger(new Logger.LogcatLogger(configuration.getMinimumLoggingLevel()));
List<Scheduler> schedulers =
createSchedulers(applicationContext, configuration, workTaskExecutor);
Processor processor = new Processor(
context,
configuration,
workTaskExecutor,
database,
schedulers);
internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
}
WorkManagerImpl
构造函数主要内容:
- 完成创建数据库操作,使用
Room
框架 createSchedulers
创建调度者集合- 创建
Processor
类,管理Schedulers
的执行
Scheduler
创建
看一下createSchedulers()
方法实现
public List<Scheduler> createSchedulers( @NonNull Context context, @NonNull Configuration configuration, @NonNull TaskExecutor taskExecutor) {
return Arrays.asList(
Schedulers.createBestAvailableBackgroundScheduler(context, this),
new GreedyScheduler(context, configuration, taskExecutor, this));
}
createSchedulers()
返回一个Scheduler
数组,其中createBestAvailableBackgroundScheduler()
创建一个最有效的后台调度者
static Scheduler createBestAvailableBackgroundScheduler( @NonNull Context context, @NonNull WorkManagerImpl workManager) {
Scheduler scheduler;
if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
scheduler = new SystemJobScheduler(context, workManager);
setComponentEnabled(context, SystemJobService.class, true);
Logger.get().debug(TAG, "Created SystemJobScheduler and enabled SystemJobService");
} else {
scheduler = tryCreateGcmBasedScheduler(context);
if (scheduler == null) {
scheduler = new SystemAlarmScheduler(context);
setComponentEnabled(context, SystemAlarmService.class, true);
Logger.get().debug(TAG, "Created SystemAlarmScheduler");
}
}
return scheduler;
}
createBestAvailableBackgroundScheduler()
方法主要内容:
- 如果Android版本号>=23,返回
SystemJobScheduler
,使用JobScheduler
完成调度 - 如果手机支持
GCM
,则返回GcmScheduler
调度者 - 其他情况返回
SystemAlarmScheduler
,内部使用AlarmManager
实现原理
internalInit
实现
回到WorkManagerImpl
构造函数,看一下internalInit
实现
private void internalInit(@NonNull Context context, @NonNull Configuration configuration, @NonNull TaskExecutor workTaskExecutor, @NonNull WorkDatabase workDatabase, @NonNull List<Scheduler> schedulers, @NonNull Processor processor) {
//......
mWorkTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));
}
public class ForceStopRunnable implements Runnable {
@Override
public void run() {
//......
try {
forceStopRunnable();
break;
}
//......
}
public void forceStopRunnable() {
boolean needsScheduling = cleanUp();
if (shouldRescheduleWorkers()) {
Logger.get().debug(TAG, "Rescheduling Workers.");
mWorkManager.rescheduleEligibleWork();
// Mark the jobs as migrated.
mWorkManager.getPreferenceUtils().setNeedsReschedule(false);
} else if (isForceStopped()) {
Logger.get().debug(TAG, "Application was force-stopped, rescheduling.");
mWorkManager.rescheduleEligibleWork();
} else if (needsScheduling) {
Logger.get().debug(TAG, "Found unfinished work, scheduling it.");
Schedulers.schedule(
mWorkManager.getConfiguration(),
mWorkManager.getWorkDatabase(),
mWorkManager.getSchedulers());
}
}
}
ForceStopRunnable
的主要作用是在WorkManager
初始化过程中,发现了未完成的,需要重新执行的任务,或者app
被强制kill
的情况下,直接对Scheduler
进行调度
WorkManager
初始化小结:
-
WorkManager
的初始化是在app冷启动后,由WorkManagerInitializer
这个ContentProvider
执行的。 -
初始化过程包含了
Configuration
,WorkManagerTaskExecutor
,WorkDatabase
,Schedulers
,Processor
等的初始化过程。 -
Schedulers
有两个GreedyScheduler
:执行没有任何约束的非周期性的任务SystemJobScheduler/GcmBasedScheduler/SystemAlarmScheduler
:执行周期性或者有约束性的任务 -
初始化的最后,会根据情况找到需要被执行的任务进行调度执行。
3.2 WorkRequest创建
关于WorkRequest
创建内容较为简单,这里不详细介绍,自行查看源码。
WorkRequest
的创建是为了持有三个重要的成员变
mId
:由UUID生成的任务id。mWorkSpec
:每个任务的属性。mTags
:每个任务的标签。
3.3 非约束条件任务执行
介绍WorkManager
使用时,定义完Worker
和WorkerRequest
,需要将工作提交到任务队列中
WorkManager.getInstance(this).enqueue(workRequest)
WorkManager
的实际实现类是WorkManagerImpl
,看一下内部的enqueue()
实现,最终调用WorkContinuationImpl
实例的enqueue
方法
@Override
@NonNull
public Operation enqueue( @NonNull List<? extends WorkRequest> requests) {
//......
return new WorkContinuationImpl(this, requests).enqueue();
}
WorkContinuationImpl
是WorkContinuation
的子类,该类保存了任务相关的所有信息,如WorkManager
、WorkRequest
等。
看一下WorkContinuationImpl
的enqueue
实现
@Override
public @NonNull Operation enqueue() {
// Only enqueue if not already enqueued.
if (!mEnqueued) {
// The runnable walks the hierarchy of the continuations
// and marks them enqueued using the markEnqueued() method, parent first.
EnqueueRunnable runnable = new EnqueueRunnable(this);
mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);
mOperation = runnable.getOperation();
} else {
Logger.get().warning(TAG,
String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
}
return mOperation;
}
enqueue()
方法主要内容:
- 创建
EnqueueRunnable
- 获取
WorkManagerTaskExecutor
对象 - 通过之前在
Configuration
创建的线程池中执行EnqueueRunnable
任务。
任务分配
看一下EnqueueRunnable
实现
public class EnqueueRunnable implements Runnable {
//......
@Override
public void run() {
try {
if (mWorkContinuation.hasCycles()) {
throw new IllegalStateException(
String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
}
//任务持久化保存到数据库中
boolean needsScheduling = addToDatabase();
if (needsScheduling) {
// Enable RescheduleReceiver, only when there are Worker's that need scheduling.
final Context context =
mWorkContinuation.getWorkManagerImpl().getApplicationContext();
PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true);
//调度任务执行
scheduleWorkInBackground();
}
mOperation.setState(Operation.SUCCESS);
} catch (Throwable exception) {
mOperation.setState(new Operation.State.FAILURE(exception));
}
}
@VisibleForTesting
public void scheduleWorkInBackground() {
WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
Schedulers.schedule(
workManager.getConfiguration(),
workManager.getWorkDatabase(),
workManager.getSchedulers());
}
}
run
方法先会调用addToDatabase
根据策略不同将任务添加到数据库或者移除,然后调用 PackageManagerHelper
将RescheduleReceiver
注册到 manifest.xml
内。
EnqueueRunnable
类 在scheduleWorkInBackground
方法中调度任务执行,内部调用Schedulers
类的schedule
方法,分配任务。
public static void schedule( @NonNull Configuration configuration, @NonNull WorkDatabase workDatabase, List<Scheduler> schedulers) {
if (schedulers == null || schedulers.size() == 0) {
return;
}
WorkSpecDao workSpecDao = workDatabase.workSpecDao();
List<WorkSpec> eligibleWorkSpecsForLimitedSlots;
List<WorkSpec> allEligibleWorkSpecs;
//获取到需要执行的任务数据
workDatabase.beginTransaction();
try {
eligibleWorkSpecsForLimitedSlots = workSpecDao.getEligibleWorkForScheduling(
configuration.getMaxSchedulerLimit());
//......
}finally {
workDatabase.endTransaction();
}
//......
for (Scheduler scheduler : schedulers) {
if (scheduler.hasLimitedSchedulingSlots()) {
scheduler.schedule(eligibleWorkSpecsArray);
}
}
//......
for (Scheduler scheduler : schedulers) {
if (!scheduler.hasLimitedSchedulingSlots()) {
scheduler.schedule(eligibleWorkSpecsArray);
}
}
}
这里关注没有约束条件任务的执行,看一下GreedyScheduler
内schedule()
实现
@Override
public void schedule(@NonNull WorkSpec... workSpecs) {
//......
for (WorkSpec workSpec : workSpecs) {
long nextRunTime = workSpec.calculateNextRunTime();
long now = System.currentTimeMillis();
//WorkSpec是ENQUEUED的状态
if (workSpec.state == WorkInfo.State.ENQUEUED) {
//延迟任务
if (now < nextRunTime) {
// Future work
if (mDelayedWorkTracker != null) {
mDelayedWorkTracker.schedule(workSpec);
}
} else if (workSpec.hasConstraints()) { //存在约束条件
if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {
// Ignore requests that have an idle mode constraint.
Logger.get().debug(TAG,
String.format("Ignoring WorkSpec %s, Requires device idle.",
workSpec));
} else if (SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) {
// Ignore requests that have content uri triggers.
Logger.get().debug(TAG,
String.format("Ignoring WorkSpec %s, Requires ContentUri triggers.",
workSpec));
} else {
constrainedWorkSpecs.add(workSpec);
constrainedWorkSpecIds.add(workSpec.id);
}
} else {
Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id));
//执行任务
mWorkManagerImpl.startWork(workSpec.id);
}
}
}
// onExecuted() which is called on the main thread also modifies the list of mConstrained
// WorkSpecs. Therefore we need to lock here.
synchronized (mLock) {
if (!constrainedWorkSpecs.isEmpty()) {
Logger.get().debug(TAG, String.format("Starting tracking for [%s]",
TextUtils.join(",", constrainedWorkSpecIds)));
mConstrainedWorkSpecs.addAll(constrainedWorkSpecs);
mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);
}
}
}
GreedyScheduler
的schedule
方法在判断任务满足没有约束条件等要求后,直接调用startWork
执行任务。
WorkManagerImpl.startWork
最终调用到WorkManagerTaskExecutor
的executeOnBackgroundThread
方法执行StartWorkRunnable
。
public void startWork( @NonNull String workSpecId, @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
mWorkTaskExecutor
.executeOnBackgroundThread(
new StartWorkRunnable(this, workSpecId, runtimeExtras));
}
public class StartWorkRunnable implements Runnable {
//......
public void run() {
mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);
}
}
getProcessor()
方法获取的是创建WorkManager
时创建的Processor
对象。看一下该方法实现
public boolean startWork( @NonNull String id, @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
WorkerWrapper workWrapper;
synchronized (mLock) {
//......
//数据封装成 WorkerWrapper
workWrapper =
new WorkerWrapper.Builder(
mAppContext,
mConfiguration,
mWorkTaskExecutor,
this,
mWorkDatabase,
id)
.withSchedulers(mSchedulers)
.withRuntimeExtras(runtimeExtras)
.build();
ListenableFuture<Boolean> future = workWrapper.getFuture();
future.addListener(
new FutureListener(this, id, future),
mWorkTaskExecutor.getMainThreadExecutor());
mEnqueuedWorkMap.put(id, workWrapper);
}
mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
return true;
}
Processor
的startWork
方法将数据包装为WorkerWrapper
,然后再次调用WorkManagerTaskExecutor
类执行WorkerWrapper
任务
WorkerWrapper
继承自Runnable
,看一下其实现
@WorkerThread
@Override
public void run() {
mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
mWorkDescription = createWorkDescription(mTags);
runWorker();
}
private void runWorker() {
//......
//创建一个WorkerParameters对象
final WorkerParameters params = new WorkerParameters(
UUID.fromString(mWorkSpecId),
input,
mTags,
mRuntimeExtras,
mWorkSpec.runAttemptCount,
mConfiguration.getExecutor(),
mWorkTaskExecutor,
mConfiguration.getWorkerFactory(),
new WorkProgressUpdater(mWorkDatabase, mWorkTaskExecutor),
new WorkForegroundUpdater(mWorkDatabase, mForegroundProcessor, mWorkTaskExecutor));
//创建Worker对象
if (mWorker == null) {
mWorker = mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback(
mAppContext,
mWorkSpec.workerClassName,
params);
}
//......
final ListenableFuture<Void> runExpedited = foregroundRunnable.getFuture();
runExpedited.addListener(new Runnable() {
@Override
public void run() {
try {
runExpedited.get();
Logger.get().debug(TAG,
String.format("Starting work for %s", mWorkSpec.workerClassName));
// Call mWorker.startWork() on the main thread.
mInnerFuture = mWorker.startWork();
future.setFuture(mInnerFuture);
} catch (Throwable e) {
future.setException(e);
}
}
}, mWorkTaskExecutor.getMainThreadExecutor());
//......
}
//Worker.java
@Override
public final @NonNull ListenableFuture<Result> startWork() {
mFuture = SettableFuture.create();
getBackgroundExecutor().execute(new Runnable() {
@Override
public void run() {
try {
Result result = doWork();
mFuture.set(result);
} catch (Throwable throwable) {
mFuture.setException(throwable);
}
}
});
return mFuture;
}
WorkerWrapper
的主要内容:
- 通过反射机制获取到
ListenableWorker
对象 - 调用
ListenableWorker.startWork
,实际上是调用Worker
类的startWork
方法 - 在
Worker
类的startWork
方法中又会调用doWork
方法,也就是我们复写的doWork
方法。
非约束条件任务执行小结:
WorkManager
执行了enqueue()
后,创建WorkContinuationImpl
对象执行enqueue()
方法。WorkContinuationImpl
持有的EnqueueRunnable
对象将任务添加到db
,并交给Schedulers
去调度。Schedulers
将任务交给每一个Scheduler
去处理,对于非约束条件的一次性任务由GreedyScheduler
处理GreedyScheduler
经过一系列判断后,调用WorkManager
的startWork()
方法执行这种一次性,非延迟,无约束的任务。WorkManager
持有的StartWorkRunnable
对象会将任务交给Processor
去处理,执行startWork()
方法。Processor
创建一个WorkerWrapper
对象,由它去调用Worker
的startWork()
方法,执行我们自定义worker
的任务,并返回相应的result
。- 任务完成后,
WorkerWrapper
会根据result
对任务状态,db
等进行更新,然后schedule
下一个任务。
3.4 带约束条件任务执行
对于带约束条件的任务,是根据build version
交由SystemJobScheduler
或SystemAlarmScheduler
进行处理。
这里仅看一下SystemJobScheduler
SystemJobScheduler
使用的是JobScheduler
来调度执行任务。通常JobScheduler
的使用步骤如下:
- 创建
JobService
。 - 配置
JobInfo
。 - 执行。
public SystemJobScheduler(@NonNull Context context, @NonNull WorkManagerImpl workManager) {
this(context,
workManager,
(JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE),//获取到了系统服务JobScheduler进行重载
new SystemJobInfoConverter(context));//用来将我们传递的数据转换为JobInfo类型
}
public SystemJobScheduler( Context context, WorkManagerImpl workManager, JobScheduler jobScheduler, SystemJobInfoConverter systemJobInfoConverter) {
mContext = context;
mWorkManager = workManager;
mJobScheduler = jobScheduler;
mSystemJobInfoConverter = systemJobInfoConverter;
}
直接看一下SystemJobScheduler
的schedule()
实现
@Override
public void schedule(@NonNull WorkSpec... workSpecs) {
WorkDatabase workDatabase = mWorkManager.getWorkDatabase();
IdGenerator idGenerator = new IdGenerator(workDatabase);
//......
WorkSpec currentDbWorkSpec = workDatabase.workSpecDao().getWorkSpec(workSpec.id);
//......
//获取对应的系统编号
SystemIdInfo info = workDatabase.systemIdInfoDao()
.getSystemIdInfo(workSpec.id);
//......
//如果info为空,通过jobId与workSpec.id生成新的数据插入SystemIdInfo
if (info == null) {
SystemIdInfo newSystemIdInfo = new SystemIdInfo(workSpec.id, jobId);
mWorkManager.getWorkDatabase()
.systemIdInfoDao()
.insertSystemIdInfo(newSystemIdInfo);
}
//scheduleInternal方法
scheduleInternal(workSpec, jobId);
//......
}
看一下scheduleInternal
实现
@VisibleForTesting
public void scheduleInternal(WorkSpec workSpec, int jobId) {
//将workspec转化为jobinfo
JobInfo jobInfo = mSystemJobInfoConverter.convert(workSpec, jobId);
//......
//将jobInfo放入JobScheduler中
int result = mJobScheduler.schedule(jobInfo);
//......
}
JobScheduler
是系统服务,由android
程序启动的时候自动拉起无需手动去操作,与JobScheduler
对应的jobservice
由系统自动生成。
系统自动实现了一个SystemJobService
,当JobScheduler
中的条件满足时,SystemJobService
就会被调用到。
执行任务的服务类,在onStartJob()
中,会调用WorkManagerImpl
的startWork()
执行任务
@Override
public boolean onStartJob(@NonNull JobParameters params) {
//......
mWorkManagerImpl.startWork(workSpecId, runtimeExtras);
return true;
}
关于mWorkManagerImpl.startWork()
过程不再赘述,与3.3部分的相关内容一致。
今天的文章WorkManager组件解析分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/19375.html