JobScheduler 详解一
来源:互联网 发布:网络诈骗一千元 编辑:程序博客网 时间:2024/04/29 02:57
前言
最近在调查一个 JobScheduler 的问题,看了不少博客和源码,为了防止撂爪就忘,将自己学习到的东西整理一下。
先给出一个自己写的 demo :
private static ComponentName sService = new ComponentName("com.example.mi.myjobtest", MyJobService.class.getName()); public static void schedule(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo job = new JobInfo.Builder(JOB_ID, sService) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)//网络条件,默认值NETWORK_TYPE_NONE .setPeriodic(DAY_TIME)//任务执行周期 .setPersisted(true)//设备重启后是否继续执行 .setRequiresCharging(true)//设置是否需要充电 .build(); js.schedule(job); }
注意:
/** * Set whether or not to persist this job across device reboots. This will only have an * effect if your application holds the permission * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED}. Otherwise an exception will * be thrown. * @param isPersisted True to indicate that the job will be written to disk and loaded at * boot. */ public Builder setPersisted(boolean isPersisted) { mIsPersisted = isPersisted; return this; }
设置此条件需要 holds the permission {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED}
一、JobScheduler 服务启动
1.1 SystemServer.startOtherServices
SystemServer.java
private void startOtherServices() { ... mSystemServiceManager.startService(JobSchedulerService.class); ...}
其会调用 JobSchedulerService.class 的构造函数
1.2 JobSchedulerService
JobSchedulerService.java
public JobSchedulerService(Context context) { super(context); 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)); // 创建主线程的looper mHandler = new JobHandler(context.getMainLooper()); // 创建binder服务端 mJobSchedulerStub = new JobSchedulerStub(); mJobs = JobStore.initAndGet(this); }
创建了5个不同的 StateController,分别添加到 mControllers 中
接下来,整篇文章将以 TimeController 为例进行讲解
1.2.1TimeController
TimeController.java
/** Singleton. */ private static TimeController mSingleton; public static synchronized TimeController get(JobSchedulerService jms) { if (mSingleton == null) { mSingleton = new TimeController(jms, jms.getContext()); } return mSingleton; } private TimeController(StateChangedListener stateChangedListener, Context context) { super(stateChangedListener, context); mDeadlineExpiredAlarmIntent = PendingIntent.getBroadcast(mContext, 0 /* ignored */, new Intent(ACTION_JOB_EXPIRED), 0); mNextDelayExpiredAlarmIntent = PendingIntent.getBroadcast(mContext, 0 /* ignored */, new Intent(ACTION_JOB_DELAY_EXPIRED), 0); mNextJobExpiredElapsedMillis = Long.MAX_VALUE; mNextDelayExpiredElapsedMillis = Long.MAX_VALUE; // Register BR for these intents. IntentFilter intentFilter = new IntentFilter(ACTION_JOB_EXPIRED); intentFilter.addAction(ACTION_JOB_DELAY_EXPIRED); mContext.registerReceiver(mAlarmExpiredReceiver, intentFilter); }
1.3 JobStore.initAndGet
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(), Environment.getDataDirectory()); } return sSingleton; } }
1.4 创建 JobStore
JobStore.java
/** * Construct the instance of the job store. This results in a blocking read from disk. */ private JobStore(Context context, File dataDir) { mContext = context; mDirtyOperations = 0; File systemDir = new File(dataDir, "system"); File jobDir = new File(systemDir, "job"); jobDir.mkdirs(); // 创建/data/system/job/jobs.xml mJobsFile = new AtomicFile(new File(jobDir, "jobs.xml")); mJobSet = new ArraySet<JobStatus>(); // 从 jobs.xml 中读取 JobMap readJobMapFromDisk(mJobSet); }
1.5 xml 解析
1.5.1 ReadJobMapFromDiskRunnable
JobStore.java
/** * Runnable that reads list of persisted job from xml. This is run once at start up, so doesn't * need to go through {@link JobStore#add(com.android.server.job.controllers.JobStatus)}. */ private class ReadJobMapFromDiskRunnable implements Runnable { private final ArraySet<JobStatus> jobSet; ReadJobMapFromDiskRunnable(ArraySet<JobStatus> jobSet) { this.jobSet = jobSet; } @Override public void run() { try { List<JobStatus> jobs; // mJobsFile 即为 /data/system/job/jobs.xml FileInputStream fis = mJobsFile.openRead(); synchronized (JobStore.this) { // 主要功能执行方法 jobs = readJobMapImpl(fis); if (jobs != null) { for (int i=0; i<jobs.size(); i++) { this.jobSet.add(jobs.get(i)); } } } fis.close(); } ... }
“reads list of persisted job from xml” 这里只会读取 setPersisted(true) 的 jobs
1.5.2 xml 示例 (以上面 demo 为例)
<?xml version='1.0' encoding='utf-8' standalone='yes' ?><job-info version="0">... <job jobid="11111111" package="com.example.mi.myjobtest" class="com.example.mi.myjobtest.MyJobService" uid="10128"> <constraints unmetered="true" charging="true" /> <periodic period="3600000" deadline="1504155742651" delay="1504152142651" /> <extras /> </job>...</job-info>
1.5.3 readJobMapImpl
JobStore.java
private List<JobStatus> readJobMapImpl(FileInputStream fis) throws XmlPullParserException, IOException { XmlPullParser parser = Xml.newPullParser(); parser.setInput(fis, StandardCharsets.UTF_8.name()); ... String tagName = parser.getName(); if ("job-info".equals(tagName)) { final List<JobStatus> jobs = new ArrayList<JobStatus>(); // Read in version info. ... eventType = parser.next(); do { // Read each <job/> if (eventType == XmlPullParser.START_TAG) { tagName = parser.getName(); // Start reading job. if ("job".equals(tagName)) { // 读取 job JobStatus persistedJob = restoreJobFromXml(parser); if (persistedJob != null) { jobs.add(persistedJob); } } } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); return jobs; } return null; }
1.5.4 restoreJobFromXml
JobStore.java
private JobStatus restoreJobFromXml(XmlPullParser parser) throws XmlPullParserException, IOException { JobInfo.Builder jobBuilder; int uid; // 解析 job identifier attributes.即 <job jobid="11111111" package="com.example.mi.myjobtest" class="com.example.mi.myjobtest.MyJobService" uid="10128"> jobBuilder = buildBuilderFromXml(parser); jobBuilder.setPersisted(true); uid = Integer.valueOf(parser.getAttributeValue(null, "uid")); int eventType; // 解析 constraints tag.即 <constraints unmetered="true" charging="true" /> buildConstraintsFromXml(jobBuilder, parser); // Tuple of (earliest runtime, latest runtime) in elapsed realtime after disk load. // 解析 deadline="1504155742651" delay="1504152142651" 为 elapsedtime 形式 elapsedRuntimes = buildExecutionTimesFromXml(parser); final long elapsedNow = SystemClock.elapsedRealtime(); if (XML_TAG_PERIODIC.equals(parser.getName())) { // 解析 XML_TAG_PERIODIC ... } else if (XML_TAG_ONEOFF.equals(parser.getName())) { // 解析 XML_TAG_ONEOFF ... } else { } maybeBuildBackoffPolicyFromXml(jobBuilder, parser); // Read out extras Bundle. ... return new JobStatus( jobBuilder.build(), uid, elapsedRuntimes.first, elapsedRuntimes.second); }
1.5.5 buildBuilderFromXml
JobStore.java
private JobInfo.Builder buildBuilderFromXml(XmlPullParser parser) throws NumberFormatException { // Pull out required fields from <job> attributes. // 即解析 <job jobid="11111111" package="com.example.mi.myjobtest" class="com.example.mi.myjobtest.MyJobService" uid="10128"> 此行 int jobId = Integer.valueOf(parser.getAttributeValue(null, "jobid")); String packageName = parser.getAttributeValue(null, "package"); String className = parser.getAttributeValue(null, "class"); ComponentName cname = new ComponentName(packageName, className); return new JobInfo.Builder(jobId, cname); }
1.5.6 创建 JobInfo
JobInfo.java
private JobInfo(JobInfo.Builder b) { jobId = b.mJobId; extras = b.mExtras; ...//形式与上面相同 } public static final class Builder { public Builder(int jobId, ComponentName jobService) { mJobService = jobService; mJobId = jobId; } public JobInfo build() { ... return new JobInfo(this); } }
1.5.7 创建 JobStatus
JobStatus.java
/** How many times this job has failed, used to compute back-off. */ private final int numFailures; private JobStatus(JobInfo job, int uId, int numFailures) { this.job = job; this.uId = uId; this.name = job.getService().flattenToShortString(); this.tag = "*job*/" + this.name; this.numFailures = numFailures; } public JobStatus(JobInfo job, int uId, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) { this(job, uId, 0); this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis; this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis; }
到这里从 jobs.xml 中解析出 JobStatus 并创建出 JobStore 就讲解完了
1.6 JobSchedulerService.onBootPhase
JobSchedulerService.java
public void onBootPhase(int phase) { if (PHASE_SYSTEM_SERVICES_READY == phase) { // 500, 注册广播 for package removals and user removals. final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); getContext().registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, filter, null, null); final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); userFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); getContext().registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE); } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { synchronized (mJobs) { // 阶段 600 mReadyToRock = true; mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( BatteryStats.SERVICE_NAME)); for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { // 创建 JobServiceContext 对象 mActiveServices.add( new JobServiceContext(this, mBatteryStats, getContext().getMainLooper())); } // Attach jobs to their controllers.开始 track jobs ArraySet<JobStatus> jobs = mJobs.getJobs(); for (int i=0; i<jobs.size(); i++) { JobStatus job = jobs.valueAt(i); for (int controller=0; controller<mControllers.size(); controller++) { mControllers.get(controller).deviceIdleModeChanged(mDeviceIdleMode); mControllers.get(controller).maybeStartTrackingJob(job); } } mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); } }}
MAX_JOB_CONTEXTS_COUNT = ActivityManager.isLowRamDeviceStatic() ? 1 : 3,对于低内存的设备,则只创建一个创建JobServiceContext对象,否则创建3个该对象
1.7 创建 JobServiceContext
JobServiceContext.java
JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats, Looper looper) { this(service.getContext(), batteryStats, service, looper); } @VisibleForTesting JobServiceContext(Context context, IBatteryStats batteryStats, JobCompletedListener completedListener, Looper looper) { mContext = context; mBatteryStats = batteryStats; mCallbackHandler = new JobServiceHandler(looper); mCompletedListener = completedListener; mAvailable = true; }
此处的 JobServiceHandler 采用的是 system_server 进程的主线程
1.8 小结
- JobSchedulerService.JobHandler 和 JobServiceContext.JobServiceHandler 都运行在 system_server 进程的主线程
- JobSchedulerService 的启动过程主要是:创建了5个不同的 StateController,分别添加到 mControllers 中;从 /data/system/job/jobs.xml 文件中读取每个 JobInfo,再解析成 JobStatus 对象,添加到 JobStore 的成员变量 mJobSet 中;注册广播,绑定 jobs 和 controllers,发送 MSG_CHECK_JOB 开始运转
- JobScheduler 详解一
- JobScheduler 详解二
- JobScheduler, Job, JobSet 详解
- 2.1 JobScheduler, Job, JobSet 详解
- android之Jobscheduler运行机制详解
- sparkstreaming-JobScheduler, Job, JobSet 详解
- JobScheduler
- JobScheduler
- Spark Streaming源码解读之JobScheduler详解
- JobScheduler学习
- JobScheduler学习
- JobScheduler学习
- JobScheduler 用法
- JobScheduler服务
- Android JobScheduler
- Android-JobScheduler
- Android JobScheduler
- JobScheduler简介
- C++编程思想之迭代器
- 洗澡
- 给RecyclerView线性布局设置分割线(只需两步)
- php 命名空间
- 新编程
- JobScheduler 详解一
- ALLEGRO学习之检查丝印以及调整丝印时要打开pin和via以及丝印的摆放规则
- 如何在visio2010的框图中插入公式?
- 【Java多线程】本地变量ThreadLocal
- idea clone git上的Maven项目并将项目与git服务器关联起来
- 树莓派3b连接GPS+BD模块并用python获取数据(USB版)
- <android> 第三方支付sdk接入 支付宝、微信支付
- [FAQ08919][NW]网络运营商名称显示规则(锁屏界面,下拉列表)---网络名称 客制化方法 和 问题处理flow
- ViewGroup源码解读