Android Jetpack之WorkManager源码分析
Android WorkManager简介
WorkManager 负责用来管理后台任务,它适用于需要保证系统即使应用程序退出也会运行的任务,
WorkManager根据设备API级别和应用程序状态等因素选择适当的方式来运行任务。如果WorkManager在应用程序运行时执行的任务,WorkManager可以在应用程序进程的新线程中运行您的任务。如果您的应用程序未运行,WorkManager会选择一种合适的方式来安排后台任务 。具体取决于设备API级别和包含的依赖项,WorkManager可能会使用 JobScheduler,Firebase JobDispatcher或AlarmManager调度任务。
为什么在有了service以后,google还有出WorkManager框架呢?
1.service的滥用导致手机后台任务不断执行,耗电量大。
2.从开发者来说,Android8.0以后,Android对于后台service管理的更加严格,应用在后台启动的服务必须是前台服务,否则会导致应用crash。当然你也可以选择降低targetSdkVersion。
3.针对targetSdkVersion Google也针对的出了一些限制。ps:2019 年起,每次发布新版本后,所有应用都必须在一年内将 Target API 更新到最新版本。
官方指导地址:官方指地址
WorkManager的使用
官方DEMO 官方DEMO
2.1 Gradle依赖配置
def work = "2.1.0"
implementation"androidx.work:work-runtime:$work"
implementation"androidx.work:work-testing:$work"
// implementation"androidx.work:work-firebase:$work"
implementation"androidx.work:work-runtime-ktx:$work"
2.2 定义Worker类
自定义Worker类,继承自Worker,然后复写doWork() 方法,返回当前任务的结果 Result。doWork方法是执行在子线程的。
class JetpackWork(context: Context,workerParameters: WorkerParameters) : Worker(context,workerParameters){
override fun doWork(): Result {
Log.e("workermanager","work start:")
Thread.sleep(2_000)
Log.e("workermanager","do work thread msg :"+Thread.currentThread().name)
return Result.success()
}
}
2.3 执行任务
(1)使用 OneTimeWorkRequest.Builder 创建对象Worker,将任务加入WorkManager队列。并且OneTimeWorkRequest.Builder创建的是一个单次执行的任务。
(2)将任务排入WorkManager队列,等待执行。
Worker不一定是立即执行的。WorkManager会选择适当的时间运行Worker,平衡诸如系统负载,设备是否插入等考虑因素。但是如果我们没有指定任何约束条件,WorkManager会立即运行我们的任务。
var request = OneTimeWorkRequest.Builder(JetpackWork::class.java)
.build()
WorkManager.getInstance(this).enqueue(request)
2.4 重复执行任务
(1)使用PeriodicWorkRequest.Builder类创建循环任务,创建一个PeriodicWorkRequest对象
(2)然后将任务添加到WorkManager的任务队列,等待执行。
(3)最小时间间隔是15分钟。
public static final long MIN_PERIODIC_INTERVAL_MILLIS = 15 * 60 * 1000L; // 15 minutes.
var pRequest = PeriodicWorkRequest.Builder(JetpackWork::class.java,1,TimeUnit.SECONDS).build()
WorkManager.getInstance(this).enqueue(pRequest)
2.4 任务的状态
通过获取LiveData查看任务的状态WorkInfo.State,只有当Activityh处于活跃状态下才可以监听成功。
WorkManager.getInstance(this).getWorkInfoByIdLiveData(request.id).observe(this, Observer {
Log.e("workermanager","state :"+it?.state?.name)
})
2.5 任务约束Constraints
WorkManager 允许我们指定任务执行的环境,比如网络已连接、电量充足时等,在满足条件的情况下任务才会执行。
(1)使用Constraints.Builder()创建并配置Constraints对象,可以指定上诉任务运行时间时的约束。
(2)创建Worker时调用setConstraints指定约束条件。
var constraint = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.setRequiresCharging(true).build()
var request = OneTimeWorkRequest.Builder(JetpackWork::class.java)
.setConstraints(constraint)
.build()
WorkManger提供了以下的约束作为Work执行的条件:
(1)setRequiredNetworkType:网络连接设置
(2)setRequiresBatteryNotLow:是否为低电量时运行 默认false
(3)setRequiresCharging:是否要插入设备(接入电源),默认false
(4)setRequiresDeviceIdle:设备是否为空闲,默认false
(5)setRequiresStorageNotLow:设备可用存储是否不低于临界阈值
2.6 取消任务
(1)从WorkRequest()获取Worker的ID
(2 调用WorkManager.getInstance().cancelWorkById(workRequest.id)根据ID取消任务。
WorkManager 对于已经正在运行或完成的任务是无法取消任务的。
WorkManager.getInstance(this).cancelWorkById(request.id)
2.7 添加TAG
通过为WorkRequest对象分配标记字符串来对任务进行分组
var twoRequest = OneTimeWorkRequest.Builder(JetpackTwoWork::class.java)
.setConstraints(constraint)
.addTag("jetpack")
.build()
WorkManager.getStatusesByTag() 返回该标记的所有任务的列表信息。
WorkManager.getInstance(this).getWorkInfosByTag("jetpack")
WorkManager.cancelAllWorkByTag() 取消具有特定标记的所有任务
WorkManager.getInstance(this).cancelAllWorkByTag("jetpack")
通过获取LiveData查看具有特定标记的所有任务的状态WorkInfo.State
WorkManager.getInstance(this).getWorkInfosByTagLiveData("jetpack").observe(this, Observer {
})
进阶使用
3.1 数据交互
WorkManager可以将参数传递给任务,并让任务返回结果。传递和返回值都是键值对形式。
(1)使用 Data.Builder创建 Data 对象,保存参数的键值对。
(2)在创建WorkQuest之前调用WorkRequest.Builder.setInputData()传递Data的实例
var requestData = Data.Builder().putString("jetpack", "workermanager").build()
var request = OneTimeWorkRequest.Builder(JetpackWork::class.java)
.setConstraints(constraint)
.setInputData(requestData)
.build()
(3 在JetpackWork.doWork方法中通过getInputData获取传递值。
(4)在构建Data对象,跟随着任务的结果返回。
class JetpackWork(context: Context,workerParameters: WorkerParameters) : Worker(context,workerParameters){
override fun doWork(): Result {
Log.e("workermanager","work start:"+inputData.getString("jetpack"))
Thread.sleep(2_000)
Log.e("workermanager","do work thread msg :"+Thread.currentThread().name)
var outData = Data.Builder().putString("back","hi,jetpack").build()
return Result.success(outData)
}
}
(5) 通过LiveData监听Worker返回的数据。
WorkManager.getInstance(this).getWorkInfoByIdLiveData(request.id).observe(this, Observer {
Log.e("workermanager", "out data :" + it?.outputData?.getString("back"))
})
3.2 链式任务
- WorkManager允许拥有多个任务的工作序列按照顺序执行任务。
()使用该WorkManager.beginWith() 方法创建一个序列 ,并传递一个OneTimeWorkRequest对象;,该方法返回一个WorkContinuation对象。
(2)使用 WorkContinuation.then()添加剩余的任务。
(3)最后调用WorkContinuation.enqueue()将整个序列排入队列 。
如果中间有任何任务返回 Result.failure(),则整个序列结束。并且上一个任务的结果数据可以作为下一个任务的输入数据,实现任务之间的数据传递。
WorkManager.getInstance(this).beginWith(request).then(twoRequest).then(threeRequest).enqueue()
- 可以使用WorkContinuation.combine()方法连接多个链来创建更复杂的序列。
要建立这个序列,先创建两个单独的链,然后将它们连接在一起成为第三个链:
WorkContinuation chainAC = WorkManager.getInstance()
.beginWith(worker A)
.then(worker C);
WorkContinuation chainBD = WorkManager.getInstance()
.beginWith(worker B)
.then(worker D);
WorkContinuation chainAll = WorkContinuation
.combine(chainAC, chainBD)
.then(worker E);
chainAll.enqueue();
在这种情况下,WorkManager在worker C之前运行workA,它也在workD之前运行workB, WorkB和workD都完成后,WorkManager 运行workE。
注意:虽然WorkManager依次运行每个子链,但不能保证链1中的任务与 链2中的任务重叠,例如,workB可能在workC之前或之后运行,或者它们可能同时运行。唯一可以保证的是每个子链中的任务将按顺序运行。
3.3 唯一的工作序列
我们可以创建一个唯一的工作序列,在任务队列里,同一个任务只存在一个,避免任务的重复执行。通过调用 beginUniqueWork() 来创建唯一的工作序列。
参数含义:1、工作序列的名称 2、当有相同名称序列时采取的策略方式 3、需要执行的Worker
WorkManager.getInstance(this).beginUniqueWork("jetpack",ExistingWorkPolicy.APPEND,request).enqueue()
ExistingWorkPolicy提供以下策略:
(1)ExistingWorkPolicy.REPLACE:取消现有序列并将其替换为新序列
(2)ExistingWorkPolicy.KEEP:保留现有序列并忽略您的新请求
(3)ExistingWorkPolicy.APPEND:将新序列附加到现有序列,在现有序列的最后一个任务完成后运行新序列的第一个任务。
源码分析
下面我们带着三个问题来看代码,梳理一下WorkManager的源码
1.没有任务约束Constraints的任务是如何执行的。
2.添加任务约束Constraints的任务是如何被触发的。
4.1 WorkManager类
- 通过上面我们知道任务的执行,是通过**WorkManager.getInstance(this).enqueue(request)**执行的。
WorkManager是个抽象类,通过WorkManager.getInstance方法返回的是,它的子类WorkManagerImpl的单例对象。
(1)单例模式初始化WorkManagerImpl对象
(2)调用getInstance方法返回sDelegatedInstance对象。这里sDelegatedInstance对象已经不是nulll了。下面我们就来分析一下sDelegatedIns