Android进阶——更节电的后台任务JobScheduler 机制使用详解

本文主要是介绍Android进阶——更节电的后台任务JobScheduler 机制使用详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章大纲

  • 引言
  • 一、JobScheduler 机制概述
  • 二、JobSchedulerService 服务
    • 1、触发JobSchedulerService 的启动
    • 2、JobSchedulerService 对象的构造
      • 2.1、使用system_server进程的主线程Looper初始化了JobHandler
      • 2.2、创建了JobSchedulerService 的对应Binder服务端
      • 2.3、创建了持久化相关的JobStore
      • 2.4、创建和注册预置条件的监听器StateController
    • 3、JobSchedulerService的真正启动
  • 三、JobService
    • 1、boolean OnStartJob(JobParameters params)
    • 2、boolean OnStopJob(JobParameters params)
    • 3、void jobFinished (JobParameters params, boolean needsReschedule)
  • 四、JobScheduler
    • 1、JobSchedulerService.JobSchedulerStub#schedule
  • 五、JobInfo & JobInfo.Builder
  • 六、JobScheduler的使用步骤
    • 1、继承JobService 重写onStartJob和onStopJob方法实现定制的JobService
      • 1.1、结合Handler形式处理耗时操作
      • 1.2、结合AsyncTask 处理耗时操作
    • 2、初始化获取JobScheduler对象实例
    • 3、构建作业JobInfo对象预置触发条件绑定JobService
    • 4、通过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> mServices}Register it.mServices.add(service);// Start it. 启动JobSchedulerService service.onStart();return service;}}

