JobService的使用介绍(2)

简介: JobService的使用介绍(2)

JobService自启动

篇首的时候说到Job条件不满足的时候,JobScheduler会强制停止该Job,并在条件满足的时候再次启动该Job。

我们来验证下是否符合预期。

IDLE状态下Job停止的例子

我们目前的demo里在创建JobInfo的时候并没有调用setRequiresDeviceIdle()。

而这个函数的默认值为false,即意味着该Job不需要在IDLE状态下运行。

那么等同于如果系统进入了IDLE状态,那么该Job的条件应该不满足,会被停止。

当不是IDLE状态了,条件满足了,Job又会被执行。


我们开始验证如上猜想,再次点击schedule的button,确保我们的Job开始了。

然后,使用如下命令使得设备强制进入IDLE状态。


1. adb shell dumpsys battery unplug 将USB充电停止


2. adb shell dumpsys battery 查看电池状态确保充电状态关闭

Current Battery Service state:
  (UPDATES STOPPED -- use 'reset' to restart)
  AC powered: false

3. adb shell dumpsys deviceidle enable 将IDLE状态许可

Deep idle mode enabled
Light idle mode enable

4. adb shell dumpsys deviceidle force-idle 强制进入IDLE状态

Now forced in to deep idle mode

这时候查看log,发现我们的JobService被停止和销毁了。

01-31 18:39:31.064 15504 15504 W Ellison : MainActivity onClick_Schedule()
01-31 18:39:31.065 15504 15504 W Ellison : Helpers schedule()
01-31 18:39:31.096 15504 15504 W Ellison : EllisonsJobService onCreate()
01-31 18:39:31.104 15504 15504 W Ellison : EllisonsJobService onStartJob()
01-31 18:39:31.104 15504 15504 W Ellison : Helpers doHardWork()
01-31 18:39:38.870 15504 15504 W Ellison : EllisonsJobService stopped & wait to restart params:android.app.job.JobParameters@1a480b9 reason:4★
01-31 18:39:38.880 15504 15504 W Ellison : EllisonsJobService destroyed.

★处显示被停止的原因值为4。

而据JobParameters.java里关于stop reason的如下定义得知确实是因为进入IDLE状态后Job被中止。

public static final int REASON_DEVICE_IDLE = 4;

5.这时候我们使用如下命令将设备走出IDLE状态。

adb shell dumpsys deviceidle disable

Deep idle mode disabled
Light idle mode disabled

再次查看log,发现我们的JobService并没有被重新启动。

哪里出了问题呢?

查阅官方文档,我们发现了答案。

boolean onStopJob (JobParameters params)

...

Returns

boolean True to indicate to the JobManager whether you'd like to reschedule this

job based on the retry criteria provided at job creation-time. False to drop

the job. Regardless of the value returned, your job must stop executing.

原来在条件不满足的时候系统会强制停止该Job并回调onStopJob()。

onStopJob()里执行停止本地任务的逻辑。如果希望该Job在条件满足的时候被重新启动,应该将返回值置为true。

那我们将onStopJob()的返回值改为true,再试一下。

public class EllisonsJobService extends JobService {
  ...
  @Override
  public boolean onStopJob(JobParameters params) {
  Log.w(TAG, "EllisonsJobService stopped & wait to restart params:" + params + " reason:" + params.getStopReason());
  return true;
  }
}

再次执行上面的步骤。


   adb shell dumpsys battery unplug

   adb shell dumpsys deviceidle enable

   adb shell dumpsys deviceidle force-idle

   adb shell dumpsys deviceidle disable


得到如下Log:

01-31 18:49:22.822 15745 15745 W Ellison : MainActivity onCreate() PID:15745 TID:15745
01-31 18:49:23.925 15745 15745 W Ellison : MainActivity onClick_Schedule()
01-31 18:49:23.926 15745 15745 W Ellison : Helpers schedule()
01-31 18:49:23.938 15745 15745 W Ellison : EllisonsJobService onCreate()
01-31 18:49:23.941 15745 15745 W Ellison : EllisonsJobService onStartJob()
01-31 18:49:23.941 15745 15745 W Ellison : Helpers doHardWork()
01-31 18:49:32.005 15745 15745 W Ellison : EllisonsJobService stopped & wait to restart params:android.app.job.JobParameters@1a480b9 reason:4
01-31 18:49:32.007 15745 15745 W Ellison : EllisonsJobService destroyed.
01-31 18:50:19.190 15745 15745 W Ellison : EllisonsJobService onCreate()
01-31 18:50:19.197 15745 15745 W Ellison : EllisonsJobService onStartJob()
01-31 18:50:19.198 15745 15745 W Ellison : Helpers doHardWork()

果然,onStopJob()的返回值改为true后,离开IDLE状态后系统可以将Job再次启动起来。

 

我们再验证另外一种情况,如果Job需要网络条件,那么断网后是否也能够停止Job,开网后是否能够再次启动Job?

