由系统统一管理和调度,在特定场景下使用JobService更加灵活和省心,相当于是Service的加强或者优化。
我们来研究下JobService如何使用。
官方链接如下:
https://developer.android.google.cn/reference/android/app/job/JobService.html
我们先看看JobService类的继承关系。
public abstract class JobService
extends Service
可以看到JobService是继承自Service的抽象类。
看来JobService的本质还是Service,只不过封装了些额外的方法和逻辑。
那到底JobService如何使用?和Service有什么区别呢?实现原理又是什么?
首先我们来看下官方的对于JobService的解释。
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
Entry point for the callback from the JobScheduler.
This is the base class that handles asynchronous requests that were previously scheduled.
You are responsible for overriding onStartJob(JobParameters), which is where you will implement your job logic.
This service executes each incoming job on a Handler running on your application's main thread.
This means that you must offload your execution logic to another thread/handler/AsyncTask
of your choosing.
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
大概就是下面的意思:
JobService是JobScheduler的回调,是安排的Job请求的实际处理类。
需要我们覆写onStartJob(JobParameters)方法,并在里面实现实际的任务逻辑。
因为JobService的执行是在APP的主线程里响应的,所以必须提供额外的异步逻辑去执行这些任务。
onStartJob()
定义:Job开始的时候的回调,实现实际的工作逻辑。上面可以看出,JobService只是实际的执行和停止任务的回调入口。
那如何将这个入口告诉系统,就需要用到JobScheduler了。
官方链接如下:
https://developer.android.google.cn/reference/android/app/job/JobScheduler.html
我们先看看JobScheduler类的继承关系。
JobScheduler
public abstract class JobScheduler
extends Object
可以看到JobScheduler就是个抽象类。
实际上它的实现逻辑在android.app.JobSchedulerImpl里。
但是我们暂时不管那么多,只要知道使用JobScheduler里面提供的API即可。
照例,看下官方的解释。
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
This is an API for scheduling various types of jobs against the framework that will be
executed in your application's own process.
See JobInfo for more description of the types of jobs that can be run and how to construct them.
You will construct these JobInfo objects and pass them to the JobScheduler with schedule(JobInfo).
When the criteria declared are met, the system will execute this job on your application's JobService.
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
描述的非常清晰,就是说。
JobScheduler是framework层里用来安排各种各样的将要执行在app自己进程里的任务的机制。
我们需要创建各种Job的描述类JobInfo。并且通过JobScheduler传递给系统。当我们描述的条件或者标准满足了,系统
将执行app的JobService。
schedule()
定义:安排一个Job任务。上面还提到需要创建JobInfo对象,实际要通过JobInfo.Builder类利用建造者模式创建出JobInfo对象。
我们选取几个代表性的API看看。
Builder()public class EllisonsJobService extends JobService {
public static final int ELLISONS_JOB_ID = 0;
public static final int ELLISONS_JOB_OVERDIDE_DEADLINE = 1000;
@Override
public void onCreate() {
super.onCreate();
Log.w(TAG, "EllisonsJobService onCreate()");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.w(TAG, "EllisonsJobService destroyed.");
}
@Override
public boolean onStartJob(JobParameters params) {
Log.w(TAG, "EllisonsJobService onStartJob()");
Helpers.doHardWork(this, params);
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
Log.w(TAG, "EllisonsJobService stopped & wait to restart params:" + params + " reason:" + params.getStopReason());
return false;
}
}
添加四个按钮:用于schedule,cancel,finish和enqueue job。
代码如下:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.w(TAG, "MainActivity onCreate() PID:" + android.os.Process.myPid() + " TID:" + android.os.Process.myTid());
setContentView(R.layout.activity_main);
}
public void onClick_Schedule(View view) {
Log.w(TAG, "MainActivity onClick_Schedule()");
Helpers.schedule(this);
}
public void onClick_Finished(View view) {
Log.w(TAG, "MainActivity onClick_Finished()");
Helpers.jobFinished();
}
public void onClick_Cancel(View view) {
Log.w(TAG, "MainActivity onClick_Cancel()");
Helpers.cancelJob(this);
}
public void onClick_Enqueue(View view) {
Log.w(TAG, "MainActivity onClick_Enqueue()");
Helpers.enqueueJob();
}
@Override
public void onDestroy() {
super.onDestroy();
Log.w(TAG, "MainActivity onDestroy()");
}
}
public class Helpers {
private static JobService mJob;
private static JobParameters mJobParams;
public static void schedule(Context context) {
Log.w(TAG, "Helpers schedule()");
final JobScheduler scheduler = context.getSystemService(JobScheduler.class);
final JobInfo.Builder builder = new JobInfo.Builder(EllisonsJobService.ELLISONS_JOB_ID,
new ComponentName(context, EllisonsJobService.class));
builder.setOverrideDeadline(EllisonsJobService.ELLISONS_JOB_OVERDIDE_DEADLINE);
scheduler.schedule(builder.build());
}
public static void cancelJob(Context context) {
Log.w(TAG, "Helpers cancelJob()");
final JobScheduler scheduler = context.getSystemService(JobScheduler.class);
scheduler.cancel(EllisonsJobService.ELLISONS_JOB_ID);
}
public static void jobFinished() {
Log.w(TAG, "Helpers jobFinished()");
mJob.jobFinished(mJobParams, false);
}
public static void enqueueJob() {
Log.w(TAG, "Helpers enqueueJob()");
}
public static void doHardWork(JobService job, JobParameters params) {
Log.w(TAG, "Helpers doHardWork()");
mJob = job;
mJobParams = params;
}
}
点击schedule的button。通过log看到JobService启动了。
01-31 17:59:23.110 13813 13813 W Ellison : MainActivity onClick_Schedule()
01-31 17:59:23.111 13813 13813 W Ellison : Helpers schedule()
01-31 17:59:23.119 13813 13813 W Ellison : EllisonsJobService onCreate()
01-31 17:59:23.121 13813 13813 W Ellison : EllisonsJobService onStartJob()
01-31 17:59:23.121 13813 13813 W Ellison : Helpers doHardWork()
点击finish的button。通过log看到JobService结束了。
01-31 17:59:35.582 13813 13813 W Ellison : MainActivity onClick_Finished()
01-31 17:59:35.582 13813 13813 W Ellison : Helpers jobFinished()
01-31 17:59:35.595 13813 13813 W Ellison : EllisonsJobService destroyed.
点击schedule和cancel的button。通过log看到JobService执行后被停止了。
01-31 17:59:48.599 13813 13813 W Ellison : MainActivity onClick_Schedule()
01-31 17:59:48.599 13813 13813 W Ellison : Helpers schedule()
01-31 17:59:48.608 13813 13813 W Ellison : EllisonsJobService onCreate()
01-31 17:59:48.611 13813 13813 W Ellison : EllisonsJobService onStartJob()
01-31 17:59:48.611 13813 13813 W Ellison : Helpers doHardWork()
01-31 17:59:55.039 13813 13813 W Ellison : MainActivity onClick_Cancel()
01-31 17:59:55.039 13813 13813 W Ellison : Helpers cancelJob()
01-31 17:59:55.040 13813 13813 W Ellison : EllisonsJobService stopped & wait to restart params:android.app.job.JobParameters@ac7bfd7 reason:0★
01-31 17:59:55.049 13813 13813 W Ellison : EllisonsJobService destroyed.
★处显示被停止的原因值为0。
/** @hide */
public static final int REASON_CANCELED = 0;
可以看到被取消的Job的reason数值确实为0。
*关于enqueue的逻辑,后续再添加。
那除了自己加log的形式,有没有别的方式查看自己的JobService了呢?
答案是有的。
使用adb shell service list命令得到如下信息。
47 jobscheduler: [android.app.job.IJobScheduler]
只能看到JobSchedulerService的信息,看不到自己的Job Service。
但是adb给我们提供了另外一种方式查看JobService。
adb shell dumpsys jobscheduler | grep packagename
我们将packagename换成我们的包名"com.example.TimeApiDemo",得到以下信息。
JOB #u0a174/0: 1beb0fa com.example.timeapidemo/.EllisonsJobService
u0a174 tag=*job*/com.example.timeapidemo/.EllisonsJobService
Source: uid=u0a174 user=0 pkg=com.example.timeapidemo
JobInfo:
Service: com.example.timeapidemo/.EllisonsJobService
Requires: charging=false batteryNotLow=false deviceIdle=false
Max execution delay: +1s0ms
Backoff: policy=1 initial=+30s0ms
Has late constraint
Required constraints: DEADLINE
Satisfied constraints: DEADLINE APP_NOT_IDLE DEVICE_NOT_DOZING
Unsatisfied constraints:
Tracking: TIME
Enqueue time: -36s216ms
Run time: earliest=none, latest=-35s216ms
Ready: false (job=true user=true !pending=true !active=false !backingup=true comp=true)
Active jobs:
...
Slot #3: 1beb0fa #u0a174/0 com.example.timeapidemo/.EllisonsJobService
Running for: +36s215ms, timeout at: +9m23s798ms
u0a174 tag=*job*/com.example.timeapidemo/.EllisonsJobService
Source: uid=u0a174 user=0 pkg=com.example.timeapidemo
Required constraints: DEADLINE
Tracking: TIME
Enqueue time: -36s216ms
Run time: earliest=none, latest=-35s216ms
Evaluated priority: 40
Active at -36s213ms, pending for +2ms
使用如下命令使得设备强制进入IDLE状态。
adb shell dumpsys battery unplug 将USB充电停止
adb shell dumpsys battery 查看电池状态确保充电状态关闭
Current Battery Service state:
(UPDATES STOPPED -- use 'reset' to restart)
AC powered: false
adb shell dumpsys deviceidle enable 将IDLE状态许可
Deep idle mode enabled
Light idle mode enable
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。
public static final int REASON_DEVICE_IDLE = 4;
可以看到进入IDLE状态后Job被停止后的stop reason数值确实为4。
这时候我们使用如下命令将设备走出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得到如下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?
修改的代码如下:
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。
public static final int REASON_CONSTRAINTS_NOT_SATISFIED = 1;
可以看到Job在条件不满足时候被停止的stop reason数值确实为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,是否也能够将其自动启动?
这个问题不需要修改代码,只需要做如下操作。点击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也没有自动启动起来。
其实很简单,我们可以在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,即便onStopJob()里返回true系统也不会将该Job再度启动。
JobService使用的大体内容就是这些,后续将补充如下内容。