欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android Jetpack系列 之 WorkManager

程序员文章站 2022-06-10 14:38:01
...

大概

  这应该是一篇比较悲伤的文章,因为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 错误状态

    基本上来张图:
    Android Jetpack系列 之 WorkManager

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

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

  6. 虽然系统声明了,使用了WorkManager就一定能使任务执行完成,即使App退出,或者设备重启都能执行。原话为:Ensures task execution, even if the app or device restarts,但是测试了效果不如意,通过查阅资料,找到了一些蛛丝马迹:https://*.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/