JobService概述

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

主要运营场景:app服务保活,电量优化,在手机电量不能太低情况下推送,开启服务,延迟1分钟再推送。

机制原理

主要类似于Handle机制的实现,通过JobInfo.Builder生成JobInfo信息,然后JobSchedule通过JobInfo开启JobService

图片

实例分析

步骤实现

  1. 写一个服务继承JobService
  2. 清单文件注册并且加权限android:permission=“android.permission.BIND_JOB_SERVICE”(如果JobService未申请权限或者申请的权限未被授予,系统将忽略该jobService的调度请求)
  3. JobInfo.Builder控制服务开启条件
  4. .Schedule(JobInfo)开启服务

JobService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package com.team308.mytest.jobService

import android.app.job.JobParameters
import android.app.job.JobService
import android.content.Intent
import android.os.Build
import android.os.Handler
import android.os.Message
import android.util.Log
import androidx.annotation.RequiresApi

//最低要求SDK版本21
/**
* 实现app保活策略
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
class MyJobService : JobService() {

companion object{
private const val TAG = "MyJobService"
}

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 onUnbind(intent: Intent?): Boolean {
Log.d(TAG, "onUnbind")
return super.onUnbind(intent)
}

override fun onDestroy() {
Log.d(TAG, "onDestroy")
super.onDestroy()
}
private val MESSAGE_ID_TASK = 0x01

//用于向主线程发送消息,因为JobService本来就是在主线程中执行的
//用于开启保活服务
private val mHandler = Handler { msg -> // 具体任务逻辑
// if (AppUtil.isAppRunning(
// CommonUtil.INSTANCE.getApp(),
// CommonUtil.INSTANCE.getApp().getPackageName()
// )
// ) {
// Log.d(
// com.atom.battery.support.keepalive.AliveJobService.TAG,
// "KeepAliveService----->APP活着的..."
// )
// } else {
// val alive = Intent(applicationContext, BatteryKeepService::class.java)
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// startForegroundService(alive)
// } else {
// startService(alive)
// }
// Log.d(
// com.atom.battery.support.keepalive.AliveJobService.TAG,
// "KeepAliveService----->APP被杀死,重启..."
// )
// }
// // 通知系统任务执行结束
jobFinished(msg.obj as JobParameters, false)
true
}

//一般只需要实现以下两种方法即可
override fun onStartJob(params: JobParameters?): Boolean {
Log.d(TAG, "onStartJob")
// 返回false,系统假设这个方法返回时任务已经执行完毕;
// 返回true,系统假定这个任务正要被执行
val msg = Message.obtain(
mHandler,
MESSAGE_ID_TASK,
params
)
mHandler.sendMessage(msg)
return true
}

override fun onStopJob(params: JobParameters?): Boolean {
Log.d(TAG, "onStopJob")
//防止内存泄漏
mHandler.removeMessages(MESSAGE_ID_TASK)
return false
}

}

JobScheduleManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.team308.mytest.jobService
import android.app.job.JobInfo
import android.app.job.JobScheduler
import android.content.ComponentName
import android.content.Context
import android.os.Build
import androidx.annotation.RequiresApi
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
class JobScheduleManager() {
constructor(con:Context) : this() {
this.mContext = con
mJobScheduler = con.getSystemService(JobScheduler::class.java)
}
companion object {
private const val JOB_ID = 1
private var mJobManager: JobScheduleManager? = null
//懒汉式
fun getInstance(con: Context): JobScheduleManager {
if (mJobManager == null) {
mJobManager = JobScheduleManager(con)
}
return mJobManager as JobScheduleManager
}
}
private var mJobScheduler: JobScheduler? = null
private var mContext: Context? = null
fun startJobScheduler(){
// 构建JobInfo对象,传递给JobSchedulerService
val builder = JobInfo.Builder(
JOB_ID,
ComponentName(mContext!!, MyJobService::class.java)
) //指定哪个JobService执行操作
// 设置每30秒执行一下任务
builder.setPeriodic(30000)
// 设置设备重启时,执行该任务
builder.setPersisted(true)
// 当插入充电器,执行该任务
builder.setRequiresCharging(true)
val info = builder.build()
//开始定时执行该系统任务
mJobScheduler!!.schedule(info)
}
fun stopJobScheduler(){
mJobScheduler!!.cancelAll()
}
}

JobServiceActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.team308.mytest.jobService

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.team308.mytest.R

class JobServiceActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_job_service)
//开启服务
JobScheduleManager.getInstance(this).startJobScheduler()
}
}

生命周期

图片

1、可以看到Jobservice生命周期。开启到任务结束对应:

onCreate->onStartJob->onUnbind->onDestroy

2、你或许会疑问,为啥onStartCommand没有回调???job结束了onStopjob 没有回调???

1、为啥onStartCommand没有回调:通过观看jobservice源码你会发现其内部是通过bind方式开启的服务,所以不走onStartCommand,并且绑定服务由系统控制。

2、job结束了onStopjob 没有回调:因为这属于系统设计。这个方法是当service的条件不满足时系统回调,例如build设置需要网络,这时用户断开网络。这时就会回调。

重要api介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
  /**
     * @param jobParameters 系统创建回调给我们的,不用自己创建,用于获得一些job的参数。例如job id
     * @return true,你的job为活跃状态。表明你的工作需要继续执行。一般为false。
     * 1、你可以主动调用jobFinished 去告诉系统job工作已经完成。结束当前job。
     * 2、job需要的约束条件不在满足时也会结束当前job。例如用户使用 JobInfo.Builder为job添加了setRequiresCharging(boolean)电量约束。
     * 当用户吧设备电源关闭时,系统会立刻停止改job,该job的onStopJob方法会被回调。
     * <p>
     * 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;
    }

JobInfo.Build 的重要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(1000L10) 设置执行的最后期限,在最后期限到达之前会执行此job*(与setPeriodic一起使用会报异常)

JobSchedule常用API

scheduler.schedule(jobinfo) 开启jobservice

cancel(jobid) 结束指定id的service

cancelAll() 结束所有的service

JobInfo jobinfo = scheduler.getPendingJob(jobid) 根据 jobid 获取jobInfo对象

List allPendingJobs = scheduler.getAllPendingJobs() 获得所有的jobInfo

参考:https://blog.csdn.net/qq_38350635/article/details/106390343