2、JobSchedulerService 对象的构造

 public final class JobSchedulerService extends com.android.server.SystemServiceimplements StateChangedListener, JobCompletedListener{...public JobSchedulerService(Context context) {super(context);mHandler = new JobHandler(context.getMainLooper());//使用system_server进程中主线程的Looper初始化JobHandlermConstants = 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));//监听设备空闲广播}@Overridepublic void onControllerStateChanged() {mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();}@Overridepublic void onRunJobNow(JobStatus jobStatus) {mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();}@Overridepublic 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);}@Overridepublic void handleMessage(Message message) {synchronized (mLock) {if (!mReadyToRock) {//phase == PHASE_THIRD_PARTY_APPS_CAN_START 时mReadyToRock为true运行运行第三方Appreturn;}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@Overridepublic int schedule(JobInfo job) throws RemoteException {。。。try {return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, -1, null);} finally {Binder.restoreCallingIdentity(ident);}}// IJobScheduler implementation@Overridepublic 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);}}@Overridepublic 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);}}@Overridepublic List<JobInfo> getAllPendingJobs() throws RemoteException {try {return JobSchedulerService.this.getPendingJobs(uid);} finally {Binder.restoreCallingIdentity(ident);}}@Overridepublic 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);}}@Overridepublic void cancelAll() throws RemoteException {...try {JobSchedulerService.this.cancelJobsForUid(uid, "cancelAll() called by app");} finally {Binder.restoreCallingIdentity(ident);}}@Overridepublic 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 implementsConnectivityManager.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.*/@Overridepublic 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() {@Overridepublic 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使用这个服务的能力了。

    @Overridepublic 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>() {@Overridepublic 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_JOB, jobParams);m.sendToTarget();}public void stopJob(JobParameters jobParams) {ensureHandler();//向主线程的Handler发送MSG_STOP_JOB消息Message m = Message.obtain(mHandler, MSG_STOP_JOB, jobParams);m.sendToTarget();}};void ensureHandler() {synchronized (mHandlerLock) {if (mHandler == null) {mHandler = new JobHandler(getMainLooper());}}}public final IBinder onBind(Intent intent) {return mBinder.asBinder();}...
}

当JobService运行在App进程时,则mHandler即App进程的主线程关联的Handler。当分别向主线程发送消息启动和停止任务时,就会分别回调onStartJob和onStopJob方法。

1、boolean OnStartJob(JobParameters params)

当作业开始执行时会触发onStartJob(JobParameters params)方法(系统用来触发已经被执行的任务)并返回一个boolean值。若值为false,系统会认为在它返回时,任务已经执行完毕;而返回true,则系统任务这个任务正要被执行,因此当任务执行完毕时你需要调用jobFinished(JobParameters params, boolean needsRescheduled)来通知系统。

  • 如果返回值是false,该系统假定任何任务运行不需要很长时间并且到方法返回时已经完成。
  • **如果返回值是true,那么系统假设该任务是需要一些时间并且是需要在我们自己应用执行的。**当给定的任务完成时,需要通过调用jobFinished(JobParameters params, boolean needsRescheduled)方法来停止该任务告知系统该任务已经处理完成。

换言之,onStartJob方法在系统判定达到约束条件时被调用,我们可以在此处执行我们的业务逻辑

2、boolean OnStopJob(JobParameters params)

当系统接收到一个取消请求时,系统会触发onStopJob(JobParameters params)方法取消正在等待执行的任务也同样返回一个boolean值很重要的一点是如果onStartJob(JobParameters params)返回false,那么系统假定在接收到一个取消请求时已经没有正在运行的任务。换句话说,onStopJob(JobParameters params)在这种情况下不会被调用。当收到取消请求时,onStopJob(JobParameters params)是系统用来取消挂起的任务的。如果onStartJob(JobParameters params)返回 false,当取消请求被接收时,该系统假定没有目前运行的工作,它根本就不调用onStopJob(JobParameters params)。因此就需要我们手动调用jobFinished (JobParameters params, boolean needsReschedule)方法了。

要注意的是,onStartJob和onStopJob方法是运行在主线程中的,我们不可以在其中做耗时操作,否则可能导致ANR,可以使用另一个线程处理程序或运行时间更长的任务异步任务处理。因此通常在上面自定义的JobService类中创建一个Handler或者AsyncTask来处理需要进行的Job。

3、void jobFinished (JobParameters params, boolean needsReschedule)

回调通知已完成执行的JobManager,这可以从任何线程调用,因为它最终将在应用程序的主线程上运行。当系统收到该消息时,它将释放正在保存的唤醒。

  • params——传入的param需要和onStartJob中的param一致
  • needsReschedule——如果这项工作应按照计划时间指定的停止条件进行重新安排,则传入true。 否则的话传入false,让系统知道这个任务是否应该在最初的条件下被重复执行**

当任务完成时,需要调用jobFinished(JobParameters params, boolean needsRescheduled)让系统知道完成了哪项任务,它可以开始排队接下来的操作。如果不这样做,工作将只运行一次,应用程序将不被允许执行额外的工作

四、JobScheduler

This is an API for scheduling various types of jobs against the framework that will be executed in your application’s own process.

JobScheduler 从代码角度上来看是给我们开发者提供了一系列管理调度JobInfo的API,同时从另一个角度它还是一个Android的系统服务——JobSchedulerService相关的Binder对象。从上面得知JobScheduler 的实现类是JobSchedulerImpl,主要就是通过Binder 调用com.android.server.job.JobSchedulerService.JobSchedulerStub里的Binder远程接口方法。

方法参数说明
int schedule(JobInfo job)对应的任务信息把要执行的任务添加到调度集合中,如果传入的参数里携带的jobId已经存在则会覆盖旧ID的作业,而如果传入的jobId的作业当前正在运行,则会将其停止。如果schedule方法失败了,它会返回一个小于0的错误码。否则它会返回我们在JobInfo.Builder中定义的标识id。
void cancel(int jobId)jobId取消jobId对应的作业,如果任务当前正在执行,它会立即停止且它的Job Service#onStopjob (Job Parameters)方法的返回值将被忽略。
void cancelAll()\取消所有当前应用配置的Job,cancalAll()小心有坑,因为该方法的功能是取消该uid下的所有jobs,即当存在多个app通过shareUid的方式,那么在其中任意一个app执行cancalAll(),则会把所有同一uid下的app中的jobs都cancel掉
List< JobInfo > getAllPendingJobs()\获取所有当前应用配置的Job
JobInfo getPendingJob(int jobId)jobId获取jobId对应的Job
public class JobSchedulerImpl extends JobScheduler {IJobScheduler mBinder;/* package */ JobSchedulerImpl(IJobScheduler binder) {mBinder = binder;}@Overridepublic int schedule(JobInfo job) {try {//{@link com.android.server.job.JobSchedulerService.JobSchedulerStub#schedule}return mBinder.schedule(job);} catch (RemoteException e) {return JobScheduler.RESULT_FAILURE;}}@Overridepublic int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag) {return mBinder.scheduleAsPackage(job, packageName, userId, tag);}@Overridepublic void cancel(int jobId) {mBinder.cancel(jobId);}@Overridepublic void cancelAll() {mBinder.cancelAll();}@Overridepublic List<JobInfo> getAllPendingJobs() {return mBinder.getAllPendingJobs();}@Overridepublic JobInfo getPendingJob(int jobId) {return mBinder.getPendingJob(jobId);}
}

1、JobSchedulerService.JobSchedulerStub#schedule

JobSchedulerImpl里通过mBinder调用schedule方法,然后传递到JobSchedulerService 服务端Binder对象JobSchedulerStub中

// IJobScheduler implementation
@Override
public int schedule(JobInfo job) throws RemoteException {return JobSchedulerService.this.schedule(job, uid);
}

在JobSchedulerStub里调用JobSchedulerService.this.schedule方法,

//@link com.android.server.job.JobSchedulerService.JobSchedulerStub#schedulepublic int schedule(JobInfo job, int uId) {return scheduleAsPackage(job, uId, null, -1, null);}public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId,String tag) {//创建JobStatusJobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);JobStatus toCancel;synchronized (mLock) {//先取消该uid下的任务toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());if (toCancel != null) {cancelJobImpl(toCancel, jobStatus);}//开始追踪该任务startTrackingJob(jobStatus, toCancel);}//向system_server进程的主线程发送messagemHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();return JobScheduler.RESULT_SUCCESS;}

然后通过JobHandler处理Message消息,其他过程大同小异。

五、JobInfo & JobInfo.Builder

JobInfo是对任务主体信息的封装,比如说任务的执行条件、绑定的JobService类名、策略、重试策略、任务执行时间、是否持久化等等。通过构造者模式 JobInfo.Builder的构造JobInfo时需要传入一个jobId和绑定的JobService类名,其中jobId是Job的唯一标志,后续通过该jobId来取消Job

Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the parameters required to schedule work against the calling application.

JobInfo.Builder成员方法参数说明备注
addTriggerContentUri(JobInfo.TriggerContentUri uri)添加一个TriggerContentUri(原理是利用ContentObserver来监控一个Content Uri),当且仅当其发生变化时将触发任务的执行。为了持续监控Content的变化,需要在最近的任务触发后再调度一个新的任务(触发器URI不能与setPeriodic(long)setPersisted(boolean)组合使用。要持续监控内容更改,需要在完成JobService处理最近的更改之前,调度新的JobInfo监控相同的URI。因为设置此属性与定期或持久化Job不兼容,这样做会在调用build()时抛出IllegalArgumentException异常。)
setBackoffCriteria(long mills, int policy)mills表示第一次尝试重试的时间间隔,policy表示重试策略设置回退/重试的策略,类似网络原理中的冲突退避,当一个任务的调度失败时需要重试,所采取的策略预设的时间间隔有:DEFAULT_INITIAL_BACKOFF_MILLIS 30000MAX_BACKOFF_DELAY_MILLIS 18000000
而预设的策略有:BACKOFF_POLICY_EXPONENTIAL 二进制退避,等待间隔呈指数增长和BACKOFF_POLICY_LINEAR
setExtras(PersistableBundle extras)Job中附带的数据设置附带的额外数据,类似于Bundle的作用,是持久的,所以只允许原始类型。可以通过pendingJob.getExtras()获取
setMinimumLatency(long minLatencyMillis)设置任务的延迟执行时间(毫秒),相当于post delay。
setOverrideDeadline(long maxDeadlineMillis)设置任务最晚的延迟时间 。如果到了规定的时间时其他条件还未满足,任务也会被启动。
setPeriodic(long ms)设置任务运行的周期,即每X毫秒,运行一次
setPeriodic(long intervalMillis, long flexMillis)设置在Job周期末的一个flex长度的窗口,任务都有可能被执行。Andoid API 24及以上
setPersisted(boolean isPersisted)设置是否支持持久化,当设备重启之后系统根据值决定相应任务是否还要继续执行。
setRequiredNetworkType(int networkType)配置任务执行的网络条件NETWORK_TYPE_NONE——默认选择,不管是否有网络这个任务都会被执行
NETWORK_TYPE_ANY——需要任意一种网络才使得任务可以执行。
NETWORK_TYPE_UNMETERED——不是蜂窝网络( 比如在WIFI连接时 )时任务才会被执行
setRequiresCharging(boolean requiresCharging)设置设备在充电时这个任务才会被执行,这个也并非只是插入充电器,而且还要在电池处于健康状态的情况下才会触发,一般来说是手机电量>15%
setRequiresDeviceIdle(boolean isDeviceIdle)指定Job在空闲状态才能运行,设备处于屏幕关闭或dreaming状态(类似window的休眠动画状态)71分钟后,执行工作。
setTransientExtras(Bundle extras)设置作业可以携带的额外数据,类似Intent携带Bundle作用。Android API 26 及以上
setTriggerContentMaxDelay(long durationMs)设置从第一次检测到内容更改到Job之前允许的最大总延迟(以毫秒为单位),设置从Content变化到任务被执行,中间的最大延迟。Android API 24及以上
setTriggerContentMaxDelay(long durationMs)设置从第一次检测到内容更改到Job之前允许的最大总延迟(以毫秒为单位),设置从Content变化到任务被执行,中间的最大延迟。Android API 24及以上
setExtras(PersistableBundle extra)设置作业可以携带的额外数据,类似Intent携带Bundle作用。

六、JobScheduler的使用步骤

1、继承JobService 重写onStartJob和onStopJob方法实现定制的JobService

作业被启动后会调用onStartJob方法,而因为JobService运行在主线程,所以要使用子线程、Handler或者一个异步任务来运行耗时的操作以防止阻塞主线程。如果要执行耗时的操作,就需要创建一个线程去做;如果onStartJob执行的是不耗时的任务,就可以返回false,表示任务执行结束。如果onStartJob起了一个线程执行耗时任务,就要返回true,表示任务还在执行,需要等任务真正结束后手动调用JobFinished()方法告诉系统任务已经结束。

1.1、结合Handler形式处理耗时操作

public class JobServiceWithHandler  extends JobService {private Messenger mActivityMessenger;@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {mActivityMessenger = intent.getParcelableExtra(MESSENGER_INTENT_KEY);return START_NOT_STICKY;}private final Handler mJobHandler = new Handler( new Handler.Callback() {@Overridepublic boolean handleMessage( Message msg ) {//TODO 业务操作//...jobFinished( (JobParameters) msg.obj, false );//任务执行完毕后通知return true;}} );@Overridepublic boolean onStartJob(JobParameters params) {mJobHandler.sendMessage( Message.obtain( mJobHandler, 1, params ) );/* GOOGLE 官网demo 模拟的是通过Messenger 跨进程通信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() {@Overridepublic void run() {sendMessage(MSG_COLOR_STOP, params.getJobId());jobFinished(params, false);}}, duration);*//*      new Handler().postDelayed(new Runnable() {@Overridepublic void run() {//Do 。。。。jobFinished(params, false);}}, duration);*///onStartJob返回true的时候,意味着耗时操作花费的事件比onStartJob执行的事件更长,并且意味着必须在合适时机手动调用jobFinished方法,否则该应用中的其他Job将不会被执行return true;}@Overridepublic boolean onStopJob(JobParameters params) {mJobHandler.removeMessages( 1 );//sendMessage(MSG_COLOR_STOP, params.getJobId());// 当系统收到一个cancel job的请求时,并且这个Job仍然在执行(onStartJob返回true),系统就会调用onStopJob方法。 但不管是否调用onStopJob,系统只要收到取消请求,都会取消该Job// true 需要重试  false 不再重试 丢弃jobreturn false;}private void sendMessage(int messageID, @Nullable Object params) {if (mActivityMessenger == null) {return;}Message m = Message.obtain();m.what = messageID;m.obj = params;mActivityMessenger.send(m);} 
}

需要到AndroidManifest.xml中添加一个service节点让你的应用拥有绑定和使用这个JobService的权限。

<service android:name=".JobServiceWithHandler"android:permission="android.permission.BIND_JOB_SERVICE" />

1.2、结合AsyncTask 处理耗时操作

public class JobServiceWithAsyncTask extends JobService {private JobParameters mJobParameters;private final AsyncTask<Void, Void, Void> mTask = new AsyncTask<Void, Void, Void>() {@Overrideprotected Void doInBackground(Void... params) {// TODO 耗时操作return null;}@Overrideprotected void onPostExecute(Void result) {// TODO 耗时操作执行完毕后,告知系统jobFinished(mJobParameters, true);super.onPostExecute(result);}};@Overridepublic boolean onStartJob(JobParameters params) {// 返回true,表示该工作耗时,同时工作处理完成后需要调用jobFinished销毁mJobParameters = params;mTask.execute();return true;}@Overridepublic boolean onStopJob(JobParameters params) {return false;}
}

同样需要在清单文件中注册

<service android:name=".JobServiceWithAsyncTask"android:permission="android.permission.BIND_JOB_SERVICE" />

2、初始化获取JobScheduler对象实例

JobScheduler mJobScheduler = (JobScheduler) getSystemService( Context.JOB_SCHEDULER_SERVICE );

3、构建作业JobInfo对象预置触发条件绑定JobService

使用JobInfo.Builder来构建一个JobInfo对象并绑定定制的JobService。

// jobId :0
PersistableBundle extras = new PersistableBundle();
extras.putString("DATA","xxxx");
//创建一个job
JobInfo jobInfo = newJobInfo.Builder(0,new ComponentName(context, JobServiceWithHandler.class))//bInfo.Builder(0,new ComponentName(context, JobServiceWithAsyncTask.class))//只在充电的时候.setRequiresCharging(true)//不是蜂窝网络.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED).setExtras(extras).build();

4、通过JobScheduler对象实例调度指定作业

  //提交任务
if( mJobScheduler.schedule( jobInfo) <= 0 ) {//If something goes wrong
}

提交了之后,就静待条件满足系统自动执行了,完。

这篇关于Android进阶——更节电的后台任务JobScheduler 机制使用详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/443886

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof