这应该是一篇比较悲伤的文章,因为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退出或者设备重启 (我测试过了,不可以,但我还是写出来,因为这是文档上的原话)节能任务可以延迟执行依次解释一下上面的点:
API支持,分两个方面,当你的设备Android版本在23之后,就会使用JobScheduler来作为WorkManager的内部实现;小于23,大于等于14时,采用是BoradcastReceiver + AlarmManager实现。这里插一句嘴,我们当年使用JobScheduler来进行进程保活,但是效果不怎么好,主要是国产的Rom修改得太厉害,各种后台杀杀杀,基本上把JobScheduler功能搞没用了,这些年也没见多少人用它。
自定义规范,目前支持的规范是有这些:
setRequiresDeviceIdle 设备是否空闲状态 setRequiredNetworkType 是否在特定的网络状态下 setRequiresBatteryNotLow 电池电量是否够 setRequiresCharging 设备是否在充电的状态下 setRequiresStorageNotLow 设备存储是否空闲监控任务的状态,WorkManager把Work(抽象类,就是我们理解的任务)的状态分为以下几种:
BLOCKED 阻塞状态, 只会在出现链式任务中,当前的work前面还有work,那么当前work状态就是BLOCKED 状态 ENQUEUED 任务即将执行,只有设定的规范满足条件,即将执行的状态 RUNNING 任务正在执行的状态 SUCCEEDED 任务指定成功 FAILED 错误状态 CANCELLED 错误状态基本上来张图:
监听任务的执行状态,通过给定的接口,可以得知当前任务执行在哪个状态。
任务链式化,也就是顺序化,workManager可以决定任务的执行顺序,任务可先可后,这个应该是比较牛的功能吧。
虽然系统声明了,使用了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能够使用,也是蛮尴尬的一件事。
关于节能和任务延迟执行,这个也算是简单聊一下吧,毕竟这个不好说:(
这里还是聊一下,如何实现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() } }定义了任务之后,我们可以直接要执行任务。执行任务分两种:一种是一次性任务,另外一个是循环任务:
一次性任务,使用OneTimeWorkRequestBuilder进行: val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>().build() WorkManager.getInstance().enqueue(uploadWorkRequest) 循环任务,使用PeriodicWorkRequestBuilder进行: val saveRequest = PeriodicWorkRequest.Builder(FileWorker::class.java, 2, TimeUnit.SECONDS).build() WorkManager.getInstance().enqueue(saveRequest) 设置我们的规范,可以使用任务满足条件下运行: 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) 任务中传值,使用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") 取消任务: 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/