Android Jetpack系列 之 WorkManager

Posted microhex

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Jetpack系列 之 WorkManager相关的知识,希望对你有一定的参考价值。

大概

  这应该是一篇比较悲伤的文章,因为WorkManager并没有达到它所描述的功能,所以作为天朝的程序员,此处静默哀悼一秒钟,具体文章下面详述。 虽然如此,我们还是要了解一下这个WorkManager是干什么的,具体怎么干,又有什么优点或者缺点。

关于WorkManager的官方文档可见:https://developer.android.com/topic/libraries/architecture/workmanager 请大伙自备梯子,我的学习和实验都来在此处。废话不多说,我们开始。

首先,WorkManager是android Architecture Components架构组件的一部分,这个架构组件的使命是为了全世界的开发者 开发更容易、更简单、更高效和更少的出错率。它包含了很多部分,比如UI部分的LiveData,数据库方面的Room,还有分页组件paging库等等,这个优秀的组件库值得大家去努力学习:)

今天要提到的WorkManager是google专门针对后台任务的!想象一下这种情景:你开发了一款APP,你的APP在晚上需要与服务器通信,提交今天的日志信息,但是要考虑到实际的各种情况,比如如果晚上用户在外面,网络情况不好;用户手机上没有太多电量;手机存储量够不够等扥等复杂的情况,针对这些情况,我需要自动判断今晚是否上传我的数据,如果有条件不满足,那么我就不能上传,将上传任务延迟,直到条件满足我再继续。

优势

然后,上面的例子只是一个个例,不过它也引入了WorkManager的优势,那么WorkManager有哪些优势呢?

  • API支持到14 (这个基本上现在所有手机都支持了)
  • 可以自定义规范,通过规范来决定后台任务是否置顶
  • 可以指定一次性任务 和 循环任务
  • 可以监控任务的执行状态
  • 可以任务链式化(这个稍后再说一下)
  • 可以确保任务一定会被执行,即使App退出或者设备重启 (我测试过了,不可以,但我还是写出来,因为这是文档上的原话)
  • 节能
  • 任务可以延迟执行

