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机制。
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方法会被回调。
* <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;
}
(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
简单总结了下,更多信息参考官网。