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退出或者设备重启(我测试过了,不可以,但我还是写出来,因为这是文档上的原话) - 节能
- 任务可以延迟执行
依次解释一下上面的点:
-
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://*.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/
下一篇: C++编程之模板与泛型