目前执行长期的后台任务时候,官方推荐使用WorkManager
来处理后台任务,这里对WorkManager
进行一个简单的记录。WorkManager
主要可以执行以下几种任务
dependencies {
val work_version = "2.7.1"
// (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
需要三部分组成。定义一个Work
,创建一个WorkRequest
,提交给WorkManager
进行执行。以下是一个简单的一次性任务的示例。
WorkTest.kt
class WorkTest(val appContext: Context, workerParams: WorkerParameters): Worker(appContext, workerParams) {
override fun doWork(): Result {
Log.e("YM--->","后台任务执行中,任务线程:${Thread.currentThread().id}-->线程名字:${Thread.currentThread().name}")
return Result.success()
}
}
以下是两种创建WorkRequest
的方式,一种复杂的,一种简单的,根据情况选择一种实现。
private fun initWork() {
// val uploadWorkRequest: WorkRequest = //复杂的构建器方式
// OneTimeWorkRequestBuilder<WorkTest>()
// .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)//设置加急任务
// .build()
val uploadWorkRequest: WorkRequest = OneTimeWorkRequest.from(WorkTest::class.java)//简单方式
WorkManager
.getInstance(this)
.enqueue(periodicWorkRequest)
}
使用WorkManager
进行执行
WorkManager
.getInstance(this)
.enqueue(periodicWorkRequest)
定期任务可以多次执行一个任务。以下是创建方式
val saveRequest =
PeriodicWorkRequestBuilder<WorkTest>(1, TimeUnit.HOURS)
// Additional configuration
.build()
需要注意下时间,如果时间小于15分钟,则会按照十五分钟进行计算,如果大于十五分钟则按照实际时间计算。
假设想要定义一个灵活的时间段,比如最后十五分钟执行任务,可以使用以下方式
val myUploadWork = PeriodicWorkRequestBuilder<SaveImageToFileWorker>(
1, TimeUnit.HOURS, // repeatInterval (the period cycle)
15, TimeUnit.MINUTES) // flexInterval
.build()
在创建 PeriodicWorkRequest
时传递 flexInterval
以及 repeatInterval
。灵活时间段从 repeatInterval - flexInterval
开始,一直到间隔结束。
重复间隔必须大于或等于 PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS,而灵活间隔必须大于或等于 PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS。
这里参考官方网站:
约束可确保将工作延迟到满足最佳条件时运行。以下约束适用于 WorkManager。
NetworkType 约束运行工作所需的网络类型。例如 Wi-Fi ( [UNMETERED](https://developer.android.google.cn/reference/androidx/work/NetworkType?hl=zh-cn#UNMETERED)
)。BatteryNotLow 如果设置为 true,那么当设备处于“电量不足模式”时,工作不会运行。 RequiresCharging 如果设置为 true,那么工作只能在设备充电时运行。 DeviceIdle 如果设置为 true,则要求用户的设备必须处于空闲状态,才能运行工作。在运行批量操作时,此约束会非常有用;若是不用此约束,批量操作可能会降低用户设备上正在积极运行的其他应用的性能。 StorageNotLow 如果设置为 true,那么当用户设备上的存储空间不足时,工作不会运行。 如需创建一组约束并将其与某项工作相关联,请使用一个
Contraints.Builder()
创建Constraints
实例,并将该实例分配给WorkRequest.Builder()
。例如,以下代码会构建了一个工作请求,该工作请求仅在用户设备正在充电且连接到 Wi-Fi 网络时才会运行:
val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.UNMETERED) .setRequiresCharging(true) .build() val myWorkRequest: WorkRequest = OneTimeWorkRequestBuilder<MyWork>() .setConstraints(constraints) .build()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
如果指定了多个约束,工作将仅在满足所有约束时才会运行。
如果在工作运行时不再满足某个约束,WorkManager 将停止工作器。系统将在满足所有约束后重试工作。
上面的任务默认来说会立即执行,倘若想要延迟执行的话可以采取以下方式:
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setInitialDelay(10, TimeUnit.MINUTES)
.build()
需要注意的是定时任务,只有首次会延迟。
倘若任务执行失败,想要进行重试,可以使用该方式。重试有两个参数构成:重试时间和重试策略。重试时间是指每次重试前的等待时间,最短10s或者 MIN_BACKOFF_MILLIS。重试策略则是指后续的重试时间以何种方式增涨,有两个参数,一个是线性增涨LINEAR,一个是指数增长EXPONENTIAL。不能设置每次固定的时间。如果使用重试的话,需要在定义 Worker
时候返回Result.retry()
,不能返回Result.success()
。以下是参考代码:
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setBackoffCriteria(
BackoffPolicy.LINEAR,
OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS)
.build()
以下参考官网网站:
每个工作请求都可以设置一个唯一标识符,该标识符可用于在以后标识该工作,以便取消工作或观察其进度。
如果有一组在逻辑上相关的工作,对这些工作项进行标记可能也会很有帮助。通过标记,您一起处理一组工作请求。
例如,WorkManager.cancelAllWorkByTag(String) 会取消带有特定标记的所有工作请求,WorkManager.getWorkInfosByTag(String)会返回一个 WorkInfo 对象列表,该列表可用于确定当前工作状态。
以下代码展示了如何向工作添加“cleanup”标记:
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>() .addTag("cleanup") .build()
- 1
- 2
- 3
最后,可以向单个工作请求添加多个标记。这些标记在内部以一组字符串的形式进行存储。您可以使用 WorkInfo.getTags() 获取与
WorkRequest
关联的标记集。从
Worker
类中,您可以通过 ListenableWorker.getTags() 检索其标记集。
有些工作需要传递一些参数。可以使用以下的方式, 示例代码如下:
// Define the Worker requiring input
class UploadWork(appContext: Context, workerParams: WorkerParameters)
: Worker(appContext, workerParams) {
override fun doWork(): Result {
val imageUriInput =
inputData.getString("IMAGE_URI") ?: return Result.failure()
uploadFile(imageUriInput)
return Result.success()
}
...
}
// Create a WorkRequest for your Worker and sending it input
val myUploadWork = OneTimeWorkRequestBuilder<UploadWork>()
.setInputData(workDataOf(
"IMAGE_URI" to "http://..."
))
.build()
加急任务据官网说是对那些需要立即执行的任务,比如添加订阅,支付账单,发送消息。但是初步接触,没有发现和其它任务的区别。可能是在WorkManager
有很多任务时候,会导致任务排队,加急任务可以将任务插入到队前进行加快执行。
加急任务是在WorkManager2.7
的时候添加的。并且可能在Android12之前的版本上运行前台服务。所以在一定程度上限制了其使用场景。
以下是引用内容
在 Android 12 之前,工作器中的
getForegroundInfoAsync()
和getForegroundInfo()
方法可让 WorkManager 在您调用setExpedited()
时显示通知。如果您想要请求任务作为加急作业运行,则所有的 ListenableWorker 都必须实现
getForegroundInfo
方法。注意:如果未能实现对应的
getForegroundInfo
方法,那么在旧版平台上调用setExpedited
时,可能会导致运行时崩溃。以 Android 12 或更高版本为目标平台时,前台服务仍然可通过对应的
setForeground
方法使用。注意:
setForeground()
可能会在 Android 12 上抛出运行时异常,并且在启动受到限制时可能会抛出异常。工作器
工作器不知道自身所执行的工作是否已加急。不过,在某些版本的 Android 上,如果
WorkRequest
被加急,工作器可以显示通知。为此,WorkManager 提供了
getForegroundInfoAsync()
方法,您必须实现该方法,让 WorkManager 在必要时显示通知,以便启动ForegroundService
。CoroutineWorker
如果您使用
CoroutineWorker
,则必须实现getForegroundInfo()
。然后,在doWork()
内将其传递给setForeground()
。这样做会在 Android 12 之前的版本中创建通知。请参考以下示例:
class ExpeditedWorker(appContext: Context, workerParams: WorkerParameters): CoroutineWorker(appContext, workerParams) { override suspend fun getForegroundInfo(): ForegroundInfo { return ForegroundInfo( NOTIFICATION_ID, createNotification() ) } override suspend fun doWork(): Result { TODO() } private fun createNotification() : Notification { TODO() }}
- 1
注意:您应该将
setForeground()
封装在try/catch
块中,以捕获可能出现的IllegalStateException
。如果您的应用此时无法在前台运行,便可能会发生这类异常。在 Android 12 及更高版本中,您可以使用更详细的ForegroundServiceStartNotAllowedException
。配额政策
您可以控制当应用达到其执行配额时加急工作会发生什么情况。如需继续,您可以传递
setExpedited()
:
OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST
,这会导致作业作为普通工作请求运行。上述代码段演示了此操作。OutOfQuotaPolicy.DROP_WORK_REQUEST
,这会在配额不足时导致请求取消。示例应用
如需查看关于 WorkManager 2.7.0 如何使用加急工作的完整示例,请查看 GitHub 上的 WorkManagerSample。
延迟加急工作
系统会尝试在调用指定的加急作业后,尽快执行该作业。不过,与其他类型的作业一样,系统可能会延迟启动新的加急工作,如在以下情况下:
- 负载:系统负载过高,当有过多作业已在运行或者当系统内存不足时,就会发生这种情况。
- 配额:已超出加急作业配额限制。加急工作使用基于应用待机存储分区的配额系统,并限制滚动时间窗口中的最大执行时间。用于加急工作的配额比用于其他类型的后台作业的配额限制性更强。
需要注意的是,上文继承的Work
类,是ListenableWorker
的子类,所以依然要重写getForegroundInfoAsync()
函数。