1、什么是WorkManager

按照官方描述,WorkManager 是适合用于持久性作业的推荐解决方案。假如作业始终要经过运用重启和体系从头启动来调度,便是持久性的作业。因为大多数后台处理操作都是经过持久性作业完成的,因而 WorkManager 是适用于后台处理操作的首要推荐 API。

2、使命类型

WorkManager使命类型分为当即运转、长时间运转和延期履行,运用办法与周期关系如下所示:

当即 一次性 OneTimeWorkRequest 和 Worker。如需处理加急作业,请对 OneTimeWorkRequest 调用 setExpedited()。
长时间运转 一次性或守时 任意 WorkRequest 或 Worker。在作业器中调用 setForeground() 来处理告知。
可延期 一次性或守时 PeriodicWorkRequest 和 Worker。

接下来来看详细的运用办法。

3、入门运用

3.1 增加依靠库

本文代码运用Kotlin编写,所以这儿仅引进Kotlin相关的库即可,在build.gradle中增加代码如下所示:

def work_version = "2.7.1"
implementation "androidx.work:work-runtime-ktx:$work_version"

假如运用的是Java语言该如何引用呢?听我的,放弃吧~

3.2 界说作业Worker

这儿咱们以上传日志文件使命为例,新建UploadLogWorker类,承继自Worker,代码如下所示:

class UploadLogWorker(context: Context, workerParams: WorkerParameters) :
    Worker(context, workerParams) {
    override fun doWork(): Result {
        Log.d("打印线程", Thread.currentThread().name)
        return Result.success()
    }
}

承继自Worker的类需求重写doWork办法,咱们能够在这个办法中履行详细的使命,这儿为了有演示成果打印出线程的称号。

Result用于回来使命的履行成果Result.success表明履行成功;Result.failure、Result.retry则分别表明履行失利和失利后尝试重试。

3.3 创立使命恳求WorkRequest

这儿咱们创立一个一次性的履行使命,代码如下所示:

val uploadLogWorkerRequset: WorkRequest = OneTimeWorkRequestBuilder<UploadLogWorker>()
            .build()

3.4 将使命提交体系

创立好使命之后,就能够将使命提交体系,履行恳求,代码如下所示:

WorkManager.getInstance(this).enqueue(uploadLogWorkerRequset)

运转App,运转成果如下图所示。

听说Jetpack WorkManager很难用?快来看这篇~

3.5 为使命传递参数

许多时分咱们在履行使命的时分是需求参数的,比方上传日志文件咱们要知道日志文件的途径或者其他参数,咱们怎么样将参数传递给Worker呢?

咱们能够经过WorkRequest的setInputData办法来设置参数,代码如下所示:

val uploadLogWorkerRequset: WorkRequest = OneTimeWorkRequestBuilder<UploadLogWorker>()
            .setInputData(workDataOf( "filePath" to "file://***" , "fileName" to "log.txt" ))
            .build()

这儿咱们传递了文件途径filePath和文件名fileName,在Worker经过getInputData办法承受,比方咱们在doWork中承受参数并打印。代码如下所示:

override suspend fun doWork(): Result {
        val filePath = inputData.getString( "filePath" )
        val fileName = inputData.getString( "fileName" )
        Log.d( "承受的参数" , " $fileName : $filePath " )
        return Result.retry()
    }

运转程序,打印如下图所示。

听说Jetpack WorkManager很难用?快来看这篇~

这样咱们就完成了一个最简单的WorkManager运用案例。接着咱们来进一步的探究。

4、履行加急作业你所需求知道的

从 WorkManager 2.7 开端,咱们能够调用setExpedited办法来告知体系,我这个使命是加急使命,请赶快履行。修正代码如下所示:

val uploadLogWorkerRequset: WorkRequest = OneTimeWorkRequestBuilder<UploadLogWorker>()
    .setExpedited(OutOfQuotaPolicy. RUN_AS_NON_EXPEDITED_WORK_REQUEST )
    .build()

setExpedited办法中的OutOfQuotaPolicy参数有两个枚举值,含义如下所示。

枚举值 含义
RUN_AS_NON_EXPEDITED_WORK_REQUEST 当体系无法为使命加急处理时,使命变成常规使命
DROP_WORK_REQUEST 当体系无法为使命加急处理时,删除改使命

所以咱们这儿声明为RUN_AS_NON_EXPEDITED_WORK_REQUEST即可。再次运转程序。

OK,完美运转???

不过我的手机是Android 12的,为了确保没问题,咱们有必要在Android 11 或低版本上履行一次。没崩溃,但是使命却没履行,咱们看到了过错日志如下图所示。

听说Jetpack WorkManager很难用?快来看这篇~

Emm.. 一堆乱七八糟的,要害信息在这句话

Expedited WorkRequests require a ListenableWorker to provide an implementation for `getForegroundInfoAsync()`

从官方咱们获取到了这些信息:在 Android 12 之前,作业器中的 getForegroundInfoAsync()getForegroundInfo() 办法可让 WorkManager 在您调用 setExpedited() 时显示告知。假如您想要恳求使命作为加急作业运转,则一切的 ListenableWorker 都有必要完成 getForegroundInfo 办法。

假如未能完成对应的 ****getForegroundInfo 办法,那么在旧版平台上调用 setExpedited 时,或许会导致运转时崩溃。

了解到了这些,那咱们就来完成getForegroundInfo()办法,修正UploadLogWorker代码如下所示:

classUploadLogWorker(context:Context,workerParams:WorkerParameters):
Worker(context,workerParams){
overridefundoWork():Result{
Log.d("打印线程",Thread.currentThread().name)
setForegroundAsync(getForegroundInfo())
returnResult.success()
}
@SuppressLint("RestrictedApi")
overridefungetForegroundInfoAsync():ListenableFuture<ForegroundInfo>{
valfuture=SettableFuture.create<ForegroundInfo>()
future.set(getForegroundInfo())
returnfuture
}
fungetForegroundInfo():ForegroundInfo{
valnotificationManager=
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE)asNotificationManager
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
valchannel=NotificationChannel(
"1",
"hh",
NotificationManager.IMPORTANCE_HIGH
)
notificationManager.createNotificationChannel(channel)
}
valnotification=NotificationCompat.Builder(applicationContext,"1")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle(applicationContext.getString(R.string.app_name))
.setContentText("我是一个上传日志的使命")
.build()
returnForegroundInfo(1337,notification)
}
}

再次在Android11 上运转程序,发现打印出了日志,并显示了一个使命告知,如下图所示。

听说Jetpack WorkManager很难用?快来看这篇~

这一点是在履行加急作业时所有必要要注意的。

5、协程作业CoroutineWorker

1、将承继类修正为CoroutineWorker

2、完成getForegroundInfo办法,内容与上getForegroundInfo共同

6、守时使命PeriodicWorkRequest

在3.2中咱们界说了一次性使命OneTimeWorkRequestBuilder,现在咱们将上传日志的这个使命修正为守时使命,代码如下所示:

val uploadLogWorkerRequset: WorkRequest = PeriodicWorkRequestBuilder<UploadLogWorker>(15,TimeUnit.MINUTES)
            .build()

这儿指定了,守时使命的周期是15分钟一次,能够界说的最短重复距离便是 15 分钟,这一点开发者在测验的时分需求注意,不能傻傻的等着…,这儿我就傻傻的等了15分钟,确保守时使命是能够履行的。

7、作业束缚、延迟履行和重试策略

7.1 作业束缚

很多情况下,咱们需求为使命增加作业束缚,比方上传日志的使命肯定是在有网络的条件下进行的,当时支撑的束缚条件如下所示。

NetworkType 束缚运转作业所需的网络类型。例如 Wi-Fi (UNMETERED)。
BatteryNotLow 假如设置为 true,那么当设备处于“电量缺乏形式”时,作业不会运转。
RequiresCharging 假如设置为 true,那么作业只能在设备充电时运转。
DeviceIdle 假如设置为 true,则要求用户的设备有必要处于空闲状态,才能运转作业。在运转批量操作时,此束缚会十分有用;若是不用此束缚,批量操作或许会下降用户设备上正在积极运转的其他运用的功能。
StorageNotLow 假如设置为 true,那么当用户设备上的存储空间缺乏时,作业不会运转。

比方咱们现在为一次性使命增加束缚为在链接wifi的情况下履行,首先用Constraints构建一个束缚实例能够将多个束缚条件放在一同。代码如下所示:

val constraints = Constraints.Builder()
            .setRequiresCharging(true)
            .build()

这儿设置为仅在充电的时分履行。接着为使命构建器增加束缚。

val uploadLogWorkerRequset: WorkRequest = OneTimeWorkRequestBuilder<UploadLogWorker>()
    .setConstraints(constraints)
    .build()

这样一来使命就会在仅充电的时分履行了。

7.2 延迟履行

延迟履行适用于一次性使命和守时使命,但运用在守时使命事仅对第一次履行有用,为啥呢?因为是守时使命呀~

咱们为一次性使命设置延迟时间为5秒钟,代码如下所示:

val uploadLogWorkerRequset: WorkRequest = OneTimeWorkRequestBuilder<UploadLogWorker>()
            .setConstraints(constraints)
            .setInitialDelay( 5 ,TimeUnit.SECONDS)
            .build()

运转程序,能够看到5秒钟后,程序才打印了日志,这儿就不演示了。

7.3 重试策略

在3.2中界说Work中咱们提到了Result.retry能够让使命重试,咱们也能够自界说使命的重试策略和退避政策,咱们经过详细的例子来解说。

val uploadLogWorkerRequset: WorkRequest = OneTimeWorkRequestBuilder<UploadLogWorker>()
            .setBackoffCriteria(
BackoffPolicy.EXPONENTIAL, OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
TimeUnit.MILLISECONDS
)
            .build()

最短退避延迟时间设置为允许的最小值,即 10 秒。因为政策为 LINEAR,每次尝试重试时,重试距离都会增加约 10 秒。例如,第一次运转以 Result.retry() 完毕并在 10 秒后重试;然后,假如作业在后续尝试后继续回来 Result.retry(),那么接下来会在 20 秒、30 秒、40 秒后重试,以此类推。

打印日志如下图所示。

听说Jetpack WorkManager很难用?快来看这篇~

咱们能够看到,第一次使命失利后延迟了10秒从头履行,第2次延迟了20秒,第三次延迟了40秒…

8、调查作业履行成果

在使命完成后,我或许需求进行更新UI或者事务逻辑操作。咱们能够经过注册监听器来调查 WorkInfo 的变化,以依据ID查询WorkInfo状态为例,代码如下所示:

WorkManager.getInstance(this).getWorkInfoByIdLiveData(uploadLogWorkerRequset.id).observe(this){
            if (it.state == WorkInfo.State.SUCCEEDED){
                Toast.makeText(this,"使命履行成功,更新UI",Toast.LENGTH_LONG).show()
            }else{
                //使命失利或重试
            }
        }

除了getWorkInfoByIdLiveData之外还有依据tag、name等查询的转化办法,这儿读者可自行检查API。

运转程序,成果如下图所示。

听说Jetpack WorkManager很难用?快来看这篇~

类似的咱们还能够经过cancelWorkById等办法来撤销使命的履行。这儿不做演示了。

9.总结

9.1 特性

  • 在早于 Android 12 的 API 版本中,加急作业都是由前台服务履行的,而从 Android 12 开端,它们将由加急作业 (expedited job) 完成。所以在第4小节中,Android12上并不会显示告知栏
  • WorkManager 仅仅一个处理守时使命的东西
  • WorkManager 最早兼容到 API 14(Android 4.0)

9.2 注意事项

  • 运用WorkManager注册的周期性使命不能确保一定会准时履行,这并不是bug,而是系 统为了减少电量消耗,或许会将触发时间接近的几个使命放在一同履行,这样能够大幅度地减 少CPU被唤醒的次数,然后有用延伸电池的运用时间。
  • WorkManager官方虽然称它能够确保即便在运用退出乃至手机重启的情况下,之前注册的使命依然将会得到履行。但是在国产手机中是不或许的,因为体系自己做了改动。但是在国产机上测验退出后,再进来也会履行之前的使命。这个时分或许就会有重复的使命履行。
  • 假如使命现已开端履行调用撤销使命的办法是无法终止使命的,但是调用撤销办法之后,无法再调查到使命成果。
  • 履行一个后台使命,在使命完毕前杀死APP,再次进来时之前未完成的使命会从头开端履行,且履行完毕后无法收到回调。
  • 使命增加到行列后,未开端履行前,假如是在onDestory中调用撤销使命的办法是不可行的,此种情况下下次进来时依然会有重复使命开端履行。(原生体系、国产机一样)

9.3 在事务中运用需求重视的问题

  • 使命增加到行列后,未开端履行前,假如是在onDestory中调用撤销使命的办法是不可行的,此种情况下下次进来时依然会有重复使命开端履行。

产生原因:cancelWork操作是一个异步操作,调用此操作后撤销操作还未履行完毕进程便完毕了

事务影响: 连续打开关闭屡次,会有多个重复的使命履行,且之前的使命无法收到使命进度回调

解决方案: 暂无