服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

Jetpack WorkManager 看这一篇就够了~

日期: 来源:郭霖收集编辑:黄林晴


/   今日科技快讯   /

3月16日下午,百度于北京总部召开新闻发布会,主题围绕新一代大语言模型、生成式AI产品文心一言。百度创始人、董事长兼首席执行官李彦宏出席及百度首席技术官王海峰出席,并展示了文心一言在文学创作、商业文案创作、数理推算、中文理解、多模态生成五个使用场景中的综合能力。


百度同时公布了文心一言的邀请测试方案。3月16日起,首批用户即可通过邀请测试码,在文心一言官网体验产品,后续将陆续开放给更多用户。此外,百度智能云即将面向企业客户开放文心一言API接口调用服务。3月16日起正式开放预约,搜索“百度智能云”进入官网,可申请加入文心一言云服务测试。


/   作者简介   /

明天就是周六啦,提前祝大家周末愉快!

本篇文章转自黄林晴的博客,文章主要分享了WorkManager的使用,相信会对大家有所帮助!

原文地址:
https://juejin.cn/post/7207707775774097466

/   什么是WorkManager   /

按照官方描述,WorkManager 是适合用于持久性工作的推荐解决方案。如果工作始终要通过应用重启和系统重新启动来调度,便是持久性的工作。由于大多数后台处理操作都是通过持久性工作完成的,因此 WorkManager 是适用于后台处理操作的主要推荐 API。

/   任务类型   /

WorkManager任务类型分为立即运行、长期运行和延期执行,使用方式与周期关系如下所示:


接下来来看具体的使用方法。

/   入门   /

添加依赖库

本文代码使用Kotlin编写,所以这里仅引入Kotlin相关的库即可,在build.gradle中添加代码如下所示:

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

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

定义工作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则分别表示执行失败和失败后尝试重试。

创建任务请求WorkRequest

这里我们创建一个一次性的执行任务,代码如下所示:

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

将任务提交系统

创建好任务之后,就可以将任务提交系统,执行请求,代码如下所示:

WorkManager.getInstance(this).enqueue(uploadLogWorkerRequset)

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


为任务传递参数

许多时候我们在执行任务的时候是需要参数的,比如上传日志文件我们要知道日志文件的路径或者其他参数,我们怎么样将参数传递给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()
    }

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


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

/   执行加急工作   /

从 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即可。再次运行程序。

OK,完美运行???

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


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

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

从官方我们获取到了这些信息:在 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("我是一个上传日志的任务")
            .build()
        return ForegroundInfo(1337, notification)
    }
}

再次在Android11 上运行程序,发现打印出了日志,并显示了一个任务通知,如下图所示。


这一点是在执行加急工作时所必须要注意的。

/   协程工作   /

1、将继承类修改为CoroutineWorker
2、实现getForegroundInfo方法,内容与上getForegroundInfo一致

/   定时任务   /

之前我们定义了一次性任务OneTimeWorkRequestBuilder,现在我们将上传日志的这个任务修改为定时任务,代码如下所示:

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

这里指定了,定时任务的周期是15分钟一次,可以定义的最短重复间隔就是 15 分钟,这一点开发者在测试的时候需要注意,不能傻傻的等着...,这里我就傻傻的等了15分钟,确保定时任务是可以执行的。

/   其它   /

工作约束

很多情况下,我们需要为任务添加工作约束,比如上传日志的任务肯定是在有网络的条件下进行的,当前支持的约束条件如下所示。


比如我们现在为一次性任务添加约束为在链接wifi的情况下执行,首先用Constraints构建一个约束实例可以将多个约束条件放在一起。代码如下所示:

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

这里设置为仅在充电的时候执行。接着为任务构建器添加约束。

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

这样一来任务就会在仅充电的时候执行了。

延迟执行

延迟执行适用于一次性任务和定时任务,但应用在定时任务事仅对第一次执行有效,为啥呢?因为是定时任务呀~

我们为一次性任务设置延迟时间为5秒钟,代码如下所示:

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

运行程序,可以看到5秒钟后,程序才打印了日志,这里就不演示了。

重试策略

在之前定义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 秒后重试,以此类推。

打印日志如下图所示。


我们可以看到,第一次任务失败后延迟了10秒重新执行,第二次延迟了20秒,第三次延迟了40秒...

/   观察执行结果   /

在任务完成后,我可能需要进行更新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。

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


类似的我们还可以通过cancelWorkById等方法来取消任务的执行。这里不做演示了。

/   总结   /

特性

  • 在早于 Android 12 的 API 版本中,加急工作都是由前台服务执行的,而从 Android 12 开始,它们将由加急作业 (expedited job) 实现。所以在第4小节中,Android12上并不会显示通知栏
  • WorkManager 只是一个处理定时任务的工具
  • WorkManager 最早兼容到 API 14(Android 4.0)

注意事项

