Android 后台任务管理调度
android后台任务执行调度一直问题蛮多的,在Android5.0之后加入了一个JobScheduler,管理这些后台执行的任务,并且给了一些限制策略,一方面控制耗电,一方面也尽量保证任务执行。项目中一直用这个管理器来执行一些后台的任务。
用起来也很简单,首先建一个任务的Service要继承自JobService,并且把这个Service注册到Manifest文件里面:
<service
android:name=".DemoJobService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
这个Service里面要实现两个方法:
override fun onStartJob(params: JobParameters?): Boolean {
XLog.info("onStartJob :" + params?.jobId)
executeTask(params)
return true//true还在执行,任务执行完成后需要手动调用jobFinished, false 表示任务执行完了
}
override fun onStopJob(params: JobParameters?): Boolean {
XLog.info("onStopJob:" + params?.jobId)
return false//true 表示异常结束的任务,重新安排任务 false是完成任务
}
最后就把这个任务放入管理调度器,由系统帮你管理执行:
val componentName = ComponentName(this, DemoJobService::class.java)
val jobInfo = JobInfo.Builder(JOB_ID, componentName)
.setPersisted(true)//手机重启之后是否继续
.setRequiresCharging(true)//充电的时候才执行
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)//不收费的网络连接,比如wifi
.setPeriodic(12 * 60 * 60 * 1000)//任务执行间隔
.build()
上面代码可以看到任务信息中可以加入一些限制条件,比如:需要网络、充电的时候执行,还有一些比如空闲的时候、电量低的时候不执行等等。
WorkManager
今年Google大会的时候,google给我们提供了一个叫 Android Jetpack
的Android 开发工具箱。它里面就包含了一个叫WorkManager
的任务管理工具,专门用来管理Android的后台任务。
先引入:
def work_version = "1.0.0-alpha02"
implementation "android.arch.work:work-runtime:$work_version" // use -ktx for Kotlin
implementation "android.arch.work:work-runtime-ktx:$work_version"
上面引入两个包,一个work-runtime就是这个任务管理工具的包,下面的加了-ktx的是kotlin的扩展,里面加了很多方便kotlin开发者的扩展工具。
说下主要的几个类:
- Worker:任务单元,任务管理器执行的任务对象,我们的任务需要继承这个类,然后把我们要执行的任务放到这里。
- WorkRequest:任务请求,它分两个子类, [
OneTimeWorkRequest
] 和 [PeriodicWorkRequest
],看名称就大概知道了,一个是执行一次的任务,一个是周期执行的任务。它将Worker封装起来,生成一个一次任务请求或者周期任务请求,然后可以加入一些限制条件[Constraints
] - WorkManager:
WorkRequest
封装好了之后就可以用WorkManager
来安排任务执行了,它是总的调度管理器 - WorkStatus:还有一个主要的类,这里面包含了持有任务的很多信息,可以通过google提供的LiveData持有这个Status,将任务信息传递到前台显示。
周期任务
比如后台需要有一个12小时执行一次的任务。
首先得有个任务Worker:
class DemoWorker: Worker() {
override fun doWork(): WorkerResult {
//需要做的工作在这里完成
doSomeWork()
// 返回 WorkerResult.SUCCESS
// WorkerResult.FAILURE(失败,不继续执行工作)
// WorkerResult.RETRY(重试,就是任务失败了,后面还会再执行这个任务)
return WorkerResult.SUCCESS
}
}
然后用PeriodicWorkRequest,创建一个任务请求,传给WorkManager进行任务安排:
// 条件限制
val constraints = Constraints.Builder()
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
.setRequiredNetworkType(NetworkType.CONNECTED)//网络连接
.build()
val periodicWork = PeriodicWorkRequestBuilder<DemoWorker>(12, TimeUnit.HOURS)//任务执行周期12小时一次
.setConstraints(constraints)
.build()
WorkManager.getInstance().enqueue(periodicWork)
这样任务就安排好了。
单次任务
单次任务和周期任务生成添加的方式一样,只是WorkRequest不一样:
// 条件限制
val constraints = Constraints.Builder()
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
.setRequiredNetworkType(NetworkType.CONNECTED)//网络连接
.build()
val oneTimeWorker = OneTimeWorkRequestBuilder<DemoWorker>()
.setConstraints(constraints)
.build()
WorkManager.getInstance().enqueue(oneTimeWorker)
但是,单次任务有更高级的玩法。
任务链 就是可以将多个任务按照一定的顺序自行
比如顺序执行:
WorkManager.getInstance()
.beginWith(workA)
.then(workB)
.then(workC)
.enqueue()
这上面的任务就按照workA 、workB、workC这样的顺序执行下来,但是如果里面有一个任务返回结果是:WorkerResult.FAILURE
那整个链条就断了,不往下执行了。
然后还可以并行:
WorkManager.getInstance()
.beginWith(workA1, workA2, workA3)
.then(workB)
.then(workC1, workC2)
.enqueue()
这样写的话,workA1、workA2、workA3先执行,他们三个没有先后,等这个三个都执行完了就开始执行workB,workB完成厚就执行workC1、workC2,C1、C2也是一样随机的没有顺序先后。
最后还有一个更高级了,连接多个任务链,就是下图这样的意思:
代码:
val chain1 = WorkManager.getInstance()
.beginWith(workA)
.then(workB)
val chain2 = WorkManager.getInstance()
.beginWith(workC)
.then(workD)
val chain3 = WorkContinuation
.combine(chain1, chain2)
.then(workE)
chain3.enqueue()
大概任务管理就这些,经过Google封装后,任务安排调度起来变得相当简单。
对了还有一些其它必要的操作:
取消任务
val demoWorkId:UUID = demoWork.getId()
WorkManager.getInstance().cancelWorkById(demoWorkId)
WorkStatus观察
WorkManager.getInstance().getStatusById(demoWork.id).observe(lifecycleOwner, Observer { workStatus->
if (workStatus != null && workStatus.state.isFinished) {
// ...
}
})
参数输入输出
class DemoWorker: Worker() {
override fun doWork(): WorkerResult {
//得到输入的参数
val inputArg = inputData.getString(INPUT_ARGUMENT_KEY, "")
//需要做的工作在这里完成
doSomeWork()
//输出参数定义
val output = mapOf<String, String>( OUTPUT_ARGUMENT_KEY to "输出内容")
outputData = output.toWorkData()//输出
// 返回 WorkerResult.SUCCESS
// WorkerResult.FAILURE(失败,不继续执行工作)
// WorkerResult.RETRY(重试,就是任务失败了,后面还会再执行这个任务)
return WorkerResult.SUCCESS
}
}
//输入参数定义
val inputData = mapOf(INPUT_ARGUMENT_KEY to "单次任务").toWorkData()
val oneTimeWorker = OneTimeWorkRequestBuilder<DemoWorker>()
.setInputData(inputData)//输入
.build()
WorkManager.getInstance().enqueue(oneTimeWorker)
WorkManager.getInstance().getStatusById(demoWork.id).observe(lifecycleOwner, Observer { workStatus->
if (workStatus != null && workStatus.state.isFinished) {
val out = workStatus.outputData.getString(OUTPUT_ARGUMENT_KEY, "")
Log.i("Status", "获得输出内容 :$out ")
// ...
}
})
添加Tag
val oneTimeWorker = OneTimeWorkRequestBuilder<DemoWorker>()
.addTag("myTag")//add tag
.build()
ok, The End !