JobScheduler 详解一

xiaoxiao2021-02-28  110

前言

最近在调查一个 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 开始运转
转载请注明原文地址: https://www.6miu.com/read-30713.html

最新回复(0)