在一篇关于网络优化的博文中发现了这个词。我们的项目中并没有使用到这个东西,但看了一下还是非常好用的,mark一下以后一定会用到吧。
贴官方文档,喜欢自学的可以过去看看
贴官方Demo,官方给出的例子也是非常好的,里面用到了Messager,以后会对它进行介绍。
这个类有什么用呢? 我们想象一种情景:
当且仅当设备在空闲状态, 并且使用wifi时, 自动下载新的Apk。
也许我们可能会这么做: 注册一个广播接受者 , 监听屏幕熄灭状态 , 熄灭之后检查网络状态 , 然后再在广播接受者中启动一个服务去下载新的apk。可以是可以,但是这个会不会过于复杂了。而且如果出现需求变更......
比如希望在特定情况下再启动事务,比如说延迟若干时间之后,或者等手机空闲了再运行,这样一方面不会在系统资源紧张之时喧宾夺主,另一方面也起到削峰填谷提高系统效率的作用。对于这些额外的条件要求,Service并不能直接支持,往往需要加入其他手段,才能较好地满足相关的运行条件
Android从5.0开始,增加支持一种特殊的机制,即任务调度JobScheduler,该工具集成了常见的几种运行条件,开发者只需添加少数几行代码,即可完成原来要多种组件配合的工作,使代码变得更加优(牛)雅(x)。
onStartJob:在任务开始执行时触发。返回false表示执行完毕,返回true表示需要开发者自己调用jobFinished方法通知系统已执行完成。
onStopJob,在任务停止执行时触发。
- JobInfo.Builder builder = new JobInfo.Builder(mJobId++, mServiceComponent);
-
- String delay = mDelayEditText.getText().toString();
- if (!TextUtils.isEmpty(delay)) {
- //设置至少延迟多久后执行,单位毫秒.
- builder.setMinimumLatency(Long.valueOf(delay) * 1000);
- }
- String deadline = mDeadlineEditText.getText().toString();
- if (!TextUtils.isEmpty(deadline)) {
- //设置最多延迟多久后执行,单位毫秒。
- builder.setOverrideDeadline(Long.valueOf(deadline) * 1000);
- }
- boolean requiresUnmetered = mWiFiConnectivityRadioButton.isChecked();
- boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isChecked();
- if (requiresUnmetered) {
- //设置需要的网络条件,有三个取值:
- //JobInfo.NETWORK_TYPE_NONE(无网络时执行,默认)、
- //JobInfo.NETWORK_TYPE_ANY(有网络时执行)、
- //JobInfo.NETWORK_TYPE_UNMETERED(网络无需付费时执行)
- builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
- } else if (requiresAnyConnectivity) {
- builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
- }
- //是否在空闲时执行
- builder.setRequiresDeviceIdle(mRequiresIdleCheckbox.isChecked());
-
- //是否在充电时执行
- builder.setRequiresCharging(mRequiresChargingCheckBox.isChecked());
-
- // Extras, work duration.
- PersistableBundle extras = new PersistableBundle();
- String workDuration = mDurationTimeEditText.getText().toString();
- if (TextUtils.isEmpty(workDuration)) {
- workDuration = "1";
- }
- extras.putLong(WORK_DURATION_KEY, Long.valueOf(workDuration) * 1000);
-
- builder.setExtras(extras);
-
- // Schedule job
- Log.d(TAG, "Scheduling job");
- JobScheduler tm = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
- tm.schedule(builder.build());
-
这里吧把官方demo中的JobService拿出来说一下吧,其大致流程就是我们使用JobScheduler的方法。它本质上是一个Service
- public class MyJobService extends JobService {
-
- private static final String TAG = MyJobService.class.getSimpleName();
-
- private Messenger mActivityMessenger;
-
- @Override
- public void onCreate() {
- super.onCreate();
- Log.i(TAG, "Service created");
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- Log.i(TAG, "Service destroyed");
- }
-
- /**
- * When the app's MainActivity is created, it starts this service. This is so that the
- * activity and this service can communicate back and forth. See "setUiCallback()"
- */
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- mActivityMessenger = intent.getParcelableExtra(MESSENGER_INTENT_KEY);
- return START_NOT_STICKY;
- }
-
- @Override
- public boolean onStartJob(final JobParameters params) {
- // The work that this service "does" is simply wait for a certain duration and finish
- // the job (on another thread).
-
- sendMessage(MSG_COLOR_START, params.getJobId());
-
- long duration = params.getExtras().getLong(WORK_DURATION_KEY);
-
- // Uses a handler to delay the execution of jobFinished().
- Handler handler = new Handler();
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- sendMessage(MSG_COLOR_STOP, params.getJobId());
- jobFinished(params, false);
- }
- }, duration);
- Log.i(TAG, "on start job: " + params.getJobId());
-
- // Return true as there's more work to be done with this job.
- return true;
- }
-
- @Override
- public boolean onStopJob(JobParameters params) {
- // Stop tracking these job parameters, as we've 'finished' executing.
- sendMessage(MSG_COLOR_STOP, params.getJobId());
- Log.i(TAG, "on stop job: " + params.getJobId());
-
- // Return false to drop the job.
- return false;
- }
-
- private void sendMessage(int messageID, @Nullable Object params) {
- // If this service is launched by the JobScheduler, there's no callback Messenger. It
- // only exists when the MainActivity calls startService() with the callback in the Intent.
- if (mActivityMessenger == null) {
- Log.d(TAG, "Service is bound, not started. There's no callback to send a message to.");
- return;
- }
- Message m = Message.obtain();
- m.what = messageID;
- m.obj = params;
- try {
- mActivityMessenger.send(m);
- } catch (RemoteException e) {
- Log.e(TAG, "Error passing service object back to activity.");
- }
- }
- }
-
首先看必须重写的两个方法:
onStartJob和onStopJob,
看来这里发送消息应该是重头戏了。
首先回到Activity中,我们在onStart方法中找到了这样的代码
- Intent startServiceIntent = new Intent(this, MyJobService.class);
- Messenger messengerIncoming = new Messenger(mHandler);
- startServiceIntent.putExtra(MESSENGER_INTENT_KEY, messengerIncoming);
- startService(startServiceIntent);
-
在Messager创建的时候就将一个handler作为参数来构造,之后作为extra来启动了服务。所以我们就能在服务中心的onStartCommand方法中获取到Messager的实例。接下来在服务的sendMessage方法中,也是直接用到了获取到的messager来发送消息,那肯定是发送到MainActivity传入的Handler中了,这里面的代码就不赘述了,都懂得。
JobScheduler的创建, 是使用的getSystemService , 我们首先点开JobScheduler类 , 尼玛居然是一个抽象类? 喵喵喵?怎么办呢? 可以去翻一下getSystemService 方法 , emm你会很惊喜的
既然是获取系统服, 按照规范, 其命名方式一定是xxxService , 所以我们直接去源码包下面搜JobScheduler, 看看会不会有什么发现
通过这种方式 , 我们可以找到获取到的真正的服务: JobSchedulerService
但是问题来了, 我们的App是一个进程 , 系统服务是在系统的进程, 两者之间怎么进行通讯呢? 对了 , 进程间通讯 , 一定是用到了AIDL
继续在JobSchedulerService中找 , 在第762行会发现
- final class JobSchedulerStub extends IJobScheduler.Stub
-
那么根据AIDL的规范 , 一定有 IJobScheduler对应的.aidl文件
打开这个文件 IJobScheduler.aidl :
- interface IJobScheduler {
- int schedule(in JobInfo job);
- void cancel(int jobId);
- void cancelAll();
- List<JobInfo> getAllPendingJobs();
- }
-
定义了aidl之后会自动生成相应的接口类
既然这个接口的存根(Stub)被继承 , 自然会重写接口中的方法 ,这些重写方法可以在JobSchedulerService$JobSchedulerStub中找到. 所以 , 我们在使用JobScheduler的一些列方法都应该是在这里了吧 , 但是JobScheduler和JobScheduler和JobSchedulerService是怎么关联在一起的呢?
因为JobScheduler是一个抽象类 , 必然有实现类 , 按照规范 , 明明应该为xxxImpl. 我们在之前的搜索中已经看到过它了 , 接下来点开看看详情
- public class JobSchedulerImpl extends JobScheduler
-
恩 , 没毛病就是它了 , 实现类当然会实现抽象方法中的抽象方法了
在看构造方法 :
- IJobScheduler mBinder;
-
- /* package */ JobSchedulerImpl(IJobScheduler binder) {
- mBinder = binder;
- }
-
这里甚至直接命名为binder , 和我们绑定服务的套路一模一样:
bindService中 , 重写onServiceConnected , 将传入的iBinder对象调用asInterface方法 ,就能获得aidl对应的接口实现 , 也就可以调用对应接口实现的方法.
再回到内部类JobSchedulerStub 中: 首先我们来看Scheduler方法
- @Override
- public int schedule(JobInfo job) throws RemoteException {
- if (DEBUG) {
- Slog.d(TAG, "Scheduling job: " + job.toString());
- }
- final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
-
- enforceValidJobRequest(uid, job);
- if (job.isPersisted()) {
- if (!canPersistJobs(pid, uid)) {
- throw new IllegalArgumentException("Error: requested job be persisted without"
- + " holding RECEIVE_BOOT_COMPLETED permission.");
- }
- }
-
- long ident = Binder.clearCallingIdentity();
- try {
- return JobSchedulerService.this.schedule(job, uid);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
在针对pid和uid进行一系列的验证之后, 最终还是调用了外部类的scheduler方法:
- /**
- * Entry point from client to schedule the provided job.
- * This cancels the job if it's already been scheduled, and replaces it with the one provided.
- * @param job JobInfo object containing execution parameters
- * @param uId The package identifier of the application this job is for.
- * @return Result of this operation. See <code>JobScheduler#RESULT_*</code> return codes.
- */
- public int schedule(JobInfo job, int uId) {
- JobStatus jobStatus = new JobStatus(job, uId);
- cancelJob(uId, job.getId());
- startTrackingJob(jobStatus);
- mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
- return JobScheduler.RESULT_SUCCESS;
- }
-
大家看下注释中的解释 , 大致了解一下流程
这里重点说一下startTrackingJob方法
- private void startTrackingJob(JobStatus jobStatus) {
- boolean update;
- boolean rocking;
- synchronized (mJobs) {
- update = mJobs.add(jobStatus);
- rocking = mReadyToRock;
- }
- if (rocking) {
- for (int i=0; i<mControllers.size(); i++) {
- StateController controller = mControllers.get(i);
- if (update) {
- controller.maybeStopTrackingJob(jobStatus);
- }
- controller.maybeStartTrackingJob(jobStatus);
- }
- }
- }
-
首先将jobStatus添加到JobStore中 , 根据是否添加成功 , 来决定是否执行
controller.maybeStopTrackingJob(jobStatus); 这里也循环遍历了控制器 . 乍一看是不是懵逼了 , 什么狗东西?
这里要提一下系统服务的创建过程了
Zygote--Linux的核心
启动系统进程SystemServer时 , 就会创建一些关键的服务,比如AMS,PMS,WMS等,其中包括JobSchedulerService。我们来看一下SystemServer这个类,一共一千多行不是很难。
首先这是一个java程序,自然要先去找main函数,然后一步一步跟。
在run方法中,我们可以看到一大堆的设置,往后直接找服务相关的(267行),三种服务:
- startBootstrapServices();
- startCoreServices();
- startOtherServices();
-
我们在startOtherServices中可以发现,众多的服务都是通过SystemServiceManager去开启,它通过类名来反射构造器来实例化相应的Service,然后添加到系统服务集合中,最后启动服务。
我们在开发中使用getSystemService的方式得到的服务,就是从刚刚提到了系统服务集合中获取的
既然是从构造器来实例化服务的,所以我们再回到JobSchedulerService看他的构造函数,发现在构造函数中,构造器的集合添加了一堆构造器,然后hander分发事件,读取本地的文件(data/system/job/jobs.xml)并执行任务。
最终在maybeRunPendingJobsH()方法中,调用了executeRunnableJob方法,这个方法在JobServiceContext中。该方法中有onServiceConnected方法,即建立链接。同时也有很多很熟悉的方法,就不一一列举了。
在服务建立链接的同时,还进行防止睡眠等wakeLock操作,emmm,坏坏的。接下来使用Handler发送了消息,what值为MSG_SERVICE_BOUND, 跟进去方法, 最后我们还能看到service.startJob的操作, 即命令服务执行一个任务。