JobService是Android L时候官方新增的组件,适用于需要特定条件才执行后台任务的场景。
由系统统一管理和调度,在特定场景下使用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
首先我们来看下官方的对于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的主线程里响应的,所以必须提供额外的异步逻辑去执行这些任务。
JobService API
・onStartJob() Job开始时的回调,实现实际的工作逻辑。
注意,如果返回false的话,系统会自动结束本job。
・jobFinished() Job执行完毕后,由App端自己调用,以通知JobScheduler已经完成了任务。
注意,该方法调用导致的Job结束并不会回调onStopJob(),只会回调onDestroy()。
・onStopJob() Job中止的时候回调。当JobScheduler发觉该Job条件不满足的时候,或者Job被抢占的时候强制回调该方法。
注意,如果想让这种意外中止的Job重新开始,复写该函数返回true。
另外还有父类Service的基础方法,可以覆写来实现一些辅助作用。
・onCreate() Service被初始化后的回调,可以在这里设置BroadcastReceiver或者ContentObserver等处理。
・onDestroy() Service被销毁前的回调。可以在这里注销BroadcastReceiver或者ContentObserver。
上面可以看出,JobService只是Job执行和中止时机的回调入口。
那如何将这个入口告诉系统,就需要用到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。
JobScheduler API。
・schedule() 安排一个Job任务。
・enqueue() 安排一个Job任务,但是可以将一个任务排入队列。
・cancel() 取消一个执行ID的Job。
・cancelAll() 取消该app所有的注册到JobScheduler里的任务。
・getAllPendingJobs() 获取该app所有的注册到JobScheduler里未完成的任务列表。
・getPendingJob() 按照ID检索获得JobScheduler里未完成的该任务的JobInfo信息。
上面还提到需要创建JobInfo对象,实际要通过JobInfo.Builder类利用建造者模式创建出JobInfo对象。
JobInfo.Builder API
我们选取几个代表性的API看看。
・Builder() JobInfo.Builder的内部类构造函数
注意,参数之一ID必须是APP UID内唯一的,如果APP和别的APP共用了UID,那么要防止该ID和别的APP里有冲突
・setOverrideDeadline() 设置job被立即执行的最大延迟期限
注意:即便其他条件没满足此期限到了也要立即执行
・setRequiresDeviceIdle() 是否需要在IDLE状态下运行该Job
・setRequiredNetworkType() 设置需要何种网络类型条件
至此,JobService,JobScheduler以及JobInfo三大块的API我们都已经有些了解了,那我们先写个简单的JobService跑起来试试。
JobService DEMO
1. 首先创建JobService实现类
onCreate():简单打个Log
onStartJob():模拟业务逻辑,返回true
onStopJob():期待被再次执行,返回true
onDestroy():简单打个log
注意:manifest里JobService的声明里必须请求android:permission="android.permission.BIND_JOB_SERVICE"的权限
2. 在UI端安排JobInfo进任务队列
获取系统的JobSchedulerService的代理对象;
新建JobInfo.Builder对象,并传入JobService实现类的ComponentName和ID;
通过JobInfo.Builder对象设置Job执行的条件,这里设置条件为简单的1s deadline;
通过JobInfo.Builder对象创建出JobInfo对象;
通过JobSchedulerService代理对象将JobInfo发送到JobScheduler机制中去;
3. UI端模拟任务完成,通知JobService
对应的代码
● JobService端
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; }
● UI端
添加四个按钮:用于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; }
DEMO的主界面
点击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。
而据JobParameters.java里关于stop reason的如下定义可以看到,确实是因为取消导致的Job中止。
/** @hide */ public static final int REASON_CANCELED = 0;
*关于enqueue的逻辑,后续再添加。
查看JobService的运行
那除了自己加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",得到以下信息。
注册到JobScheduler里的JobService的属性记录
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
正在运行的JobService的信息
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