前言
调度任务也是最近产品中需要用的,定时与后台进行数据同步,研究了几种方法后,觉得还是JobSchedule相对效果还好点,主要原因是WorkManager的定时任务最短也需要15分钟,虽然JobSchedule在Android7.0后也这样的,但是可以通过别的办法实现,所以两个都说一下,两个也都会用到。
WorkManger
微卡智享
WorkManager 是一个 Android Jetpack 扩展库,它可以让您轻松规划那些可延后、异步但又需要可靠运行的任务。对于绝大部分后台执行任务来说,使用 WorkManager 是目前 Android 平台上的最佳实践。
WorkManager使用起来也非常简单,因为我这边定时任务的频率在1分钟以内,如果不是因为最小间隔是15分钟的原因,就全部使用WorkManager了,直接代码开始。
01
添加依赖项
在新建的工程的build.gradle中加入依赖项
def work_version = "2.5.0"
// (Java only)
implementation "androidx.work:work-runtime:$work_version"
// Kotlin + coroutines
implementation "androidx.work:work-runtime-ktx:$work_version"
02
创建自己的Worker类
新建一个TestWorker继承自Worker,里面只有一个重写的方法dowork()
package dem.vaccae.task
import android.content.Context
import android.os.Parcel
import android.os.Parcelable
import android.util.Log
import androidx.work.Data
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.Worker
import androidx.work.WorkerParameters
import java.util.concurrent.TimeUnit
/**
* 作者:Vaccae
* 邮箱:3657447@qq.com
* 创建时间: 10:43
* 功能模块说明:WorkManager测试类
*/
class TestWorker(context: Context, parameters: WorkerParameters) : Worker(context,parameters) {
private var TAG= "taskjob";
private var times =0;
override fun doWork(): Result {
times++;
var data=Data.Builder().putInt("times",times).build();
if(times<10){
Log.i(TAG, "我是Work的测试")
return Result.success()
}else{
Log.i(TAG, "重新测试")
return Result.failure()
}
}
}
从 doWork() 返回的 Result 会通知 WorkManager 服务工作是否成功,以及工作失败时是否应重试工作。
03
创建WorkRequest
可以自定义 WorkRequest 对象来处理常见用例,例如:
WorkRequest 对象包含 WorkManager 调度和运行工作所需的所有信息。其中包括运行工作必须满足的约束、调度信息(例如延迟或重复间隔)、重试配置,并且可能包含输入数据(如果工作需要)。
WorkRequest 本身是抽象基类。该类有两个派生实现,可用于创建 OneTimeWorkRequest 和 PeriodicWorkRequest 请求。顾名思义,OneTimeWorkRequest 适用于调度非重复性工作,而 PeriodicWorkRequest 则更适合调度以一定间隔重复执行的工作。
以下代码会构建了一个工作请求,该工作请求仅在用户设备正在充电且连接到 Wi-Fi 网络时才会运行:
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresCharging(true)
.build()
val myWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<MyWork>()
.setConstraints(constraints)
.build()
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
.setInitialDelay(10, TimeUnit.MINUTES)
.build()
我这边主要用的是重复的调度,也不需要别的参数设置,所以直接创建了PeriodicWorkRequest调用,在Activity中代码如下:
//创建WorkManager任务
val periodicwork =
PeriodicWorkRequestBuilder<TestWorker>(5000, TimeUnit.MILLISECONDS).build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"test", ExistingPeriodicWorkPolicy.REPLACE,
periodicwork
)
代码中设置了重复间隔的时间为5秒钟,结果运行起来后,5秒是不起作用的,还是间隔的15分钟,效果如下图:
总的来说其实WorkManager还是挺不错的,简单,方便,可以多任务,如果不是对间隔时间要求短,推荐使用WorkManager。
JobSchedule
微卡智享
JobScheduler和JobService是安卓在api 21中增加的接口,用于在某些指定条件下执行后台任务。
JobScheduler是用于计划基于应用进程的多种类型任务的api接口。
JobService继承自Service,是用于处理JobScheduler中规划的异步请求的特殊Service
01
创建JobService
package dem.vaccae.task.jobschedule
import android.app.job.JobParameters
import android.app.job.JobService
import android.util.Log
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import java.lang.Exception
/**
* 作者:Vaccae
* 邮箱:3657447@qq.com
* 创建时间: 11:31
* 功能模块说明:
*/
class TestJobService : JobService() {
private var TAG= "taskjob";
suspend fun Count():Flow<Int>{
return flow {
for(i in 1..10){
delay(100)
emit(i)
}
}
}
override fun onStartJob(p0: JobParameters?): Boolean {
// 返回true,表示该工作耗时,同时工作处理完成后需要调用onStopJob销毁(jobFinished)
// 返回false,任务运行不需要很长时间,到return时已完成任务处理
val resScope = CoroutineScope(Job())
resScope.launch {
try {
Count().flowOn(Dispatchers.IO).collect {
Log.i(TAG, "collect $it")
}
//任务完成
Log.i(TAG, "jobFinished")
jobFinished(p0,false)
}catch (e:Exception){
e.printStackTrace()
Log.e(TAG, e.message.toString())
}
}
return true
}
override fun onStopJob(p0: JobParameters?): Boolean {
// 有且仅有onStartJob返回值为true时,才会调用onStopJob来销毁job
// 返回false来销毁这个工作
Log.i(TAG, "jobTest is over")
return false
}
}
创建了一个TestJobService 继承自JobService,主要在onStartJob中写入执行的函数,这里用flow写了个循环的输出打印。
02
创建JobScheduler任务
//创建JobSchedule任务
val mJobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
val jobid = 10;
var componentName = ComponentName(this, TestJobService::class.java)
val jobinfo = JobInfo.Builder(jobid, componentName)
//设置间隔时间,不断的触发任务的启动,android 7后最少时间是15分钟,所以不用了
// .setPeriodic(3000)
.setMinimumLatency(15000)// 设置任务运行最少延迟时间,与setPeriodic相似,只是间隔时间不确定,不能与setPeriodic一起使用,
.setOverrideDeadline(50000)// 设置deadline,若到期还没有达到规定的条件则会开始执行
.setPersisted(true)//设备重启之后你的任务是否还要继续执行
.build()
上面的这段任务,调用后只会执行一次,因为把最小间隔去掉了,即使设置了也是15分钟的周期,无法实现我想要的效果,接下来就是本篇的重点了,利用JobScheduler自己写了个间隔时间的处理。
package dem.vaccae.task.jobschedule
import android.app.job.JobInfo
import android.app.job.JobParameters
import android.app.job.JobScheduler
import android.app.job.JobService
import android.content.ComponentName
import android.content.Context
import android.util.Log
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import java.lang.Exception
import java.util.concurrent.TimeUnit
/**
* 作者:Vaccae
* 邮箱:3657447@qq.com
* 创建时间: 13:44
* 功能模块说明:
*/
class PeriodicJobService: JobService() {
private suspend fun Count(): Flow<Int> {
return flow {
for(i in 200..210){
delay(100)
emit(i)
}
}
}
override fun onStartJob(p0: JobParameters?): Boolean {
val resScope = CoroutineScope(Job())
resScope.launch {
try {
Count().flowOn(Dispatchers.IO).collect {
Log.i(TAG, "collect $it")
}
Log.i(TAG, "jobFinished")
}catch (e: Exception){
e.printStackTrace()
Log.e(TAG, e.message.toString())
}
}
startScheduler(this)
return false
}
override fun onStopJob(p0: JobParameters?): Boolean = false
companion object {
var TAG: String = "taskjob"
var JOBID : Int = 100
var InterValTime :Long = 10000
private var jobScheduler: JobScheduler? = null
private var jobInfo: JobInfo? = null
fun startScheduler(context: Context) {
jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
cancelScheduler()
if (jobInfo == null) {
jobInfo = JobInfo.Builder(JOBID, ComponentName(context, PeriodicJobService::class.java))
.setMinimumLatency(InterValTime) // 最小为10秒
.build()
}
val result = jobScheduler?.schedule(jobInfo!!)
}
fun cancelScheduler() {
jobScheduler?.cancel(JOBID)
}
}
}
代码中主要是通过递归的方式,在onStartJob中,利用setMinimumLatency来设置时间间隔,执行完后再重新创建启用任务来实现。写为了静态方法,外部调用也方便。
外部调用直接一句
//启动周期性任务
PeriodicJobService.startScheduler(this)
接下来看看实现的效果:
上面设置了为3秒钟,可以看到,每隔三秒都会输出,说明当前的间隔任务已经实现。
完