Android JobService

JobService这个东西或许我们真的没有接触过,但是他的出现解决了特定的问题:比如电量优化、服务广播多重绑定执行任务等。公司的业务逻辑中就用到了这个Service,趁着空闲来总结篇。正好完善下自己的Service知识体系。

1、故事引入:JobService出现的背景

A:假如现在有个需求…我需要做一套推送逻辑,从服务器获得数据后向通知栏推送消息。
B:这个so easy,我搞个普通的service就ojbk了。service中做请求,请求解析完毕后台推送。
A:那好吧,我要添加需求了,为了友好用户体验,我需要用户在手机电量不能太低情况下推送。
B:这个可以解决,我监听电量广播。
A:我还要加需求,我要你开启服务,延迟1分钟再推送。。。
B:emmm,,,,需求甄姬多,这越搞越复杂。
A:哈哈,给你说个好用的类JobService,专门解决这类问题。

2、啥是JobService

JobService用于执行一些需要满足特定条件但不紧急的后台任务,利用 JobScheduler 来执行这些特殊的后台任务来减少电量的消耗。开发者可以设定需要执行的任务JobService,以及任务执行的条件 JobInfo,JobScheduler 会将任务加入到队列。在特定的条件满足时 Android 系统会去批量的执行所有应用的这些任务,而非对每个应用的每个任务单独处理。这样可以减少设备被唤醒的次数。

3、JobService相关类的介绍

安卓入门人员也会知道Handle吧!handle与之相关的还有几个类:Message、Looper、MessageQueue。这几个类共同来完成安卓的消息机制。可能有人会疑问老铁你跑题了吧???哈哈这里我做了个类比,其实JobService与之类似,他与JobScheduler、JobInfo.Builder、JobInfo这几个重要的类来共同完成JobService机制。

Android JobService_第1张图片

4、结合栗子分析

实现步骤简介:
1、写个服务继承jobService
2、清单文件注册并且加权限android:permission=“android.permission.BIND_JOB_SERVICE”
3、jobBuild控制服务开启的条件
4、jobschedule开启服务
简单栗子:如下模拟推送栗子

(1)PushJobService


/**
 * Created sunnyday sunnyday on 2020/5/27 14:58
 */
private const val TAG = "PushJobService"

class PushJobService : JobService() {

    override fun onCreate() {
        Log.d(TAG, "onCreate")
        super.onCreate()

    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(TAG, "onStartCommand" + super.onStartCommand(intent, flags, startId))
        return super.onStartCommand(intent, flags, startId)
    }


    override fun onStopJob(params: JobParameters?): Boolean {
        Log.d(TAG, "onStopJob")
        return false
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun onStartJob(params: JobParameters?): Boolean {
        Log.d(TAG, "onStartJob")
        pushData()
        schedulePushWork(applicationContext, 0) // after 20s push again。
        return false
    }

    override fun onUnbind(intent: Intent?): Boolean {
        Log.d(TAG, "onUnbind")
        return super.onUnbind(intent)
    }

    override fun onDestroy() {
        Log.d(TAG, "onDestroy")
        super.onDestroy()
    }

    private fun pushData() {
        // todo push data logical
    }
}

(2)自定义一个帮助类:ScheduleHelper



/**
 * Created by sunnyday on 2020/5/27 15:05
 */
private const val TAG = "ScheduleHelper"

/**
 * schedule job
 *
 * */
@RequiresApi(Build.VERSION_CODES.N)
fun schedulePushWork(context: Context, currentId: Int) {
    val jobScheduler = context.getSystemService(JobScheduler::class.java)
    val componentName = ComponentName(context, PushJobService::class.java)
    val builder = JobInfo.Builder(generateNextJobId(jobScheduler, currentId), componentName)
    builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)//need environment of network
    builder.setMinimumLatency((1000 * 20).toLong())// delay 20s before start jobService
    jobScheduler.schedule(builder.build())
    Log.d(TAG, "schedulePushWork...")
}


/**
 * generate a jobId by currentId
 *
 * @param currentId current job's id
 * @return  next job's id
 * */
@RequiresApi(Build.VERSION_CODES.N)
private fun generateNextJobId(jobScheduler: JobScheduler, currentId: Int): Int {
    //avoid crossing the border
    var tempId: Int = if (currentId == Int.MAX_VALUE) {
        1
    } else {
        currentId + 1
    }

    // avoid new id is used, if so old id plus 1
    while (jobScheduler.getPendingJob(currentId) != null) {
        if (tempId == Int.MAX_VALUE) {
            1
        } else {
            tempId++
        }
    }
    return tempId
}

(3)MainActivity中开启

class MainActivity : AppCompatActivity() {

    @RequiresApi(Build.VERSION_CODES.N)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        schedulePushWork(this, 0) //start
    }
}

(4)log