依次解释一下上面的点:

  1. API支持,分两个方面,当你的设备Android版本在23之后,就会使用JobScheduler来作为WorkManager的内部实现;小于23,大于等于14时,采用是BoradcastReceiver + AlarmManager实现。这里插一句嘴,我们当年使用JobScheduler来进行进程保活,但是效果不怎么好,主要是国产的Rom修改得太厉害,各种后台杀杀杀,基本上把JobScheduler功能搞没用了,这些年也没见多少人用它。

  2. 自定义规范,目前支持的规范是有这些:

    • setRequiresDeviceIdle 设备是否空闲状态
    • setRequiredNetworkType 是否在特定的网络状态下
    • setRequiresBatteryNotLow 电池电量是否够
    • setRequiresCharging 设备是否在充电的状态下
    • setRequiresStorageNotLow 设备存储是否空闲
  3. 监控任务的状态,WorkManager把Work(抽象类,就是我们理解的任务)的状态分为以下几种:

    • BLOCKED 阻塞状态, 只会在出现链式任务中,当前的work前面还有work,那么当前work状态就是BLOCKED 状态
    • ENQUEUED 任务即将执行,只有设定的规范满足条件,即将执行的状态
    • RUNNING 任务正在执行的状态
    • SUCCEEDED 任务指定成功
    • FAILED 错误状态
    • CANCELLED 错误状态

    基本上来张图:

  4. 监听任务的执行状态,通过给定的接口,可以得知当前任务执行在哪个状态。

  5. 任务链式化,也就是顺序化,workManager可以决定任务的执行顺序,任务可先可后,这个应该是比较牛的功能吧。

  6. 虽然系统声明了,使用了WorkManager就一定能使任务执行完成,即使App退出,或者设备重启都能执行。原话为:Ensures task execution, even if the app or device restarts,但是测试了效果不如意,通过查阅资料,找到了一些蛛丝马迹:https://stackoverflow.com/questions/50682061/android-is-workmanager-running-when-app-is-closed 上面的大哥解释道天朝的ROM改的比较严重,所以就造成了这么好的功能不能使用,目前在Pixel 2 XL能够使用,也是蛮尴尬的一件事。

  7. 关于节能和任务延迟执行,这个也算是简单聊一下吧,毕竟这个不好说:(

代码展示

这里还是聊一下,如何实现WorkManager:
先添加androidX 依赖,现在一般都是使用了kotlin实现了。

 dependencies 
    def work_version = "2.0.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 - Test helpers
    androidTestImplementation "androidx.work:work-testing:$work_version"
  

定义我们的任务,继承androidx.work.Worker,实现抽象方法doWork(),我们来实现一个写入文件的任务:

class FileWorker(appContext: Context, workerParameters: WorkerParameters) : Worker(appContext, workerParameters) 

    override fun doWork(): Result 
        Log.d("doWork", "doWork start")

        try 
            val file = File(Environment.getExternalStorageDirectory(), "1.txt")
            BufferedWriter(FileWriter(file)).append("hello world \\n").close()
         catch (e: Exception) 
            e.printStackTrace()
        

        Log.d("doWork","doWork end")

        return Result.success()
    

定义了任务之后,我们可以直接要执行任务。执行任务分两种:一种是一次性任务,另外一个是循环任务:

  1. 一次性任务,使用OneTimeWorkRequestBuilder进行:
   val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>().build()
   WorkManager.getInstance().enqueue(uploadWorkRequest)
  1. 循环任务,使用PeriodicWorkRequestBuilder进行:
  val saveRequest = PeriodicWorkRequest.Builder(FileWorker::class.java, 2, TimeUnit.SECONDS).build()
  WorkManager.getInstance().enqueue(saveRequest)
  1. 设置我们的规范,可以使用任务满足条件下运行:
 val constraints = Constraints.Builder().
           setRequiresDeviceIdle(true).
            //特定的网络状态
           setRequiredNetworkType(NetworkType.CONNECTED).
            //电池在可接受的水平 [电量?]
           setRequiresBatteryNotLow(true).
            //是否在充电时执行
           setRequiresCharging(true).
            //存储是否满足 [容量知否足够]
            setRequiresStorageNotLow(true).
            build()

 val uploadWorkRequest = OneTimeWorkRequestBuilder<FileWorker>().
            setConstraints(constraints).build()

WorkManager.getInstance().enqueue(uploadWorkRequest)
  1. 任务中传值,使用Data:
val inputData = Data.Builder().putString("name","Tom").putInt("age",20).build()
 val uploadWorkRequest = OneTimeWorkRequestBuilder<FileWorker>().
            setConstraints(constraints).
            //延时执行
            setInitialDelay(20, TimeUnit.SECONDS).
            setInputData(inputData).
            addTag("uploadImage").
            build()

WorkManager.getInstance().enqueue(uploadWorkRequest)

然后可以在FileWorker#doWork()中获取:

val name = inputData.getString("name")
val age = inputData.getInt("age", 0)

Log.d("inputData", "name:$name , age: $age")
  1. 取消任务:
WorkManager.getInstance().cancelAllWork()
//or
WorkManager.getInstance().cancelAllWorkByTag("tagName")
//or
WorkManager.getInstance().cancelUniqueWork("uniqueWorkName")
//or 
WorkManager.getInstance().cancelWorkById(UUID.randomUUID())

结论

可能是以前用过JobScheduler,所以对这个WorkManager感觉在天朝用处不大,如果在天朝真的能用,也将是一种可怕的灾难,天知道谁会做出一起很奇怪的事情呢。所以WorkManager也算是做一个简单的了解吧,具体项目中应该不考的:)

参考的文章为:
1.https://developer.android.com/jetpack/androidx/releases/work#declaring_dependencies
2.https://medium.com/androiddevelopers/introducing-workmanager-2083bcfc4712
3.https://medium.com/androiddevelopers/workmanager-basics-beba51e94048
4.https://www.androidauthority.com/schedule-background-tasks-jetpacks-workmanager-874189/

以上是关于Android Jetpack系列 之 WorkManager的主要内容,如果未能解决你的问题,请参考以下文章

Android Jetpack系列之ViewModel

Android Jetpack系列之DataStore

Android Jetpack系列之MVI架构

Android Jetpack系列 之 WorkManager

Android Jetpack之AppCompat - Actionbar篇

《Android Jetpack从入门到精通+高级Jetpack强化实战》,最新Jetpack系列开发笔记开源