Android WorkManager 初探

前言

总结和学习 Android WorkManager 的相关用法。

WorkManager 有什么特殊之处

在日常开发中,有些任务需要在异步线程执行,并且这些工作耗时较长,通过普通的子线程或后台服务无法胜任时,就需要 WorkManager 了。

WorkManager 适用于需要可靠运行的工作,即使用户导航离开屏幕、退出应用或重启设备也不影响工作的执行。例如:

  • 向后端服务发送日志或分析数据。
  • 定期将应用数据与服务器同步。 WorkManager 不适用于那些可在应用进程结束时安全终止的进程内后台工作。

它也并非对所有需要立即执行的工作都适用的通用解决方案

可靠运行 这个特点太有诱惑力了,尤其是对于周期性的工作。相比通过诸如1像素的 Activity、进程保活、互相保活/拉起之类的方式来执行特定的工作,使用官方提供的 WorkManager 可以避免做一些非常 hack 的工作,正所谓事半功倍。

WorkManager 的功能

相比传统的 AsyncTask/Coroutine/RxJava/Executors ,WorkManager 有更鲜明的特点。

  • 结合系统特性的约束: 可以结合设备当前的网络、电量等状况,约束任务执行的条件。比如等到用户连接 WiFi 后进行 App 的更新,电量充足的时候进行系统更新,条件不满足就等待,都可以通过 WorkManager 内置的 API 非常方便的实现。

  • 强大的调度: 通过 WorkManager 执行的工作,会由系统通过 SQLite 数据库进行管理,而上层开发者可以基于工作的标记进行状态监控、取消等操作。

  • 工作链和灵活的重试策略: 这一点熟练使用 RxJava 的同学应该非常喜欢。将多个工作通过合理的编排进行串联并联,根据工作执行的结果进行重试,WorkManager 都支持。

同时 WorkManager 可以与 Coroutine 及 RxJava 无缝衔接。

这里需要明白的是,WorkManager 不是用来替代 Coroutine 或其他可以执行异步操作框架的,而是基于这些框架的特点提供了更强大的功能,是互补的关系,一些通过 Coroutine 就可以完成的工作,就不要强行往 WorkManager 上套了,不要为了用而用,很多时候合适就足够了。

WorkManager 入门

关于使用 WorkManager 如何添加依赖及 demo 级别的的使用场景,可以直接参考 WorkManager 使用入门,非常简单。

在这里插入图片描述

从官方提供的工作类型,可以看到主要有两类工作,即一次性和周期性的,顾名思义一个是执行一次就完事,一类是周期性重复执行的工作。

下面就通过一个简单的示例,从一次性工作(在 WorkManager 中,可以多执行多个任务,规范期间一个任务被定义为一个 Work,这里索性就称为工作)开始,了解一下具体如何使用 WorkManager.

  1. 定义目标

一个删除文件的任务,连续多次调用,覆盖执行。删除任务执行时,传入的路劲参数异常或文件不存在时返回失败,正常删除则返回成功。

  1. 创建 Work
    class CleanWork(appContext: Context, workerParameters: WorkerParameters) :
        Worker(appContext, workerParameters) {
        override fun doWork(): Result {
            val path = inputData.getString("path")
            return if (TextUtils.isEmpty(path)) {
                Result.failure(Data.Builder().putString("result", "filepath is null").build())
            } else {
                if (cleanOutDateFile(path!!)) {
                    Result.success()
                } else {
                    Result.failure(Data.Builder().putString("result", "file not exist").build())
                }
            }
        }
    }

这里关注以下几点 :

  • 继承抽象类 Worker 定义自己的 Worker ,实现 doWork 方法。
  • 返回参数 Result 有三个类型
    • Result.success():工作成功完成。
    • Result.failure():工作失败。
    • Result.retry():工作失败,应根据其重试政策在其他时间尝试。
  • Work 入参和结果的传递
    • 通过 inputData 的 getXXX 方法,可以获取调用任务时传入的参数
    • 通过 Result 返回结果是参入的 Data 参数,可以返回具体的结果,不传的话,就相当于 void 类型的函数。

简单看一下 cleanOutDateFile 的定义

    private fun cleanOutDateFile(path: String): Boolean {
        val file = File(path)
        return if (file.exists()) {
            file.delete()
            true
        } else {
            false
        }
    }

  1. 创建 WorkRequest 并加入队列
    fun clean(context: Context, path: String) {
        val request =
            OneTimeWorkRequestBuilder<CleanWork>()
                .addTag("cleanWork")
                .setInputData(workDataOf("path" to path)).build()

        WorkManager.getInstance(context)
            .enqueueUniqueWork("clean", ExistingWorkPolicy.REPLACE, request)
    }

