Android从5.0开始添加了一个任何调度服务JobSchedulerService
,APP可以通过该服务进行各种任务的调度,如定时任务,与服务器同步资源,下载特定信息等。由于JobScheduler
通过收集各个应用的调度任务,采用批处理的方式,允许多个任务同时运行,可以让设备具有更长的睡眠时间, 从而延长了电池使用。这篇文章,主要从应用与原理两个方面讲述分析JobScheduler
。
如何使用JobScheduler
这里假定我们有一个定时任务,需要每隔一段时间就同步本地与服务器的数据。通过JobScheduelr
发起任务调度,第一步是要创建一个JobService
用于处理定时同步服务器的任务请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
public class DataSyncJobService extends JobService { private static final String TAG = "DataSyncJobService";
@Override public boolean onStartJob(JobParameters jobParameters) { Log.v(TAG,"onStartJob()"); startSyncData(); return false; }
@Override public boolean onStopJob(JobParameters jobParameters) { Log.v(TAG,"onStopJob()"); showMessage(getString(R.string.stop_job_scheduler)); return false; }
private void startSyncData() { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } showMessage(getString(R.string.data_sync_complete)); } }).start(); }
private void showMessage(String message) { Intent intent = new Intent(getBaseContext(), MessageActivity.class); intent.putExtra("message" , message); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } }
|
同时需要在AndroidManifest.xml
中声明该servce
, 并且该service
必须添加一个权限android:permission="android.permission.BIND_JOB_SERVICE
, 这样当定时时间到后,系统会主动绑定该服务,从而发起任务调度:
1 2 3 4
| <service android:name=".DataSyncJobService" android:permission="android.permission.BIND_JOB_SERVICE"/>
|
建立了任务处理的service
之后,就可以通过JobScheduler
来发起任务调度的请求了, Android提供了如下几个接口供用户调度任务:
int schedule(in JobInfo job)
: 发起一个任务job
,如成功则返回1,失败则返回0;
enqueue(JobInfo job, JobWorkItem worker)
: 与schedule
类似,但允许将一个新的任务放入job
的队列;
void cancel(int jobId)
: 取消一个任务;
void cancelAll()
: 取消所有任务;
List<JobInfo> getAllPendingJobs()
:获取当前未处理的任务列表;
JobInfo getPendingJob(int jobId)
: 根据jobId
来获取对应未处理任务的信息;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public abstract class JobScheduler { public static final int RESULT_FAILURE = 0; public static final int RESULT_SUCCESS = 1;
public abstract int schedule(JobInfo var1);
public abstract int enqueue(JobInfo job, JobWorkItem worker);
public abstract void cancel(int jobId);
public abstract void cancelAll();
public abstract List<JobInfo> getAllPendingJobs();
public abstract JobInfo getPendingJob(int jobId); }
|
JobScheduler
无法实例化,只能通过getSystemService
接口来获取实例,在下面这个函数中, 首先构建一个任务用于定时同步服务器数据:
setPeriordic
: 设置定时任务的时间间隔,如果没有设置则不是一个定时任务;
setPersisted
: 持久化任务数据,下次开机时该任务会自动启动,如果设置了该选项,则需要在AndroidManifest.xml
中添加一个用户权限声明:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
,这样任务在开机启动后,立刻启动任务调度;
setRequiredNetworkType
: 是否需要设置调度所需要的网络模式,比如可以设置是否在收费或者漫游情况下发起任务调度;
1 2 3 4 5 6 7 8 9 10 11 12
| private void startDataSyncJobScheduler() { ComponentName jobService = new ComponentName(this, DataSyncJobService.class); JobInfo.Builder builder = new JobInfo.Builder(DATA_SYNC_JOB_ID, jobService); builder.setPeriodic(DATA_SYNC_INTERVAL) .setPersisted(true) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
JobScheduler js = (JobScheduler)getSystemService(JOB_SCHEDULER_SERVICE); js.schedule(builder.build()); }
|
这样系统就会定时向DataSyncJobService
发起任务请求,这里需要注意的是,当Android系统进入低电量的睡眠模式时,JobScheuduler
是不允许执行的,详细可以参考休眠与待机 。
JobScheduler的原理
JobSchedulerService初始化
JobSchedulerService
属于系统服务,因此在手机启动时,SystemServer
进程会主动加载该服务。创建时,JobSchedulerService
会依次初始化:
JobHandler
: 用于处理请求的Handler
;
JobSchedulerStub
:服务端stub类,用于处理客户端请求;
JobStore
: 持久化客户端请求数据
StateController
: 监听系统状态,以此触发任务调度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public JobSchedulerService(Context context) { super(context); mHandler = new JobHandler(context.getMainLooper()); mConstants = new Constants(mHandler); mJobSchedulerStub = new JobSchedulerStub(); mJobs = JobStore.initAndGet(this);
mControllers = new ArrayList<StateController>(); mControllers.add(ConnectivityController.get(this)); mControllers.add(TimeController.get(this)); mControllers.add(IdleController.get(this)); mControllers.add(BatteryController.get(this)); mControllers.add(AppIdleController.get(this)); mControllers.add(ContentObserverController.get(this)); mControllers.add(DeviceIdleJobsController.get(this)); }
|
系统启动阶段,JobSchedulerService
需要注册应用程序安装包监听器(监听APP是否被卸载、重启),同时需要将持久化的任务数据从存储中加载,重新启动这些任务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| @Override public void onBootPhase(int phase) { if (PHASE_SYSTEM_SERVICES_READY == phase) { mConstants.start(getContext().getContentResolver()); final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); filter.addDataScheme("package"); getContext().registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, filter, null, null); final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); getContext().registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE); try { ActivityManagerNative.getDefault().registerUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE); } catch (RemoteException e) { } } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { synchronized (mLock) { mReadyToRock = true; mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( BatteryStats.SERVICE_NAME)); mLocalDeviceIdleController = LocalServices.getService(DeviceIdleController.LocalService.class); for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { mActiveServices.add( new JobServiceContext(this, mBatteryStats, mJobPackageTracker, getContext().getMainLooper())); } mJobs.forEachJob(new JobStatusFunctor() { @Override public void process(JobStatus job) { for (int controller = 0; controller < mControllers.size(); controller++) { final StateController sc = mControllers.get(controller); sc.maybeStartTrackingJobLocked(job, null); } } }); mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); } } }
|
JobSchedulerService
中共启动了16个JobServiceContext
服务负责处理任务请求。接下来,就来看下JobSchedulerService
是如何进行工作调度的。
工作调度
目前JobSchedulerService
支持的任务调度主要有如下几个属性:
- 周期性:给定时间间隔触发一次任务调度;
- 设备充电:是否需要设备当前正在充电;
- 设备空闲:要求任务调度时设备处于空闲状态;
- 数据库内容更改: 监听数据库内容状态,如有改变则触发任务调度;
- 网络连接: 指定网络链接时触发任务调度
通过指定任务属性,我们可以发起一个定时任务,也可以指定某个网络条件下触发任务调度:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public class JobInfo implements Parcelable { .... public static final class Builder { private final int mJobId; private final ComponentName mJobService; private PersistableBundle mExtras = PersistableBundle.EMPTY; private int mPriority = PRIORITY_DEFAULT; private int mFlags; private boolean mRequiresCharging; private boolean mRequiresDeviceIdle; private int mNetworkType; private ArrayList<TriggerContentUri> mTriggerContentUris; private long mTriggerContentUpdateDelay = -1; private long mTriggerContentMaxDelay = -1; private boolean mIsPersisted; private long mMinLatencyMillis; private long mMaxExecutionDelayMillis; private boolean mIsPeriodic; private boolean mHasEarlyConstraint; private boolean mHasLateConstraint; private long mIntervalMillis; private long mFlexMillis; private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS; private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY; private boolean mBackoffPolicySet = false;
.... } }
|
**参考文献 **