使用WorkManager注册的周期性任务不能保证一定会准时执行,这并不是bug,而是系 统为了减少电量消耗,可能会将触发时间临近的几个任务放在一起执行,这样可以大幅度地减 少CPU被唤醒的次数,从而有效延长电池的使用时间。

WorkManager官方虽然称它可以保证即使在应用退出甚至手机重启的情况下,之前注册的任务仍然将会得到执行。但是在国产手机中是不可能的,因为系统自己做了改动。但是在国产机上测试退出后,再进来也会执行之前的任务。这个时候可能就会有重复的任务执行。

如果任务已经开始执行调用取消任务的方法是无法终止任务的,但是调用取消方法之后,无法再观察到任务结果。

执行一个后台任务,在任务结束前杀死APP,再次进来时之前未完成的任务会从头开始执行,且执行结束后无法收到回调。

任务添加到队列后,未开始执行前,如果是在onDestory中调用取消任务的方法是不可行的,此种情况下下次进来时仍然会有重复任务开始执行。(原生系统、国产机一样)

在业务中使用需要关注的问题

任务添加到队列后,未开始执行前,如果是在onDestory中调用取消任务的方法是不可行的,此种情况下下次进来时仍然会有重复任务开始执行。

产生原因:cancelWork操作是一个异步操作,调用此操作后取消操作还未执行结束进程便结束了

业务影响:连续打开关闭多次,会有多个重复的任务执行,且之前的任务无法收到任务进度回调

解决方案: 暂无

推荐阅读:
我的新书,《第一行代码 第3版》已出版!
Android 13 Developer Preview一览
Android 14 Developer Preview一览

欢迎关注我的公众号
学习技术或投稿


长按上图,识别图中二维码即可关注

相关阅读

  • 警告!不要滥用!!

  • 资源获取方式在文末 混合盘 Windows这是一款非常强大的资源搜索工具,该软件不需要注册登录,也没有任何的广告干扰,软件内置了全网多个高质量资源站,各类资源应有尽有,图片、影视
  • 邮政业发展内容纳入安徽安庆市政府年度重点任务

  • 近日,安徽省安庆市政府印发《2023年市政府工作报告重点工作任务的通知》(以下简称《通知》),邮政业发展内容被纳入市政府年度重点任务责任清单,市邮政管理局成为主要责任单位。
  • Google 工程主管:AIGC 将在三年内终结编程!

  • 随着 ChatGPT 聊天机器人、GPT-4 等大模型的到来,可以用自然语言生成代码、解决 Bug,为程序员编码大大降低了门槛,甚至一定程度上,还可以一键生成自己想要的代码与网页。不过,这
  • 报名 | “Go!到春天里”八院联合活动

  • 终于熬过了疫情和寒冬,你是否想要找个机会尽情拥抱春天?想要出去走走,你是否会因为没有同伴而感到意兴阑珊?由生科、艺院、物院、信科、化院、社系、哲学、心院(排名不分先后)共同
  • 葡萄牙联合实验室概述及其启示

  • 科情智库摘 要:强化国家战略科技力量,是应对国际科技和经济竞争格局深刻调整、把握新一轮科技革命和产业变革机遇的必然选择。葡萄牙联合实验室类似我国重点实验室,是面向国家
  • 火到爆的 GPT-4 来了!

  • 3月14日,人工智能公司OpenAI发布了ChatGPT(GPT-3.5阶段)的升级版GPT-4,让原本已经略显沉寂的 ChatGPT 话题再次迎来了“爆炸式热议”,毫无悬念地冲上了社交平台热搜。“AI 终将取

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • Jetpack WorkManager 看这一篇就够了~

  • / 今日科技快讯 /3月16日下午,百度于北京总部召开新闻发布会,主题围绕新一代大语言模型、生成式AI产品文心一言。百度创始人、董事长兼首席执行官李彦宏出席及百度首席技
  • (限时删)设计圈疯传!请低调使用!

  • “好想挣钱啊...…”单位难开工,生意不开张,咱们才惊醒:“领死工资的生活,真的好脆弱。”平时总是说做副业做兼职可以赚钱,但也就说说而已。副业在哪?兼职在哪?钱在哪?都没影!现实是
  • 共享两篇GPT-4的中英文版官方报告,欢迎自取

  • 昨晚OpenAI开的发布会,以下是中英文字幕的直播视频:同时OpenAI发布了两份关于GPT-4的官方报告:需要这两份GPT-4的中英文版报告的朋友可以在公众号回复“OpenAI”获取。
  • 海报很潮但不卖货,有球用

  • 写前叨逼叨:学一种风格容易,但想用好并不容易。现在做设计,绝大部分的行业都在倾向年轻化、时尚化,如果是专门针对年轻人的品牌那就更加如此,各大品牌都会使劲往潮、前卫的方向做
  • ​人生的决定权掌握在自己手中

  • hi,今天是我日更46天,共输出6w+字,这是我日更的41天内容我们身边有很多这样的人,常常抱怨后悔做出的一些选择,不该听别人这样做,不该跟别人那样做,过的十分纠结与矛盾。其实深究其