这里关注以下几点 :

  • OneTimeWorkRequestBuilder 的泛型参数就是上面自定义的 Work
  • 通过 addTag 可以给当前要执行的 Work 添加唯一标识,这样可以基于这个 tag 获取 Work 执行的结果,或者取消 Work
  • setInputData 传入 Data 对象,即可给 Work 传参,这里的参数类型其实就是 Map,因此可以传递各种类型的值,确保 key 和 Work 中对应即可。
  • WorkRequest 加入队列时,可以简单调用 enqueue 方法,这样如果有重复的 WorkRequest 发生冲突,默认会采取保留现有 Work ,抛弃新 WorkRequest 。或者像这里一样调用 enqueueUniqueWork,通过定义 tag ,自行制定面对冲突情况的策略。
  1. 调用并观察 Work 执行情况
class WorkManagerViewModel(app: Application) : AndroidViewModel(app) {
    private val workManager = WorkManager.getInstance(app)

    val cleanWorkInfo = workManager.getWorkInfosByTagLiveData("cleanWork")
}


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        workManagerViewModel.cleanWorkInfo.observe(this@FilterActivity) {
            Log.i("cleanWork", "info -> $it")
            if (it != null && it.size > 0) {
                val work = it[it.size - 1]
                Log.i("cleanWork", "$work")
            val out = work.outputData.getString("result")
                Log.i("cleanWork", "$out")               
            }
        }


        val path = cacheDir.absolutePath + File.separator + "current.txt"
        WorkUtil.clean(it.context, path)

    }

这里连续调用两次 clean 方法之后看日志


20:23:16.391 17128-17128 cleanWork                           I  info -> [WorkInfo{mId='d64379e8-17b5-484d-9c2f-2273c1fd629f', mState=SUCCEEDED, mOutputData=Data
                                                                                                    {}, mTags=[cleanWork, com.engineer.android.mini.jetpack.work.WorkUtil$CleanWork], mProgress=Data {}}]
20:23:16.391 17128-17128 cleanWork                           I  WorkInfo{mId='d64379e8-17b5-484d-9c2f-2273c1fd629f', mState=SUCCEEDED, mOutputData=Data
                                                                                                    {}, mTags=[cleanWork, com.engineer.android.mini.jetpack.work.WorkUtil$CleanWork], mProgress=Data {}}
20:23:16.391 17128-17128 cleanWork                           I  null
20:25:07.611 17128-17128 cleanWork                           I  info -> [WorkInfo{mId='b238f68c-b1c8-4994-975b-7f5d2b67d10c', mState=FAILED, mOutputData=Data {result : file not
                                                                                                    exist, }, mTags=[cleanWork, com.engineer.android.mini.jetpack.work.WorkUtil$CleanWork], mProgress=Data {}}]
20:25:07.611 17128-17128 cleanWork                           I  WorkInfo{mId='b238f68c-b1c8-4994-975b-7f5d2b67d10c', mState=FAILED, mOutputData=Data {result : file not
                                                                                                    exist, }, mTags=[cleanWork, com.engineer.android.mini.jetpack.work.WorkUtil$CleanWork], mProgress=Data {}}
20:25:07.611 17128-17128 cleanWork                           I  file not exist

可以看到,第一次执行成功后,再次进行 clean 操作时就返回了 file not exist 的错误信息,而整个队列里也只有一条 WorkInfo,说明设置的 ExistingWorkPolicy.REPLACE 的策略也生效了。

这里通过这个简单的示例可以看到,WorkManager 非常强大,而且其 API 的定义非常友好,参数和方法命名很恰当,通过名称就可以知道要方法或参数的含义。

小结

WorkManager 最大的特点是其执行的是持久化的任务。这点我们通过 Android Studio Inspection 功能就可以看到,他通过 SQLite 数据库从不同维度存储了各种插入到队列中 Worker 的信息。

inspection.png

因此,在定义 Worker ,创建 WorkRequest 和选择 Worker 添加到队列中的方式是都要谨慎,因为这些信息都会被存储。同时,我们也可以通过 Inspection 工具查看当前进程中存在的 Worker 信息,进行问题的排查和了解。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题

图片

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值