网络条件下执行Job

修改的代码如下:

public class Helpers {
  ...
  public static void schedule(Context context) {
  ...
  builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
  // builder.setOverrideDeadline(EllisonsJobService.ELLISONS_JOB_OVERDIDE_DEADLINE);
  scheduler.schedule(builder.build());
  }
}

首先开网并点击schedule的button,可以看到service开始运行。

01-31 19:00:56.239 16043 16043 W Ellison : MainActivity onCreate() PID:16043 TID:16043
01-31 19:01:00.943 16043 16043 W Ellison : MainActivity onClick_Schedule()
01-31 19:01:00.943 16043 16043 W Ellison : Helpers schedule()
01-31 19:01:00.952 16043 16043 W Ellison : EllisonsJobService onCreate()
01-31 19:01:00.954 16043 16043 W Ellison : EllisonsJobService onStartJob()
01-31 19:01:00.954 16043 16043 W Ellison : Helpers doHardWork()

我们关闭网络,可以看到service停止运行了。

01-31 19:01:13.182 16043 16043 W Ellison : EllisonsJobService stopped & wait to restart params:android.app.job.JobParameters@8394280 reason:1★
01-31 19:01:13.184 16043 16043 W Ellison : EllisonsJobService destroyed.

★处显示被停止的原因值为1。

同样根据stop reason的定义得知Job是在条件不满足时候被中止的。

public static final int REASON_CONSTRAINTS_NOT_SATISFIED = 1;

我们再次开启网络,发现service自己起来了。

01-31 19:01:43.203 16043 16043 W Ellison : EllisonsJobService onCreate()
01-31 19:01:43.209 16043 16043 W Ellison : EllisonsJobService onStartJob()
01-31 19:01:43.209 16043 16043 W Ellison : Helpers doHardWork()

可以看到,网络条件的Job运行过程和IDLE例子结果完全一致。

 

综上可以发现JobService是如此的灵活,可以满足很多特定场景的要求。

这时候,我们思考一个问题,如果自己cancel了的job,同时onStopJob()里返回true,是否也能够自动再启动?

自行取消Job后能否自启动

这个问题不需要修改代码,只需要做如下操作。

点击schedule和cancel的button。

01-31 19:02:48.917 16043 16043 W Ellison : MainActivity onClick_Schedule()
01-31 19:02:48.918 16043 16043 W Ellison : Helpers schedule()
01-31 19:02:48.923 16043 16043 W Ellison : EllisonsJobService onCreate()
01-31 19:02:48.936 16043 16043 W Ellison : EllisonsJobService onStartJob()
01-31 19:02:48.936 16043 16043 W Ellison : Helpers doHardWork()
01-31 19:02:55.956 16043 16043 W Ellison : MainActivity onClick_Cancel()
01-31 19:02:55.956 16043 16043 W Ellison : Helpers cancelJob()
01-31 19:02:55.961 16043 16043 W Ellison : EllisonsJobService stopped & wait to restart params:android.app.job.JobParameters@4634857 reason:0
01-31 19:02:55.963 16043 16043 W Ellison : EllisonsJobService destroyed.

在cancel完之后等待了很长时间,service也没有自动启动起来。


这个是不是和上面的官方说明矛盾啊,不是说返回true,系统就会再次启动Job吗?


其实并不矛盾。

首先从逻辑上讲,APP自行取消的Job,系统是不需要为你再次启动的。

只有因外部条件不满足了等场合被强制的Job才有再次启动的可能性。



后期我们将从源码角度探究JobSecheduler如此设计的证据。


那如果说我们收到要求,不管是系统自动结束的我们Job还是我们自行Cancel了Job,

我们都希望Job能够再次启动。那该怎么办?



其实很简单,我们可以在onStopJob()或onDestroy()里再次schedule我们的jobservice。

这里以onStopJob()为例,我们将代码做下修改:

public class EllisonsJobService extends JobService {
  ...
  @Override
  public boolean onStopJob(JobParameters params) {
  Log.w(TAG, "EllisonsJobService stopped & wait to restart params:" + params + " reason:" + params.getStopReason());
  Helper.schedule(this);
  return false;
  }
}

继续点击点击schedule和cancel的button。

01-31 19:20:02.615 16589 16589 W Ellison : MainActivity onClick_Schedule()
01-31 19:20:02.615 16589 16589 W Ellison : Helpers schedule()
01-31 19:20:02.632 16589 16589 W Ellison : EllisonsJobService onCreate()
01-31 19:20:02.636 16589 16589 W Ellison : EllisonsJobService onStartJob()
01-31 19:20:02.636 16589 16589 W Ellison : Helpers doHardWork()
01-31 19:20:08.248 16589 16589 W Ellison : MainActivity onClick_Cancel()
01-31 19:20:08.248 16589 16589 W Ellison : Helpers cancelJob()
01-31 19:20:08.250 16589 16589 W Ellison : EllisonsJobService stopped & wait to restart params:android.app.job.JobParameters@cd62c5f reason:0
01-31 19:20:08.250 16589 16589 W Ellison : Helpers schedule()
01-31 19:20:08.262 16589 16589 W Ellison : EllisonsJobService destroyed.
01-31 19:20:08.264 16589 16589 W Ellison : EllisonsJobService onCreate()
01-31 19:20:08.267 16589 16589 W Ellison : EllisonsJobService onStartJob()
01-31 19:20:08.267 16589 16589 W Ellison : Helpers doHardWork()

