Android 5.0系统以后,Google为了提高使用流畅度以及延长电池续航,引入了在应用后台/锁屏时,系统会回收应用并自动销毁应用拉起的Service的机制。同时为了满足在特定条件下(比如网络、充电状态、电量、时间、周期等)触发执行某些任务的需求,于是乎JobScheduler 机制应运而生。总之,对于一定预定条件而触发的任务,JobScheduler是绝佳选择。
JobScheduler 机制中把每个需要后台的业务抽象为一个Job,通过系统管理Job,来提高资源的利用率和减少不必要的唤醒,从而提高性能,节省电源。当系统启动时会通过system_server进程启动**JobSchedulerService
**服务,然后当使用该机制时,首先通过JobInfo
构造具体的后台任务对象,并通过Jobscheduler
传入到后台任务调度器,当满足配置的条件时系统便会在对应的JobService
上执行对应的作业。简而言之,系统提供了一种条件周期性执行的后台任务,无需开发者自己去唤醒,达到配置的条件便会自动执行。
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。
在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; } }
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接口,构造方法执行时主要完成四件事。
该过程运行在主线程,因此不能做耗时操作。
//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);
}
}
}
/**
* 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);
}
}
};
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;
}
}
创建和注册预置条件的监听器,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; }
与其他系统服务一样,执行发布,这样其他应用就可以直接通过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 服务启动完成。
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方法。
当作业开始执行时会触发onStartJob(JobParameters params)
方法(系统用来触发已经被执行的任务)并返回一个boolean值。若值为false,系统会认为在它返回时,任务已经执行完毕;而返回true,则系统任务这个任务正要被执行,因此当任务执行完毕时你需要调用jobFinished(JobParameters params, boolean needsRescheduled)
来通知系统。
jobFinished(JobParameters params, boolean needsRescheduled)
方法来停止该任务告知系统该任务已经处理完成。换言之,onStartJob方法在系统判定达到约束条件时被调用,我们可以在此处执行我们的业务逻辑。
当系统接收到一个取消请求时,系统会触发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。
回调通知已完成执行的JobManager,这可以从任何线程调用,因为它最终将在应用程序的主线程上运行。当系统收到该消息时,它将释放正在保存的唤醒。
当任务完成时,需要调用jobFinished(JobParameters params, boolean needsRescheduled)
让系统知道完成了哪项任务,它可以开始排队接下来的操作。如果不这样做,工作将只运行一次,应用程序将不被允许执行额外的工作。
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;
}
@Override
public int schedule(JobInfo job) {
try {
//{@link com.android.server.job.JobSchedulerService.JobSchedulerStub#schedule}
return mBinder.schedule(job);
} catch (RemoteException e) {
return JobScheduler.RESULT_FAILURE;
}
}
@Override
public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag) {
return mBinder.scheduleAsPackage(job, packageName, userId, tag);
}
@Override
public void cancel(int jobId) {
mBinder.cancel(jobId);
}
@Override
public void cancelAll() {
mBinder.cancelAll();
}
@Override
public List<JobInfo> getAllPendingJobs() {
return mBinder.getAllPendingJobs();
}
@Override
public JobInfo getPendingJob(int jobId) {
return mBinder.getPendingJob(jobId);
}
}
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#schedule
public 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) {
//创建JobStatus
JobStatus 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进程的主线程发送message
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
return JobScheduler.RESULT_SUCCESS;
}
然后通过JobHandler处理Message消息,其他过程大同小异。
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 30000 和MAX_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作用。 |
作业被启动后会调用onStartJob方法,而因为JobService运行在主线程,所以要使用子线程、Handler或者一个异步任务来运行耗时的操作以防止阻塞主线程。如果要执行耗时的操作,就需要创建一个线程去做;如果onStartJob执行的是不耗时的任务,就可以返回false,表示任务执行结束。如果onStartJob起了一个线程执行耗时任务,就要返回true,表示任务还在执行,需要等任务真正结束后手动调用JobFinished()方法告诉系统任务已经结束。
public class JobServiceWithHandler extends JobService {
private Messenger mActivityMessenger;
@Override
public 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() {
@Override
public boolean handleMessage( Message msg ) {
//TODO 业务操作
//...
jobFinished( (JobParameters) msg.obj, false );//任务执行完毕后通知
return true;
}
} );
@Override
public 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() {
@Override
public void run() {
sendMessage(MSG_COLOR_STOP, params.getJobId());
jobFinished(params, false);
}
}, duration);*/
/* new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//Do 。。。。
jobFinished(params, false);
}
}, duration);
*/
//onStartJob返回true的时候,意味着耗时操作花费的事件比onStartJob执行的事件更长,并且意味着必须在合适时机手动调用jobFinished方法,否则该应用中的其他Job将不会被执行
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
mJobHandler.removeMessages( 1 );
//sendMessage(MSG_COLOR_STOP, params.getJobId());
// 当系统收到一个cancel job的请求时,并且这个Job仍然在执行(onStartJob返回true),系统就会调用onStopJob方法。 但不管是否调用onStopJob,系统只要收到取消请求,都会取消该Job
// true 需要重试 false 不再重试 丢弃job
return 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" />
public class JobServiceWithAsyncTask extends JobService {
private JobParameters mJobParameters;
private final AsyncTask<Void, Void, Void> mTask = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
// TODO 耗时操作
return null;
}
@Override
protected void onPostExecute(Void result) {
// TODO 耗时操作执行完毕后,告知系统
jobFinished(mJobParameters, true);
super.onPostExecute(result);
}
};
@Override
public boolean onStartJob(JobParameters params) {
// 返回true,表示该工作耗时,同时工作处理完成后需要调用jobFinished销毁
mJobParameters = params;
mTask.execute();
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
同样需要在清单文件中注册
<service android:name=".JobServiceWithAsyncTask"
android:permission="android.permission.BIND_JOB_SERVICE" />
JobScheduler mJobScheduler = (JobScheduler) getSystemService( Context.JOB_SCHEDULER_SERVICE );
使用JobInfo.Builder
来构建一个JobInfo对象并绑定定制的JobService。
// jobId :0
PersistableBundle extras = new PersistableBundle();
extras.putString("DATA","xxxx");
//创建一个job
JobInfo jobInfo = new
JobInfo.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();
//提交任务
if( mJobScheduler.schedule( jobInfo) <= 0 ) {
//If something goes wrong
}
提交了之后,就静待条件满足系统自动执行了,完。