Android之任务调度WorkManager和JobSchedule的使用

学更好的别人,

做更好的自己。

——《微卡智享》

Android之任务调度WorkManager和JobSchedule的使用_第1张图片

本文长度为5347,预计阅读11分钟

前言

调度任务也是最近产品中需要用的,定时与后台进行数据同步,研究了几种方法后,觉得还是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
 * 邮箱:[email protected]
 * 创建时间: 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 服务工作是否成功,以及工作失败时是否应重试工作。

  • Result.success():工作成功完成。

  • Result.failure():工作失败。

  • Result.retry():工作失败,应根据其重试政策在其他时间尝试。

03

创建WorkRequest

可以自定义 WorkRequest 对象来处理常见用例,例如:

  • 调度一次性工作和重复性工作

  • 设置工作约束条件,例如要求连接到 Wi-Fi 网络或正在充电

  • 确保至少延迟一定时间再执行工作

  • 设置重试和退避策略

  • 将输入数据传递给工作

  • 使用标记将相关工作分组在一起

WorkRequest 对象包含 WorkManager 调度和运行工作所需的所有信息。其中包括运行工作必须满足的约束、调度信息(例如延迟或重复间隔)、重试配置,并且可能包含输入数据(如果工作需要)。

WorkRequest 本身是抽象基类。该类有两个派生实现,可用于创建 OneTimeWorkRequest 和 PeriodicWorkRequest 请求。顾名思义,OneTimeWorkRequest 适用于调度非重复性工作,而 PeriodicWorkRequest 则更适合调度以一定间隔重复执行的工作。

Android之任务调度WorkManager和JobSchedule的使用_第2张图片

以下代码会构建了一个工作请求,该工作请求仅在用户设备正在充电且连接到 Wi-Fi 网络时才会运行:

val constraints = Constraints.Builder()
   .setRequiredNetworkType(NetworkType.UNMETERED)
   .setRequiresCharging(true)
   .build()


val myWorkRequest: WorkRequest =
   OneTimeWorkRequestBuilder()
       .setConstraints(constraints)
       .build()


Android之任务调度WorkManager和JobSchedule的使用_第3张图片

val myWorkRequest = OneTimeWorkRequestBuilder()
   .setInitialDelay(10, TimeUnit.MINUTES)
   .build()

我这边主要用的是重复的调度,也不需要别的参数设置,所以直接创建了PeriodicWorkRequest调用,在Activity中代码如下:

        //创建WorkManager任务
        val periodicwork =
            PeriodicWorkRequestBuilder(5000, TimeUnit.MILLISECONDS).build()
        WorkManager.getInstance(this).enqueueUniquePeriodicWork(
            "test", ExistingPeriodicWorkPolicy.REPLACE,
            periodicwork
        )


代码中设置了重复间隔的时间为5秒钟,结果运行起来后,5秒是不起作用的,还是间隔的15分钟,效果如下图:

Android之任务调度WorkManager和JobSchedule的使用_第4张图片

总的来说其实WorkManager还是挺不错的,简单,方便,可以多任务,如果不是对间隔时间要求短,推荐使用WorkManager。

JobSchedule

微卡智享

JobScheduler和JobService是安卓在api 21中增加的接口,用于在某些指定条件下执行后台任务。

JobScheduler

JobScheduler是用于计划基于应用进程的多种类型任务的api接口。

  • 对象获取方法:[Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)]

  • 使用JobInfo.Builder.JobInfo.Builder(int, android.content.ComponentName)构造JobInfo对象,并作为参数传给JobSechduler的schedule(JobInfo)方法。

  • 当JobInfo中声明的执行条件满足时,系统会在应用的JobService中启动执行这个任务。
    当任务执行时,系统会为你的应用持有WakeLock,所以应用不需要做多余的确保设备唤醒的工作。

JobService

JobService继承自Service,是用于处理JobScheduler中规划的异步请求的特殊Service

  • 使用JobService必须先在AndroidManifest.xml中声明service和权限

  • 应用需要实现onStartJob(JobParameters)接口,在其中执行任务逻辑。

  • 这个Service会在一个运行在主线程的Handler中执行规划的任务,所以应用需要在另外的thread/handler/AsyncTask中执行业务逻辑,如果不这么做的话可能会引起主线程的阻塞。

  • onStopJob(android.app.job.JobParameters)接口是当计划的执行条件“不再”满足时被执行的(例如网络中断)。

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
 * 邮箱:[email protected]
 * 创建时间: 11:31
 * 功能模块说明:
 */
class TestJobService : JobService() {


    private var TAG= "taskjob";


    suspend fun Count():Flow{
        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()


setPeriodic的最小执行间隔,从Android7.0后,这个设置最少也是15分钟了,就是你设置的再短也是按15分钟执行。
  • 在获取执行间隔时,会先比较最小间隔时间和设置的间隔时间,取其中大的那个。所以setPeriodic设置时间小于15分钟是不会生效的。

  • flexMillis参数是用来设置周期任务执行的活动时间的,这意味着JobScheduler规划的任务不是在精确的时间执行的。并且这个时间也是有最小值的,系统默认5分钟。

  • setMinimumLatency和setOverrideDeadline不能同setPeriodic一起使用,会引起报错。

上面的这段任务,调用后只会执行一次,因为把最小间隔去掉了,即使设置了也是15分钟的周期,无法实现我想要的效果,接下来就是本篇的重点了,利用JobScheduler自己写了个间隔时间的处理。

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
 * 邮箱:[email protected]
 * 创建时间: 13:44
 * 功能模块说明:
 */
class PeriodicJobService: JobService() {


    private suspend fun Count(): Flow {
        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秒钟,可以看到,每隔三秒都会输出,说明当前的间隔任务已经实现。‍

需要提醒

  • JobScheduler和WorkManager都只能在APP存活的时候执行,但是定时器是一直工作的。

  • 关闭APP再启动,JobScheduler并不能够直接继续运行,但是WorkManager可以。

  • 如果重启APP的时候,WorkManager任务的计时器应该已经执行了一次或多次,则会立即开始执行。

  • 重启App之后WorkManager如果直接执行了一个任务,则从这个时候开始算新的周期,不会按旧有周期走。

扫描二维码

获取更多精彩

微卡智享

Android之任务调度WorkManager和JobSchedule的使用_第5张图片

「 往期文章 」

Android使用LiveEventBus消息实现组件间通讯

Android制作AAR包并混淆后加载调用

Android使用Flow检测版本升级自动下载安装

你可能感兴趣的:(android,java,移动开发,多进程,go)