Android进阶——更节电的后台任务JobScheduler 机制使用详解
Posted CrazyMo_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android进阶——更节电的后台任务JobScheduler 机制使用详解相关的知识,希望对你有一定的参考价值。
文章大纲
- 引言
- 一、JobScheduler 机制概述
- 二、JobSchedulerService 服务
- 三、JobService
- 四、JobScheduler
- 五、JobInfo & JobInfo.Builder
- 六、JobScheduler的使用步骤
引言
android 5.0系统以后,Google为了提高使用流畅度以及延长电池续航,引入了在应用后台/锁屏时,系统会回收应用并自动销毁应用拉起的Service的机制。同时为了满足在特定条件下(比如网络、充电状态、电量、时间、周期等)触发执行某些任务的需求,于是乎JobScheduler 机制应运而生。总之,对于一定预定条件而触发的任务,JobScheduler是绝佳选择。
一、JobScheduler 机制概述
JobScheduler 机制中把每个需要后台的业务抽象为一个Job,通过系统管理Job,来提高资源的利用率和减少不必要的唤醒,从而提高性能,节省电源。当系统启动时会通过system_server进程启动**JobSchedulerService
**服务,然后当使用该机制时,首先通过JobInfo
构造具体的后台任务对象,并通过Jobscheduler
传入到后台任务调度器,当满足配置的条件时系统便会在对应的JobService
上执行对应的作业。简而言之,系统提供了一种条件周期性执行的后台任务,无需开发者自己去唤醒,达到配置的条件便会自动执行。
二、JobSchedulerService 服务
The JobSchedulerService knows nothing about constraints, or the state of active jobs. It receives callbacks from the various controllers and completed jobs and operates accordingly.
从通过 (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE)的方式获取JobScheduler实例可以得知JobSchedulerService 也是以系统服务形式运行在后台,JobSchedulerService
对Job的状态和约束都不了解,完全是通过各种controller的回调去处理各种Job。
1、触发JobSchedulerService 的启动
在com.android.server.SystemServer#startOtherServices方法里
mSystemServiceManager.startService(JobSchedulerService.class);
SystemServiceManager启动所有系统核心服务的方式都大同小异,基本上都是首先根据传入的class字节码类型newInstance反射构造相关的对象,注册到系统服务列表后再触发其相应的onStart方法启动对应的服务。
com.android.server.SystemServiceManager#startService(java.lang.Class)
public <T extends SystemService> T startService(Class<T> serviceClass) try final String name = serviceClass.getName(); final T service; Constructor<T> constructor = serviceClass.getConstructor(Context.class); service = constructor.newInstance(mContext);//传入SystemServiceManager的mContext 反射构造JobSchedulerService 对象 ... // @ link ArrayList<SystemService> mServicesRegister it. mServices.add(service); // Start it. 启动JobSchedulerService service.onStart(); return service;
2、JobSchedulerService 对象的构造
public final class JobSchedulerService extends com.android.server.SystemService
implements StateChangedListener, JobCompletedListener
...
public JobSchedulerService(Context context)
super(context);
mHandler = new JobHandler(context.getMainLooper());//使用system_server进程中主线程的Looper初始化JobHandler
mConstants = new Constants(mHandler);
mJobSchedulerStub = new JobSchedulerStub();//创建对应Binder服务端
mJobs = JobStore.initAndGet(this);
// Create the controllers.
mControllers = new ArrayList<StateController>();
mControllers.add(ConnectivityController.get(this));//注册监听网络连接状态的广播
mControllers.add(TimeController.get(this));//注册监听Job时间到期的广播
mControllers.add(IdleController.get(this));//注册监听屏幕亮/灭,dream进入/退出,状态改变的广播
mBatteryController = BatteryController.get(this);//注册监听电池是否充电,电量状态的广播
mControllers.add(mBatteryController);
mStorageController = StorageController.get(this);
mControllers.add(mStorageController);
mControllers.add(AppIdleController.get(this));//监听app是否空闲
mControllers.add(ContentObserverController.get(this));//监听ContentObserver事件广播
mControllers.add(DeviceIdleJobsController.get(this));//监听设备空闲广播
@Override
public void onControllerStateChanged()
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
@Override
public void onRunJobNow(JobStatus jobStatus)
mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
@Override
public void onStart()
publishLocalService(JobSchedulerInternal.class, new LocalService());
publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
JobSchedulerService 继承自SystemService类并实现了StateChangedListener、JobCompletedListener接口,构造方法执行时主要完成四件事。
2.1、使用system_server进程的主线程Looper初始化了JobHandler
该过程运行在主线程,因此不能做耗时操作。
//com.android.server.job.JobSchedulerService.JobHandler
final private class JobHandler extends Handler
public JobHandler(Looper looper)
super(looper);
@Override
public void handleMessage(Message message)
synchronized (mLock)
if (!mReadyToRock) //phase == PHASE_THIRD_PARTY_APPS_CAN_START 时mReadyToRock为true运行运行第三方App
return;
switch (message.what)
case MSG_JOB_EXPIRED:
...
break;
case MSG_CHECK_JOB:
if (mReportedActive)
// if jobs are currently being run, queue all ready jobs for execution.
queueReadyJobsForExecutionLocked();
else
// Check the list of jobs and run some of them if we feel inclined.
maybeQueueReadyJobsForExecutionLocked();
break;
case MSG_CHECK_JOB_GREEDY:
queueReadyJobsForExecutionLocked();
break;
case MSG_STOP_JOB:
cancelJobImplLocked((JobStatus) message.obj, null,
"app no longer allowed to run");
break;
maybeRunPendingJobsLocked();
// Don't remove JOB_EXPIRED in case one came along while processing the queue.
removeMessages(MSG_CHECK_JOB);
2.2、创建了JobSchedulerService 的对应Binder服务端
/**
* Binder stub trampoline implementation
*/
final class JobSchedulerStub extends IJobScheduler.Stub
private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
// IJobScheduler implementation
@Override
public int schedule(JobInfo job) throws RemoteException
。。。
try
return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, -1, null);
finally
Binder.restoreCallingIdentity(ident);
// IJobScheduler implementation
@Override
public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException
...
long ident = Binder.clearCallingIdentity();
try
return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, -1, null);
finally
Binder.restoreCallingIdentity(ident);
@Override
public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
throws RemoteException
...
try
return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
packageName, userId, tag);
finally
Binder.restoreCallingIdentity(ident);
@Override
public List<JobInfo> getAllPendingJobs() throws RemoteException
try
return JobSchedulerService.this.getPendingJobs(uid);
finally
Binder.restoreCallingIdentity(ident);
@Override
public JobInfo getPendingJob(int jobId) throws RemoteException
final int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try
return JobSchedulerService.this.getPendingJob(uid, jobId);
finally
Binder.restoreCallingIdentity(ident);
@Override
public void cancelAll() throws RemoteException
...
try
JobSchedulerService.this.cancelJobsForUid(uid, "cancelAll() called by app");
finally
Binder.restoreCallingIdentity(ident);
@Override
public void cancel(int jobId) throws RemoteException
...
try
JobSchedulerService.this.cancelJob(uid, jobId);
finally
Binder.restoreCallingIdentity(ident);
;
2.3、创建了持久化相关的JobStore
JobStore对象构造时会在创建/data/system/job/jobs.xml
文件,同时可能之前已经存储过,还会解析XML文件创建JobInfo和,并转化为对应的JobStatus,最后把所有的JobStatus并保存到JobSet集合中,也是为什么JobScheduler可以持久化的原因。
JobStatus对象记录着任务的jobId, ComponentName, uid以及标签和失败次数信息。
private JobStore(Context context, Object lock, File dataDir)
File systemDir = new File(dataDir, "system");
File jobDir = new File(systemDir, "job");
jobDir.mkdirs();
mJobsFile = new AtomicFile(new File(jobDir, "jobs.xml"));
mJobSet = new JobSet();
readJobMapFromDisk(mJobSet);
// frameworks/base/services/core/java/com/android/server/job/JobStore.java
/** Used by the @link JobSchedulerService to instantiate the JobStore. */
static JobStore initAndGet(JobSchedulerService jobManagerService)
synchronized (sSingletonLock)
if (sSingleton == null)
sSingleton = new JobStore(jobManagerService.getContext(),
jobManagerService.getLock(), Environment.getDataDirectory());
return sSingleton;
2.4、创建和注册预置条件的监听器StateController
创建和注册预置条件的监听器,StateControler内部依然是通过广播实现的,监听到相应广播然后通知到监听者,当满足条件后,就会通过Handler 发送相应的消息触发任务执行。
StateControler类型 | 说明 |
---|---|
ConnectivityController | 注册监听网络连接状态的广播 |
TimeController | 注册监听job时间到期的广播 |
IdleController | 注册监听屏幕亮/灭,dream进入/退出,状态改变的广播 |
BatteryController | 注册监听电池是否充电,电量状态的广播 |
AppIdleController | 监听app是否空闲 |
ContentObserverController | 通过ContentObserver监测content URIs的变化 |
DeviceIdleJobsController | 根据doze状态为app设置约束。 |
public class ConnectivityController extends StateController implements
ConnectivityManager.OnNetworkActiveListener
private final ConnectivityManager mConnManager;
/** Singleton. */
private static ConnectivityController mSingleton;
private ConnectivityController(StateChangedListener stateChangedListener, Context context,
Object lock)
super(stateChangedListener, context, lock);
mConnManager = mContext.getSystemService(ConnectivityManager.class);
mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
final IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
mContext.registerReceiverAsUser(
mConnectivityReceiver, UserHandle.SYSTEM, intentFilter, null, null);
mNetPolicyManager.registerListener(mNetPolicyListener);//主动监听网络相关广播
...
/**
* Update all jobs tracked by this controller.
* @param uid only update jobs belonging to this UID, or @code -1 to update all tracked jobs.
*/
private void updateTrackedJobs(int uid)
synchronized (mLock)
boolean changed = false;
for (int i = 0; i < mTrackedJobs.size(); i++)
final JobStatus js = mTrackedJobs.get(i);
if (uid == -1 || uid == js.getSourceUid())
changed |= updateConstraintsSatisfied(js);
if (changed)
mStateChangedListener.onControllerStateChanged();
/**
* We know the network has just come up. We want to run any jobs that are ready.
*/
@Override
public synchronized void onNetworkActive()
synchronized (mLock)
for (int i = 0; i < mTrackedJobs.size(); i++)
final JobStatus js = mTrackedJobs.get(i);
if (js.isReady())
mStateChangedListener.onRunJobNow(js);
private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver()
@Override
public void onReceive(Context context, Intent intent)
updateTrackedJobs(-1);
;
Android O以后禁止了一些广播的发送后,都是由这些Controller进行动态注册广播,由这些Controller触发相应的预置回调接口,从而转交给JobScheduler进行处理
/** * 是否正在充电 */ public static boolean isCharging(Context context) //注册个包含充电状态的广播,并且是一个持续的广播 IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent intent = context.registerReceiver(null,filter); //获取充电状态 int isPlugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); boolean acPlugged = isPlugged == BatteryManager.BATTERY_PLUGGED_AC; boolean usbPlugged = isPlugged == BatteryManager.BATTERY_PLUGGED_USB; boolean wifiPlugged = isPlugged == BatteryManager.BATTERY_PLUGGED_WIRELESS; return acPlugged || usbPlugged || wifiPlugged;
3、JobSchedulerService的真正启动
与其他系统服务一样,执行发布,这样其他应用就可以直接通过Binder使用这个服务的能力了。
@Override
public void onStart()
publishLocalService(JobSchedulerInternal.class, new LocalService());
publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
最后由SystemServiceRegistry的静态代码块中完成注册工作,可以看到当客户端请求获取JOB_SCHEDULER_SERVICE服务, 返回的是继承自JobScheduler 的JobSchedulerImpl实例。
registerService(Context.JOB_SCHEDULER_SERVICE, JobScheduler.class,
new StaticServiceFetcher<JobScheduler>()
@Override
public JobScheduler createService()
IBinder b = ServiceManager.getService(Context.JOB_SCHEDULER_SERVICE);
return new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b));
);
至此JobSchedulerService 服务启动完成。
三、JobService
Entry point for the callback from the @link android.app.job.JobScheduler
抽象类JobService 继承自Service类,在JobScheduler
监测到系统状态达到对应启动条件时,会启动JobService
执行任务。所以我们需要继承JobService
创建一个继承自JobService的Service,并必须实现两个方法:onStartJob(JobParameters params)
和 onStopJob(JobParameters params)
。
public abstract class JobService extends Service
final JobHandler mHandler;
final JobSchedulerStub mJobSchedulerStub;
IJobService mBinder = new IJobService.Stub()
public void startJob(JobParameters jobParams)
ensureHandler();
//向主线程的Handler发送MSG_EXECUTE_JOB消息
Message m = Message.obtain(mHandler, MSG_EXECUTE_JOBAndroid进阶——更节电的后台任务JobScheduler 机制使用详解