2020-05-27 15:46:03.757 8255-8255/com.example.jobservicedemo D/PushJobService: onCreate
2020-05-27 15:46:03.762 8255-8255/com.example.jobservicedemo D/PushJobService: onStartJob
2020-05-27 15:46:03.767 8255-8255/com.example.jobservicedemo D/PushJobService: onUnbind
2020-05-27 15:46:03.767 8255-8255/com.example.jobservicedemo D/PushJobService: onDestroy

1、可以看到Jobservice生命周期。开启到任务结束对应:
onCreate->onStartJob->onUnbind->onDestroy
2、你或许会疑问,为啥onStartCommand没有回调???job结束了onStopjob 没有回调???

1、为啥onStartCommand没有回调:通过观看jobservice源码你会发现其内部是通过bind方式开启的服务,所以不走onStartCommand,并且绑定服务由系统控制。
2、job结束了onStopjob 没有回调:因为这属于系统设计。这个方法是当service的条件不满足时系统回调,例如build设置需要网络,这时用户断开网络。这时就会回调。

5、重要api及其方法介绍

(1)方法

  /**
     * @param jobParameters 系统创建回调给我们的,不用自己创建,用于获得一些job的参数。例如job id
     * @return true,你的job为活跃状态。表明你的工作需要继续执行。一般为false。
     * 1、你可以主动调用jobFinished 去告诉系统job工作已经完成。结束当前job。
     * 2、job需要的约束条件不在满足时也会结束当前job。例如用户使用 JobInfo.Builder为job添加了setRequiresCharging(boolean)电量约束。
     * 当用户吧设备电源关闭时,系统会立刻停止改job,该job的onStopJob方法会被回调。
     * 

* 3、只要你的job正在执行时,系统就会持有你app的唤醒锁。(此方法调用之前,系统就会获得唤醒锁)在您主动调用obFinished或者系统调用 * onStopJob后这把锁才被释放。 * 4、返回false代表您写在此方法体中的工作已经完成,系统将释放该job的锁。系统不会去调用onStopJob * @function job执行时调用此方法。这个方法运行在app的主线程中。你要重写这个方法,做一些自己的逻辑工作。 */ // 触发build的开启机制时走此方法.返回值一般为false.走完后系统不会调用onStopJob. @Override public boolean onStartJob(JobParameters jobParameters) { Log.i(TAG, "onStartJob: "); return false; } /** * 1、当系统确定停止job时会调用此方法,如果不满足build设置的相关要求时会触发此方法。 * 例如:你设置了setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY),执行任务期间你切换了wifi。 * 2、这个方法回调时,系统将释放app的唤醒锁。 * * @param jobParameters 系统创建回调给我们的,不用自己创建,用于获得一些job的参数。例如job id * @return true 向JobManager 表明你要基于创建工作时的重试条件重新 schedule 这个job。false表明 * 彻底结束了这个job。 * 3、无论返回值如何都表明当前的job执行完毕啦。 */ // 当系统确定停止job时会调用此方法,如果不满足build设置的相关要求时会触发此方法. @Override public boolean onStopJob(JobParameters jobParameters) { Log.i(TAG, "onStopJob: "); return false; }

(2)JobInfo.Build 的重要API

API 简介
setRequiredNetworkType 网络要求(例子JobInfo.NETWORK_TYPE_ANY表名需要有网)
setRequiresBatteryNotLow(true) 运行此job时,设备电量不能低
setRequiresCharging(true) 默认值为false。true表示此job需要在“充电”状态下工作。这里的充电可以理解为充电玩手机时,电量是增的。(假如你使用usb插在电脑上边充电边看视屏,电量可能冲着还减着)
setRequiresDeviceIdle(true) 设置设备在空闲的时候执行job,此条件一般很难达到。
setRequiresBatteryNotLow(true) 运行此job需要设备的可用存储空间不能太低。
builder.setPeriodic(5*1000) 设置的时间段内执行,执行不会超过一次。你不能控制执行的时间,仅仅能确定的是在设定的期间内job会执行一次。(安卓7.0开始这个值最小要设置15min否则15min内不起作用)
builder.setMinimumLatency(1000 * 5) 设置延迟的时间,在延迟时间到达之前不会考虑执行job(与setPeriodic一起使用会报异常)
builder.setOverrideDeadline(1000L*10) 设置执行的最后期限,在最后期限到达之前会执行此job(与setPeriodic一起使用会报异常)

(3)JobSchedule常用API

API 简介
scheduler.schedule(jobinfo) 开启jobservice
cancel(jobid) 结束指定id的service
cancelAll() 结束所有的service
JobInfo jobinfo = scheduler.getPendingJob(jobid) 根据 jobid 获取jobInfo对象
List《JobInfo》allPendingJobs = scheduler.getAllPendingJobs() 获得所有的jobInfo
6、jobservice自启动方案

1、onstopjob返回值为true.(对于调用cancel的job无效)
2、jobservice的生命周期方法中再次schedule下jobservice(推荐做法)

the end

简单总结了下,更多信息参考官网。

你可能感兴趣的:(android)