当前位置:网站首页>Jetpack WorkManager is enough to read this article~

Jetpack WorkManager is enough to read this article~

2022-08-06 07:53:26Huang Linqing

前言

最近有读者反馈,在我的新书《Android Jetpack 开发:原理解析与应用实战》中并没有提及到WorkManager,This is because at present this thing is not very useful in China.Recently, due to work needs, I just researched it,It is also shared with readers as a supplementary chapter.

什么是WorkManager

按照官方描述,WorkManager Is the recommended solution for persistent work.如果工作始终要通过应用重启和系统重新启动来调度,It's lasting work.Since most background processing operations are done with persistent work,因此 WorkManager is the primary recommendation for background processing operations API.

任务类型

WorkManagerTask types are divided into run immediately、Long-running and deferred execution,The relationship between usage and period is shown below:

立即一次性OneTimeWorkRequest 和 Worker.如需处理加急工作,请对 OneTimeWorkRequest 调用 setExpedited().
长期运行一次性或定期任意 WorkRequest 或 Worker.在工作器中调用 setForeground() 来处理通知.
可延期一次性或定期PeriodicWorkRequest 和 Worker.

Next, let's see how to use it.

入门使用

添加依赖库

本文代码使用Kotlin编写,So it is only introduced hereKotlinrelated libraries,在build.gradle中添加代码如下所示:

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

如果使用的是JavaHow to refer to language?听我的,放弃吧~

定义工作Worker

Here we take the upload log file task as an example,新建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方法,We can perform specific tasks in this method,This prints out the name of the thread for demonstration results.

ResultUsed to return the execution result of the taskResult.success表示执行成功;Result.failure、Result.retryIt means that the execution fails and the attempt to retry after the failure, respectively.

Create a task requestWorkRequest

Here we create a one-time execution task,代码如下所示:

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

Submit the task to the system

After creating the task,The task can be submitted to the system,执行请求,代码如下所示:

WorkManager.getInstance(this).enqueue(uploadLogWorkerRequset)

运行App,运行结果如下图所示.

为任务传递参数

Many times we need parameters when executing tasks,For example, to upload a log file, we need to know the path or other parameters of the log file,How do we pass parameters to Worker呢?

我们可以通过WorkRequest的setInputData方法来设置参数,代码如下所示:

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

Here we pass the file pathfilePath和文件名fileName,在Worker通过getInputData方法接受,比如我们在doWorkaccepts parameters and prints.代码如下所示:

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

运行程序,打印如下图所示.

这样我们就完成了一个最简单的WorkManager使用案例.Then let's explore further.

All you need to know to perform expedited work

从 WorkManager 2.7 开始,我们可以调用setExpeditedmethod to tell the system,My mission is an expedited mission,Please do it as soon as possible.修改代码如下所示:

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

setExpedited方法中的OutOfQuotaPolicyThe parameter has two enumeration values,含义如下所示.

枚举值

含义

RUN_AS_NON_EXPEDITED_WORK_REQUEST

When the system cannot expedite a task,Tasks become regular tasks

DROP_WORK_REQUEST

When the system cannot expedite a task,删除该任务

So we declare it hereRUN_AS_NON_EXPEDITED_WORK_REQUEST即可.再次运行程序.

OK,完美运行???

不过我的手机是Android 12的,To make sure it's ok,我们必须在Android 11 or a lower version.没崩溃,But the task was not carried out,We saw the error log as shown in the image below.

Emm.. 一堆乱七八糟的,The key message is in this sentence

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

We got this information from the official:在 Android 12 之前,工作器中的 getForegroundInfoAsync()getForegroundInfo() 方法可让 WorkManager 在您调用 setExpedited() 时显示通知.如果您想要请求任务作为加急作业运行,则所有的 ListenableWorker 都必须实现 getForegroundInfo 方法.

如果未能实现对应的getForegroundInfo 方法,那么在旧版平台上调用 setExpedited 时,可能会导致运行时崩溃.

了解到了这些,那我们就来实现getForegroundInfo()方法,修改UploadLogWorker代码如下所示:

class UploadLogWorker(context: Context, workerParams: WorkerParameters) :
    Worker(context, workerParams) {

    override fun doWork(): Result {
        Log.d("打印线程", Thread.currentThread().name)
        setForegroundAsync(getForegroundInfo())
        return Result.success()
    }

    @SuppressLint("RestrictedApi")
    override fun getForegroundInfoAsync(): ListenableFuture<ForegroundInfo> {
        val future = SettableFuture.create<ForegroundInfo>()
        future.set(getForegroundInfo())
        return future
    }


    fun getForegroundInfo(): ForegroundInfo {
        val notificationManager =
            applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                "1",
                "hh",
                NotificationManager.IMPORTANCE_HIGH
            )
            notificationManager.createNotificationChannel(channel)
        }

        val notification = NotificationCompat.Builder(applicationContext, "1")
            .setSmallIcon(R.drawable.ic_launcher_background)
            .setContentTitle(applicationContext.getString(R.string.app_name))
            .setContentText("I am a task to upload logs")
            .build()
        return ForegroundInfo(1337, notification)
    }


}

再次在Android11 上运行程序,It was found that the log was printed,and shows a task notification,如下图所示.

This is something that must be taken into account when performing expedited work.

协程工作CoroutineWorker

1、Modify the inherited class to CoroutineWorker

2、实现getForegroundInfo方法,content and abovegetForegroundInfo一致

定时任务PeriodicWorkRequest

在3.2In , we define a one-time taskOneTimeWorkRequestBuilder,Now we modify the task of uploading logs to a scheduled task,代码如下所示:

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

这里指定了,The period of the scheduled task is15分钟一次,The shortest repetition interval that can be defined is 15 分钟,Developers need to pay attention to this when testing,Can't wait stupidly...,I just waited stupidly here15分钟,Make sure that the scheduled task can be executed.

工作约束、Delayed execution and retry strategy

工作约束

很多情况下,We need to add work constraints to the task,For example, the task of uploading logs must be carried out under the conditions of a network,The currently supported constraints are listed below.

NetworkType约束运行工作所需的网络类型.例如 Wi-Fi (UNMETERED).
BatteryNotLow如果设置为 true,那么当设备处于“电量不足模式”时,工作不会运行.
RequiresCharging如果设置为 true,那么工作只能在设备充电时运行.
DeviceIdle如果设置为 true,则要求用户的设备必须处于空闲状态,才能运行工作.在运行批量操作时,此约束会非常有用;若是不用此约束,批量操作可能会降低用户设备上正在积极运行的其他应用的性能.
StorageNotLow如果设置为 true,那么当用户设备上的存储空间不足时,工作不会运行.

For example, we now add a constraint for one-time tasks to be connectedwifi的情况下执行,首先用ConstraintsBuilding a constraint instance can put multiple constraints together.代码如下所示:

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

This is set to execute only when charging.Then add constraints to the task builder.

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

This way the task will be executed while charging only.

延迟执行

Delayed execution applies to one-time and scheduled tasks,However, the application is valid for the first execution of the scheduled task,为啥呢?Because it is a scheduled task~

We set the delay time for one-time tasks as 5秒钟,代码如下所示:

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

运行程序,可以看到5秒钟后,The program just printed the log,这里就不演示了.

重试策略

在3.2中定义Work中我们提到了Result.retryThe task can be retried,We can also customize the retry policy and backoff policy of the task,We explain with concrete examples.

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 秒后重试,以此类推.

The print log is shown below.

我们可以看到,Delayed after the first mission failed10re-execute in seconds,第二次延迟了20秒,第三次延迟了40秒...

Observe the results of the work execution

在任务完成后,I may need to updateUIOr business logic operations.我们可以通过注册监听器来观察 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{
                //Task failed or retried
            }
        }

除了getWorkInfoByIdLiveDataThere are other reasonstag、nameand other query conversion methods,Readers can check it out hereAPI.

运行程序,结果如下图所示.

Similarly, we can also passcancelWorkByIdand other methods to cancel the execution of the task.这里不做演示了.In addition, there are some other features that interested readers can practice by themselves.

总结

Features and Precautions

  • 在早于 Android 12 的 API 版本中,加急工作都是由前台服务执行的,而从 Android 12 开始,它们将由加急作业 (expedited job) 实现.所以在第4小节中,默认Android12The notification bar will not be displayed

  • WorkManager 只是一个处理定时任务的工具

  • WorkManager 最早兼容到 API 14(Android 4.0)

  • 使用WorkManager注册的周期性任务不能保证一定会准时执行,这并不是bug,rather system to reduce power consumption,可能会将触发时间临近的几个任务放在一起执行,This can be greatly reduced 少CPU被唤醒的次数,从而有效延长电池的使用时间

  • WorkManagerOfficially though it says it's guaranteed even in the event of an app exit or even a phone restart,之前注册的任务仍然将会得到执行.But it is impossible in domestic mobile phones,Because the system itself has made changes.But tested on domestic aircraft(OPPO)退出后,Re-entering will also perform the previous task.At this time, there may be repeated tasks to execute.

问题

  • 任务添加到队列后,Before execution begins,如果是在onDestoryIt is not feasible to call the cancel task method in ,In this case, there will still be repeated tasks to start executing the next time you come in.原因不明.(原生系统、Same as domestic machine)

Encountered such a problem here,at the time of publication of this article,I still don't know how to fix it.Hope someone who knows can give me some advice~

如果你想学习Jetpack更多精彩内容,欢迎购买我的新书《Android Jetpack开发:原理解析与应用实战》

原网站

版权声明
本文为[Huang Linqing]所创,转载请带上原文链接,感谢
https://chowdera.com/2022/218/202208060744125917.html

随机推荐