通过log发现JobService cancel之后立马又被系统重新创建并启动了。

总结

JobService被意外终止之后如何自启动的方案有两种。


1.JobService的onStopJob()返回值设置为true。

 注意:对于自行cancel了的Job无效。

2.JobService的onStopJob()或onDestroy()里强制再次schedule我们的jobservice。

注意

JobService绑定成功后,系统的JobServiceContext都会给该JobService创建和持有WakeLock,直到JobService销毁的时候才释放WakeLock对象。


也就是说,除非JobService执行结束或意外中止,系统一直会保持运转以保证JobService的正常运行。



所以,如果JobService保持自启动的话,最好不要无限循环自启动,这样会导致系统一直无法休眠,加速电量的损耗。


我们将目前的尝试结果做下总结,总结那些JobService使用中应当知道的规则。

JobService规则

① 权限声明

manifest里JobService的声明里必须声明android:permission="android.permission.BIND_JOB_SERVICE"的权限。

不然的话,在schdule或者enqueue job的时候会发生IllegalArgumentException。


Error: requested job be persisted without holding RECEIVE_BOOT_COMPLETED permission.

② 必须设置执行条件

JobInfo创建的时候必须设置一个条件。

不然的话,在创建JobInfo对象时会抛如下的IllegalArgumentException。


You're trying to build a job with no constraints, this is not allowed.

③ 唯一ID

同一个UID的进程里只能有唯一一个Job的ID。

不然的话,新生成的Job会抢占已经运行的Job,导致该Job被异常终止。

④ 自启动

JobService因为运行条件变化后被强制停止后想自启动的话,需要将onStopJob()返回true。

⑤ 强行自启动

JobService不论何种原因被停止了都希望能自动启动的话,需要在onStopJob()或

onDestroy()里强制再次schedule我们的jobservice。

⑥ 取消Job的注意点

如果自行cancel了Job,即便onStopJob()里返回true系统也不会将该Job再度启动。

➆ 手动结束Job的注意点

如果自行finished了Job,那么onStopJob()将得不到回调,将只回调onDestroy()。

⑧ 耗时任务的注意点

Job如果要执行长时间任务的话,onStartJob()应当返回true。不然onStartJob()刚回调结束,Job就会被停止。

后续

JobService使用的大体内容就是这些,后续将补充如下内容。


● enqueue API的相关使用说明。


● JobService和Service的区别。


● 源码层面探究JobScheduler如何实现cancel后的job即便onStopJob()返回true也不能再次启动。


● 源码层面探究JobScheduler的大体原理。


相关文章
|
7月前
|
存储 Android开发
Android 高版本 packageManager.getPackageArchiveInfo 总是返回null
Android 高版本 packageManager.getPackageArchiveInfo 总是返回null
289 1
|
6月前
|
机器学习/深度学习 并行计算 安全
ImportError: DLL load failed while importing libpaddle: 找不到指定的模块问题
【6月更文挑战第7天】ImportError: DLL load failed while importing libpaddle: 找不到指定的模块问题
519 0
|
安全 API 开发工具
Android14 适配之——targetSdkVersion 升级到 34 需要注意些什么?(下)
Android14 适配之——targetSdkVersion 升级到 34 需要注意些什么?(下)
1346 0
|
7月前
|
存储 编译器 C++
从Proto到C++:探索Protocol Buffers的强大转换机制
从Proto到C++:探索Protocol Buffers的强大转换机制
757 4
|
Shell API 调度
JobService的使用介绍(1)
JobService的使用介绍(1)
JobService的使用介绍(1)
|
消息中间件 运维 Kubernetes
工作中用Go: Go中异步任务怎么写
工作中用Go: Go中异步任务怎么写
3063 0
工作中用Go: Go中异步任务怎么写
|
7月前
|
Web App开发 JSON Android开发
【Android App】实战项目之仿微信的视频通话(附源码和演示 超详细必看)
【Android App】实战项目之仿微信的视频通话(附源码和演示 超详细必看)
605 2
|
运维 Kubernetes Cloud Native
Kubernetes(K8s)常用命令大全:熟练编排更完美
Kubernetes(K8s)常用命令大全:熟练编排更完美
1485 0
|
Android开发 容器
Bug日志(一):RecyclerView只显示第一行
Bug日志(一):RecyclerView只显示第一行
|
Java Maven
Gradle、Maven使用阿里镜像源
Gradle、Maven使用阿里镜像源
37890 1