前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android之任务调度WorkManager和JobSchedule的使用

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

作者头像
Vaccae
发布2021-09-17 10:10:31
3.2K0
发布2021-09-17 10:10:31
举报
文章被收录于专栏:微卡智享微卡智享微卡智享

前言

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

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

03

创建WorkRequest

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

  • 调度一次性工作和重复性工作
  • 设置工作约束条件,例如要求连接到 Wi-Fi 网络或正在充电
  • 确保至少延迟一定时间再执行工作
  • 设置重试和退避策略
  • 将输入数据传递给工作
  • 使用标记将相关工作分组在一起

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

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和权限 <service android:name="MyJobService" android:permission="android.permission.BIND_JOB_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
 * 邮箱: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()
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
 * 邮箱: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秒钟,可以看到,每隔三秒都会输出,说明当前的间隔任务已经实现。‍

需要提醒

  • JobScheduler和WorkManager都只能在APP存活的时候执行,但是定时器是一直工作的。
  • 关闭APP再启动,JobScheduler并不能够直接继续运行,但是WorkManager可以。
  • 如果重启APP的时候,WorkManager任务的计时器应该已经执行了一次或多次,则会立即开始执行。
  • 重启App之后WorkManager如果直接执行了一个任务,则从这个时候开始算新的周期,不会按旧有周期走。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-08-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微卡智享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JobScheduler
  • JobService
  • JobScheduler实现定时间隔处理
  • 需要提醒
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档