JSS 第 2 篇

来源:互联网 发布:python中的sys.argv 编辑:程序博客网 时间:2024/06/06 12:21

前言

服务端和客户端是通过 Binder 机制通信的,我们一般是通过如下方式来注册和执行一个任务:
  1. public static int schedulePreOdexJob(int jobId, Context context) {
  2. JobScheduler jobScheduler= (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
  3. JobInfo.Builder builder = new JobInfo.Builder(jobId, new ComponentName(context.getPackageName(), OdexJobService.class.getName()));
  4. builder.setOppoJob(true);
  5. builder.setRequiresProtectFore(true);
  6. builder.setHasCpuConstraint(true);
  7. builder.setOverrideDeadline(2*60*60*1000);
  8. JobInfo jobInfo = builder.build();
  9. return jobScheduler.schedule(jobInfo);
  10. }
接下来,我们就继续来分析服务端是如何实现的!

JobSchedulerService 服务的启动,是在 SystemServer 的 startOtherServices 方法中:
  1. private void startOtherServices() {
  2. ...
  3. mSystemServiceManager.startService(JobSchedulerService.class);
  4. ...
  5. }
这个方法首先会调用服务的 Constructer,然后调用服务的 onStart 方法!
下面我们一个一个看:

1 JobSchedulerService

我们先来看 JobSchedulerService 的构造器:
  1. public JobSchedulerService(Context context) {
  2. super(context);
  3. // 创建主线程的 looper,创建对应的 Handler
  4. mHandler = new JobHandler(context.getMainLooper());
  5. mConstants = new Constants(mHandler);
  6. // 创建 binder 服务端
  7. mJobSchedulerStub = new JobSchedulerStub();
  8. // 初始化 JobStore 对象!
  9. mJobs = JobStore.initAndGet(this);
  10. // Create the controllers.
  11. mControllers = new ArrayList<StateController>();
  12. mControllers.add(ConnectivityController.get(this));
  13. mControllers.add(TimeController.get(this));
  14. mControllers.add(IdleController.get(this));
  15. mControllers.add(BatteryController.get(this));
  16. mControllers.add(AppIdleController.get(this));
  17. mControllers.add(ContentObserverController.get(this));
  18. mControllers.add(DeviceIdleJobsController.get(this));
  19. }
这里构造器其中,创建了一些很重要的成员变量,其中有一个控制器:
  • ConnectivityController:注册监听网络连接状态的广播;
  • DeviceIdleJobsController:
  • ContentObserverController:
  • AppIdleController:
  • BatteryController:注册监听电池是否充电,电量状态的广播;
  • IdleController:注册监听屏幕亮 / 灭,dream 进入 / 退出,状态改变的广播;
  • TimeController:注册监听 job 时间到期的广播;
下图展示了 这几个主要的 Controller 之间的关系:
可以看到,他们都继承了 StateController 类,我们后面来分析控制器!

1.1 JobStore

JobStore 方法的作用是存储了管理系统中存储的所有注册过的 JobStore:
  1. public class JobStore {
  2. private static final String TAG = "JobStore";
  3. private static final boolean DEBUG = JobSchedulerService.DEBUG;
  4. /** Threshold to adjust how often we want to write to the db. */
  5. private static final int MAX_OPS_BEFORE_WRITE = 1;
  6. final Object mLock;
  7. final JobSet mJobSet; // per-caller-uid tracking // 存储 jobs.xml 中的数据
  8. final Context mContext;
  9. private int mDirtyOperations;
  10. private static final Object sSingletonLock = new Object();
  11. private final AtomicFile mJobsFile; // 只向本地文件: /system/job/jobs.xml
  12. /** Handler backed by IoThread for writing to disk. */
  13. private final Handler mIoHandler = IoThread.getHandler();
  14. private static JobStore sSingleton;
  15. /** Used by the {@link JobSchedulerService} to instantiate the JobStore. */
  16. static JobStore initAndGet(JobSchedulerService jobManagerService) {
  17. synchronized (sSingletonLock) {
  18. if (sSingleton == null) {
  19. sSingleton = new JobStore(jobManagerService.getContext(),
  20. jobManagerService.getLock(), Environment.getDataDirectory());
  21. }
  22. return sSingleton;
  23. }
  24. }
  25. /**
  26. * Construct the instance of the job store. This results in a blocking read from disk.
  27. */
  28. private JobStore(Context context, Object lock, File dataDir) {
  29. mLock = lock;
  30. mContext = context;
  31. mDirtyOperations = 0;
  32. File systemDir = new File(dataDir, "system");
  33. File jobDir = new File(systemDir, "job");
  34. jobDir.mkdirs();
  35. mJobsFile = new AtomicFile(new File(jobDir, "jobs.xml"));
  36. mJobSet = new JobSet();
  37. readJobMapFromDisk(mJobSet); // 从 Jobs.xml 中读取数据,数四化 JobSet!
  38. }
上面是 JobStore 的初始化操作,该方法会创建job目录以及jobs.xml文件, 以及从文件中读取所有的JobStatus。

1.1.1 readJobMapFromDisk

这个方法创建了一个 Runnable 去实现具体的业务!
  1. @VisibleForTesting
  2. public void readJobMapFromDisk(JobSet jobSet) {
  3. new ReadJobMapFromDiskRunnable(jobSet).run();
  4. }
从 Jobs.xml 文件中读取数据:
  1. private class ReadJobMapFromDiskRunnable implements Runnable {
  2. private final JobSet jobSet;
  3. /**
  4. * @param jobSet Reference to the (empty) set of JobStatus objects that back the JobStore,
  5. * so that after disk read we can populate it directly.
  6. */
  7. ReadJobMapFromDiskRunnable(JobSet jobSet) {
  8. this.jobSet = jobSet;
  9. }
  10. @Override
  11. public void run() {
  12. try {
  13. List<JobStatus> jobs;
  14. FileInputStream fis = mJobsFile.openRead();
  15. synchronized (mLock) {
  16. jobs = readJobMapImpl(fis);
  17. if (jobs != null) {
  18. for (int i=0; i<jobs.size(); i++) {
  19. this.jobSet.add(jobs.get(i));
  20. }
  21. }
  22. }
  23. fis.close();
  24. } catch (FileNotFoundException e) {
  25. if (JobSchedulerService.DEBUG) {
  26. Slog.d(TAG, "Could not find jobs file, probably there was nothing to load.");
  27. }
  28. } catch (XmlPullParserException e) {
  29. if (JobSchedulerService.DEBUG) {
  30. Slog.d(TAG, "Error parsing xml.", e);
  31. }
  32. } catch (IOException e) {
  33. if (JobSchedulerService.DEBUG) {
  34. Slog.d(TAG, "Error parsing xml.", e);
  35. }
  36. }
  37. }
  38. ... ... ... ...
  39. }
可以看出,调用了 readJobMapImpl 方法来解析 Jobs.xml !

1.1.2 readJobMapImpl

  1. private List<JobStatus> readJobMapImpl(FileInputStream fis)
  2. throws XmlPullParserException, IOException {
  3. ... ... ... ...
  4. String tagName = parser.getName();
  5. if ("job-info".equals(tagName)) { // 解析 <job-info> 标签,校验版本是否有问题
  6. final List<JobStatus> jobs = new ArrayList<JobStatus>();
  7. // Read in version info.
  8. try {
  9. int version = Integer.parseInt(parser.getAttributeValue(null, "version"));
  10. if (version != JOBS_FILE_VERSION) {
  11. Slog.d(TAG, "Invalid version number, aborting jobs file read.");
  12. return null;
  13. }
  14. } catch (NumberFormatException e) {
  15. Slog.e(TAG, "Invalid version number, aborting jobs file read.");
  16. return null;
  17. }
  18. eventType = parser.next();
  19. do {
  20. // Read each <job/>
  21. if (eventType == XmlPullParser.START_TAG) {
  22. tagName = parser.getName();
  23. // Start reading job.
  24. if ("job".equals(tagName)) {
  25. // 解析<job> 标签,获得每个job 对应的 jobStatus 对象
  26. JobStatus persistedJob = restoreJobFromXml(parser);
  27. if (persistedJob != null) {
  28. if (DEBUG) {
  29. Slog.d(TAG, "Read out " + persistedJob);
  30. }
  31. jobs.add(persistedJob); // 加入到缓存集合中,最后返回!
  32. } else {
  33. Slog.d(TAG, "Error reading job from file.");
  34. }
  35. }
  36. }
  37. eventType = parser.next();
  38. } while (eventType != XmlPullParser.END_DOCUMENT);
  39. return jobs;
  40. }
  41. return null;
  42. }
这里是通过循环处理 jobs.xml 文件中的标签

1.1.3 restoreJobFromXml

这个方法负责解析每个<job>标签,返回每个任务对应的 JobStatus。
注意这里只有重启设备后需要保留的 Job,才会写入 jobs.xml 文件中,即:这个 Job 是 isPersisted 的!!
  1. private JobStatus restoreJobFromXml(XmlPullParser parser) throws XmlPullParserException,
  2. IOException {
  3. JobInfo.Builder jobBuilder;
  4. int uid, sourceUserId;
  5. // Read out job identifier attributes and priority.
  6. try {
  7. jobBuilder = buildBuilderFromXml(parser);
  8. jobBuilder.setPersisted(true);
  9. // 解析基本信息: uid、priority、flags 和 sourceUserId
  10. uid = Integer.parseInt(parser.getAttributeValue(null, "uid"));
  11. String val = parser.getAttributeValue(null, "priority");
  12. if (val != null) {
  13. jobBuilder.setPriority(Integer.parseInt(val));
  14. }
  15. val = parser.getAttributeValue(null, "flags");
  16. if (val != null) {
  17. jobBuilder.setFlags(Integer.parseInt(val));
  18. }
  19. val = parser.getAttributeValue(null, "sourceUserId");
  20. sourceUserId = val == null ? -1 : Integer.parseInt(val);
  21. } catch (NumberFormatException e) {
  22. Slog.e(TAG, "Error parsing job's required fields, skipping");
  23. return null;
  24. }
  25. // 解析获得 sourcePackageName 和 sourceTag!
  26. String sourcePackageName = parser.getAttributeValue(null, "sourcePackageName");
  27. final String sourceTag = parser.getAttributeValue(null, "sourceTag");
  28. int eventType;
  29. // Read out constraints tag.
  30. do {
  31. eventType = parser.next();
  32. } while (eventType == XmlPullParser.TEXT); // Push through to next START_TAG.
  33. if (!(eventType == XmlPullParser.START_TAG &&
  34. XML_TAG_PARAMS_CONSTRAINTS.equals(parser.getName()))) {
  35. // Expecting a <constraints> start tag.
  36. return null;
  37. }
  38. try {
  39. buildConstraintsFromXml(jobBuilder, parser);
  40. } catch (NumberFormatException e) {
  41. Slog.d(TAG, "Error reading constraints, skipping.");
  42. return null;
  43. }
  44. parser.next(); // Consume </constraints>
  45. // Read out execution parameters tag.
  46. do {
  47. eventType = parser.next();
  48. } while (eventType == XmlPullParser.TEXT);
  49. if (eventType != XmlPullParser.START_TAG) {
  50. return null;
  51. }
  52. // Tuple of (earliest runtime, latest runtime) in elapsed realtime after disk load.
  53. Pair<Long, Long> elapsedRuntimes;
  54. try {
  55. elapsedRuntimes = buildExecutionTimesFromXml(parser);
  56. } catch (NumberFormatException e) {
  57. if (DEBUG) {
  58. Slog.d(TAG, "Error parsing execution time parameters, skipping.");
  59. }
  60. return null;
  61. }
  62. final long elapsedNow = SystemClock.elapsedRealtime();
  63. if (XML_TAG_PERIODIC.equals(parser.getName())) {
  64. try {
  65. String val = parser.getAttributeValue(null, "period");
  66. final long periodMillis = Long.valueOf(val);
  67. val = parser.getAttributeValue(null, "flex");
  68. final long flexMillis = (val != null) ? Long.valueOf(val) : periodMillis;
  69. jobBuilder.setPeriodic(periodMillis, flexMillis);
  70. // As a sanity check, cap the recreated run time to be no later than flex+period
  71. // from now. This is the latest the periodic could be pushed out. This could
  72. // happen if the periodic ran early (at flex time before period), and then the
  73. // device rebooted.
  74. if (elapsedRuntimes.second > elapsedNow + periodMillis + flexMillis) {
  75. final long clampedLateRuntimeElapsed = elapsedNow + flexMillis
  76. + periodMillis;
  77. final long clampedEarlyRuntimeElapsed = clampedLateRuntimeElapsed
  78. - flexMillis;
  79.  
  80. ... ... ... ...
  81. elapsedRuntimes =
  82. Pair.create(clampedEarlyRuntimeElapsed, clampedLateRuntimeElapsed);
  83. }
  84. } catch (NumberFormatException e) {
  85. Slog.d(TAG, "Error reading periodic execution criteria, skipping.");
  86. return null;
  87. }
  88. } else if (XML_TAG_ONEOFF.equals(parser.getName())) {
  89. try {
  90. if (elapsedRuntimes.first != JobStatus.NO_EARLIEST_RUNTIME) {
  91. jobBuilder.setMinimumLatency(elapsedRuntimes.first - elapsedNow);
  92. }
  93. if (elapsedRuntimes.second != JobStatus.NO_LATEST_RUNTIME) {
  94. jobBuilder.setOverrideDeadline(
  95. elapsedRuntimes.second - elapsedNow);
  96. }
  97. } catch (NumberFormatException e) {
  98. Slog.d(TAG, "Error reading job execution criteria, skipping.");
  99. return null;
  100. }
  101. } else {
  102. if (DEBUG) {
  103. Slog.d(TAG, "Invalid parameter tag, skipping - " + parser.getName());
  104. }
  105. // Expecting a parameters start tag.
  106. return null;
  107. }
  108. maybeBuildBackoffPolicyFromXml(jobBuilder, parser);
  109. parser.nextTag(); // Consume parameters end tag.
  110. // Read out extras Bundle.
  111. do {
  112. eventType = parser.next();
  113. } while (eventType == XmlPullParser.TEXT);
  114. if (!(eventType == XmlPullParser.START_TAG
  115. && XML_TAG_EXTRAS.equals(parser.getName()))) {
  116. if (DEBUG) {
  117. Slog.d(TAG, "Error reading extras, skipping.");
  118. }
  119. return null;
  120. }
  121. PersistableBundle extras = PersistableBundle.restoreFromXml(parser);
  122. jobBuilder.setExtras(extras);
  123. parser.nextTag(); // Consume </extras>
  124. // Migrate sync jobs forward from earlier, incomplete representation
  125. if ("android".equals(sourcePackageName)
  126. && extras != null
  127. && extras.getBoolean("SyncManagerJob", false)) {
  128. sourcePackageName = extras.getString("owningPackage", sourcePackageName);
  129. if (DEBUG) {
  130. Slog.i(TAG, "Fixing up sync job source package name from 'android' to '"
  131. + sourcePackageName + "'");
  132. }
  133. }
  134. // And now we're done
  135. JobStatus js = new JobStatus(
  136. jobBuilder.build(), uid, sourcePackageName, sourceUserId, sourceTag,
  137. elapsedRuntimes.first, elapsedRuntimes.second);
  138. return js;
  139. }
这里面以后很多的细节,继续看:

1.1.3.1 buildBuilderFromXml

用来创建 JobInfo:
  1. private JobInfo.Builder buildBuilderFromXml(XmlPullParser parser) throws NumberFormatException {
  2. // Pull out required fields from <job> attributes.
  3. int jobId = Integer.parseInt(parser.getAttributeValue(null, "jobid"));
  4. String packageName = parser.getAttributeValue(null, "package");
  5. String className = parser.getAttributeValue(null, "class");
  6. ComponentName cname = new ComponentName(packageName, className);
  7. return new JobInfo.Builder(jobId, cname);
  8. }
这里使用了建造者模式:
  1. public static final class Builder {
  2. private final int mJobId;
  3. private final ComponentName mJobService;
  4. private PersistableBundle mExtras = PersistableBundle.EMPTY;
  5. private int mPriority = PRIORITY_DEFAULT;
  6. private int mFlags;
  7. // Requirements.
  8. private boolean mRequiresCharging;
  9. private boolean mRequiresDeviceIdle;
  10. private int mNetworkType;
  11. private ArrayList<TriggerContentUri> mTriggerContentUris;
  12. private long mTriggerContentUpdateDelay = -1;
  13. private long mTriggerContentMaxDelay = -1;
  14. private boolean mIsPersisted;
  15. // One-off parameters.
  16. private long mMinLatencyMillis;
  17. private long mMaxExecutionDelayMillis;
  18. // Periodic parameters.
  19. private boolean mIsPeriodic;
  20. private boolean mHasEarlyConstraint;
  21. private boolean mHasLateConstraint;
  22. private long mIntervalMillis;
  23. private long mFlexMillis;
  24. // Back-off parameters.
  25. private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS;
  26. private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY;
  27. /** Easy way to track whether the client has tried to set a back-off policy. */
  28. private boolean mBackoffPolicySet = false;
  29. public Builder(int jobId, ComponentName jobService) {
  30. mJobService = jobService;
  31. mJobId = jobId;
  32. }
  33.  
  34. ... ... ... ...
  35. }
返回了 JobInfo 的内部类 Builder 的对象!

1.1.3.2 buildConstraintsFromXml

传入参数:JobInfo.Buidlder 和 XmlPullParser :
  1. private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) {
  2. String val = parser.getAttributeValue(null, "connectivity");
  3. if (val != null) {
  4. jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
  5. }
  6. val = parser.getAttributeValue(null, "unmetered");
  7. if (val != null) {
  8. jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
  9. }
  10. val = parser.getAttributeValue(null, "not-roaming");
  11. if (val != null) {
  12. jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING);
  13. }
  14. val = parser.getAttributeValue(null, "idle");
  15. if (val != null) {
  16. jobBuilder.setRequiresDeviceIdle(true);
  17. }
  18. val = parser.getAttributeValue(null, "charging");
  19. if (val != null) {
  20. jobBuilder.setRequiresCharging(true);
  21. }
  22. }
显然,这个方法也很简单,解析剩下的参数!

1.1.3.3 buildExecutionTimesFromXml

计算这个 Job 的最早运行时间和最晚的结束时间:
  1. private Pair<Long, Long> buildExecutionTimesFromXml(XmlPullParser parser)
  2. throws NumberFormatException {
  3. // Pull out execution time data.
  4. final long nowWallclock = System.currentTimeMillis();
  5. final long nowElapsed = SystemClock.elapsedRealtime();
  6. long earliestRunTimeElapsed = JobStatus.NO_EARLIEST_RUNTIME;
  7. long latestRunTimeElapsed = JobStatus.NO_LATEST_RUNTIME;
  8. String val = parser.getAttributeValue(null, "deadline");
  9. if (val != null) {
  10. long latestRuntimeWallclock = Long.valueOf(val);
  11. long maxDelayElapsed =
  12. Math.max(latestRuntimeWallclock - nowWallclock, 0);
  13. latestRunTimeElapsed = nowElapsed + maxDelayElapsed;
  14. }
  15. val = parser.getAttributeValue(null, "delay");
  16. if (val != null) {
  17. long earliestRuntimeWallclock = Long.valueOf(val);
  18. long minDelayElapsed =
  19. Math.max(earliestRuntimeWallclock - nowWallclock, 0);
  20. earliestRunTimeElapsed = nowElapsed + minDelayElapsed;
  21. }
  22. return Pair.create(earliestRunTimeElapsed, latestRunTimeElapsed);
  23. }
  24. }
接着来看:

1.1.3.4 maybeBuildBackoffPolicyFromXml

  1. /**
  2. * Builds the back-off policy out of the params tag. These attributes may not exist, depending
  3. * on whether the back-off was set when the job was first scheduled.
  4. */
  5. private void maybeBuildBackoffPolicyFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) {
  6. String val = parser.getAttributeValue(null, "initial-backoff");
  7. if (val != null) {
  8. long initialBackoff = Long.valueOf(val);
  9. val = parser.getAttributeValue(null, "backoff-policy");
  10. int backoffPolicy = Integer.parseInt(val); // Will throw NFE which we catch higher up.
  11. jobBuilder.setBackoffCriteria(initialBackoff, backoffPolicy);
  12. }
  13. }
接着:

1.1.3.5 PersistableBundle.restoreFromXml

  1. /** @hide */
  2. public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
  3. XmlPullParserException {
  4. final int outerDepth = in.getDepth();
  5. final String startTag = in.getName();
  6. final String[] tagName = new String[1];
  7. int event;
  8. while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
  9. (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
  10. if (event == XmlPullParser.START_TAG) {
  11. return new PersistableBundle((ArrayMap<String, Object>)
  12. XmlUtils.readThisArrayMapXml(in, startTag, tagName,
  13. new MyReadMapCallback()));
  14. }
  15. }
  16. return EMPTY;
  17. }
接着:

1.1.3.6 JobInfo.buidler.build

根据解析的结果,创建 JobInfo 对象,这里调用了 JobInfo.buidler 的 build 方法:
  1. ... ... ... ...
  2.  
  3. public JobInfo build() {
  4. // Allow jobs with no constraints - What am I, a database?
  5. if (!mHasEarlyConstraint && !mHasLateConstraint && !mRequiresCharging &&
  6. !mRequiresDeviceIdle && mNetworkType == NETWORK_TYPE_NONE &&
  7. mTriggerContentUris == null) {
  8. throw new IllegalArgumentException("You're trying to build a job with no " +
  9. "constraints, this is not allowed.");
  10. }
  11. mExtras = new PersistableBundle(mExtras); // Make our own copy.
  12. // Check that a deadline was not set on a periodic job.
  13. if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) {
  14. throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
  15. "periodic job.");
  16. }
  17. if (mIsPeriodic && (mMinLatencyMillis != 0L)) {
  18. throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
  19. "periodic job");
  20. }
  21. if (mIsPeriodic && (mTriggerContentUris != null)) {
  22. throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " +
  23. "periodic job");
  24. }
  25. if (mIsPersisted && (mTriggerContentUris != null)) {
  26. throw new IllegalArgumentException("Can't call addTriggerContentUri() on a " +
  27. "persisted job");
  28. }
  29. if (mBackoffPolicySet && mRequiresDeviceIdle) {
  30. throw new IllegalArgumentException("An idle mode job will not respect any" +
  31. " back-off policy, so calling setBackoffCriteria with" +
  32. " setRequiresDeviceIdle is an error.");
  33. }
  34. JobInfo job = new JobInfo(this);
  35. if (job.isPeriodic()) {
  36. if (job.intervalMillis != job.getIntervalMillis()) {
  37. StringBuilder builder = new StringBuilder();
  38. builder.append("Specified interval for ")
  39. .append(String.valueOf(mJobId))
  40. .append(" is ");
  41. formatDuration(mIntervalMillis, builder);
  42. builder.append(". Clamped to ");
  43. formatDuration(job.getIntervalMillis(), builder);
  44. Log.w(TAG, builder.toString());
  45. }
  46. if (job.flexMillis != job.getFlexMillis()) {
  47. StringBuilder builder = new StringBuilder();
  48. builder.append("Specified flex for ")
  49. .append(String.valueOf(mJobId))
  50. .append(" is ");
  51. formatDuration(mFlexMillis, builder);
  52. builder.append(". Clamped to ");
  53. formatDuration(job.getFlexMillis(), builder);
  54. Log.w(TAG, builder.toString());
  55. }
  56. }
  57. return job;
  58. }
  59. ... ... ... ...
这里返回了 JobInfo 对象!

1.1.3.7 new JobStatus

最后,根据创建的 JobInfo 对象和前面的解析结果,创建 JobStatus 对象!
  1. public JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId,
  2. String sourceTag, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
  3. this(job, callingUid, sourcePackageName, sourceUserId, sourceTag, 0,
  4. earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
  5. }
这个构造器调用了下面这个:
  1. private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
  2. int sourceUserId, String tag, int numFailures, long earliestRunTimeElapsedMillis,
  3. long latestRunTimeElapsedMillis) {
  4. this.job = job;
  5. this.callingUid = callingUid;
  6. int tempSourceUid = -1;
  7. if (sourceUserId != -1 && sourcePackageName != null) {
  8. try {
  9. tempSourceUid = AppGlobals.getPackageManager().getPackageUid(sourcePackageName, 0,
  10. sourceUserId);
  11. } catch (RemoteException ex) {
  12. // Can't happen, PackageManager runs in the same process.
  13. }
  14. }
  15. if (tempSourceUid == -1) {
  16. this.sourceUid = callingUid;
  17. this.sourceUserId = UserHandle.getUserId(callingUid);
  18. this.sourcePackageName = job.getService().getPackageName();
  19. this.sourceTag = null;
  20. } else {
  21. this.sourceUid = tempSourceUid;
  22. this.sourceUserId = sourceUserId;
  23. this.sourcePackageName = sourcePackageName;
  24. this.sourceTag = tag;
  25. }
  26. this.batteryName = this.sourceTag != null
  27. ? this.sourceTag + ":" + job.getService().getPackageName()
  28. : job.getService().flattenToShortString();
  29. this.tag = "*job*/" + this.batteryName;
  30. this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
  31. this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
  32. this.numFailures = numFailures;
  33. int requiredConstraints = 0;
  34. if (job.getNetworkType() == JobInfo.NETWORK_TYPE_ANY) {
  35. requiredConstraints |= CONSTRAINT_CONNECTIVITY;
  36. }
  37. if (job.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED) {
  38. requiredConstraints |= CONSTRAINT_UNMETERED;
  39. }
  40. if (job.getNetworkType() == JobInfo.NETWORK_TYPE_NOT_ROAMING) {
  41. requiredConstraints |= CONSTRAINT_NOT_ROAMING;
  42. }
  43. if (job.isRequireCharging()) {
  44. requiredConstraints |= CONSTRAINT_CHARGING;
  45. }
  46. if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) {
  47. requiredConstraints |= CONSTRAINT_TIMING_DELAY;
  48. }
  49. if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
  50. requiredConstraints |= CONSTRAINT_DEADLINE;
  51. }
  52. if (job.isRequireDeviceIdle()) {
  53. requiredConstraints |= CONSTRAINT_IDLE;
  54. }
  55. if (job.getTriggerContentUris() != null) {
  56. requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
  57. }
  58. this.requiredConstraints = requiredConstraints;
  59. }
暂时看到这里!

1.2 JobHandler

接下来,我们来看看 JobHander 的消息处理机制:
  1. private class JobHandler extends Handler {
  2. public JobHandler(Looper looper) {
  3. super(looper);
  4. }
  5. @Override
  6. public void handleMessage(Message message) {
  7. synchronized (mLock) {
  8. // 在系统刚刚启动的时候,mReadyToRock 的值为 false,当系统启动到 phase 600,则 mReadyToRock=true.
  9. if (!mReadyToRock) {
  10. return;
  11. }
  12. }
  13. switch (message.what) {
  14. case MSG_JOB_EXPIRED:
  15. synchronized (mLock) {
  16. JobStatus runNow = (JobStatus) message.obj;
  17. // runNow can be null, which is a controller's way of indicating that its
  18. // state is such that all ready jobs should be run immediately.
  19. if (runNow != null && !mPendingJobs.contains(runNow)
  20. && mJobs.containsJob(runNow)) {
  21. mJobPackageTracker.notePending(runNow);
  22. mPendingJobs.add(runNow);
  23. }
  24. queueReadyJobsForExecutionLockedH();
  25. }
  26. break;
  27. case MSG_CHECK_JOB: // 检查任务
  28. synchronized (mLock) {
  29. if (mReportedActive) {
  30. // if jobs are currently being run, queue all ready jobs for execution.
  31. queueReadyJobsForExecutionLockedH();
  32. } else {
  33. // Check the list of jobs and run some of them if we feel inclined.
  34. maybeQueueReadyJobsForExecutionLockedH();
  35. }
  36. }
  37. break;
  38. case MSG_CHECK_JOB_GREEDY:
  39. synchronized (mLock) {
  40. queueReadyJobsForExecutionLockedH();
  41. }
  42. break;
  43. case MSG_STOP_JOB: // 停止任务
  44. cancelJobImpl((JobStatus)message.obj, null);
  45. break;
  46. }
  47. maybeRunPendingJobsH();
  48. // Don't remove JOB_EXPIRED in case one came along while processing the queue.
  49. removeMessages(MSG_CHECK_JOB);
  50. }
  51. ... ... ... ...
  52.  
  53. private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
  54. ... ... ... ...
  55.  
  56. private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
  57. ... ... ... ...
  58. }
这里,我们省略了一些方法,以后再慢慢分析!

1.3 Controllers

上面我们总结了 7.0 所有的控制器,下面,我们一个一个来看!

1.3.1 ConnectivityController

我们先来看一个控制器 ConnectivityController,先来看看他的代码:
  1. public class ConnectivityController extends StateController implements
  2. ConnectivityManager.OnNetworkActiveListener {
  3. private static final String TAG = "JobScheduler.Conn";
  4. private final ConnectivityManager mConnManager;
  5. private final NetworkPolicyManager mNetPolicyManager;
  6. @GuardedBy("mLock")
  7. private final ArrayList<JobStatus> mTrackedJobs = new ArrayList<JobStatus>();
  8. /** Singleton. */
  9. private static ConnectivityController mSingleton;
  10. private static Object sCreationLock = new Object();
  11. public static ConnectivityController get(JobSchedulerService jms) {
  12. synchronized (sCreationLock) {
  13. if (mSingleton == null) {
  14. mSingleton = new ConnectivityController(jms, jms.getContext(), jms.getLock());
  15. }
  16. return mSingleton;
  17. }
  18. }
  19. private ConnectivityController(StateChangedListener stateChangedListener, Context context,
  20. Object lock) {
  21. super(stateChangedListener, context, lock);
  22. mConnManager = mContext.getSystemService(ConnectivityManager.class);
  23. mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
  24. // 注册监听网络连接状态的广播,且采用 BackgroundThread 线程
  25. final IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
  26. mContext.registerReceiverAsUser(
  27. mConnectivityReceiver, UserHandle.SYSTEM, intentFilter, null, null);
  28. mNetPolicyManager.registerListener(mNetPolicyListener);
  29. }
当监听到 CONNECTIVITY_ACTION 广播,onReceive 方法的执行位于 “android.bg” 线程。

2 onStart

在 onStart 方法中,又调用了如下的方法:
  1. @Override
  2. public void onStart() {
  3. publishLocalService(JobSchedulerInternal.class, new LocalService());
  4. publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
  5. }
主要有两个作用:
  1. 公开本地服务:LocalService,类型为 JobSchedulerInternal.class,只能被系统进程访问。
  2. 公开Binder通信服务,名称为:jobscheduler,服务的实体对象是:mJobSchedulerStub,实现了 IJobScheduler.Stub 接口,其实就是将 JobSchedulerService 自身注册进 SystemManager 中,是其能被其他服务和应用访问到!
接下来,我们一个一个来看:

2.1 publishLocalService

这个方法是继承自 SystemService 的:
  1. /**
  2. * Publish the service so it is only accessible to the system process.
  3. */
  4. protected final <T> void publishLocalService(Class<T> type, T service) {
  5. LocalServices.addService(type, service);
  6. }
调用了 LocalServices 的静态方法,addService:
  1. public final class LocalServices {
  2. private LocalServices() {}
  3. private static final ArrayMap<Class<?>, Object> sLocalServiceObjects =
  4. new ArrayMap<Class<?>, Object>();
  5. ... ... ... ...
  6. /**
  7. * Adds a service instance of the specified interface to the global registry of local services.
  8. */
  9. public static <T> void addService(Class<T> type, T service) {
  10. synchronized (sLocalServiceObjects) {
  11. if (sLocalServiceObjects.containsKey(type)) {
  12. throw new IllegalStateException("Overriding service registration");
  13. }
  14. sLocalServiceObjects.put(type, service);
  15. }
  16. }
  17. ... ... ... ...
  18. }
接着来看:

2.2 publishBinderService

这个方法也是继承了 SystemServer 的方法:
  1. /**
  2. * Publish the service so it is accessible to other services and apps.
  3. */
  4. protected final void publishBinderService(String name, IBinder service) {
  5. publishBinderService(name, service, false);
  6. }
  7. /**
  8. * Publish the service so it is accessible to other services and apps.
  9. */
  10. protected final void publishBinderService(String name, IBinder service,
  11. boolean allowIsolated) {
  12. ServiceManager.addService(name, service, allowIsolated);
  13. }
其实就是将自身加入到 SystemManager 中去!

3 onBootPhase

设备开机的时候,会调用这个方法:
  1. @Override
  2. public void onBootPhase(int phase) {
  3. if (PHASE_SYSTEM_SERVICES_READY == phase) { // 500:表示系统服务准备就绪。
  4. mConstants.start(getContext().getContentResolver());
  5. // Register br for package removals and user removals.
  6. // 注册第一个 BroadcastReceiver,监听 package 的操作!
  7. final IntentFilter filter = new IntentFilter();
  8. filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
  9. filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
  10. filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
  11. filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
  12. filter.addDataScheme("package");
  13. getContext().registerReceiverAsUser(
  14. mBroadcastReceiver, UserHandle.ALL, filter, null, null);
  15.  // 注册第二个 BroadcastReceiver,监听 user 的移除操作!
  16. final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
  17. getContext().registerReceiverAsUser(
  18. mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
  19. mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
  20. try {
  21. // 注册 uid 观察者,用于回调!
  22. ActivityManagerNative.getDefault().registerUidObserver(mUidObserver,
  23. ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
  24. | ActivityManager.UID_OBSERVER_IDLE);
  25. } catch (RemoteException e) {
  26. // ignored; both services live in system_server
  27. }
  28. } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { // 600:表示服务可以被 start 和 binder 到第三方 app。
  29. synchronized (mLock) {
  30. // Let's go!
  31. mReadyToRock = true; // JobSchedulerService 已经准备好了!
  32. mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
  33. BatteryStats.SERVICE_NAME));
  34. // 获得本地设备控制器!
  35. mLocalDeviceIdleController
  36. = LocalServices.getService(DeviceIdleController.LocalService.class);
  37. // Create the "runners".
  38. // 最多允许一次执行 16 个任务
  39. for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
  40. // 需要被执行的任务,都会保存到 JobServiceContext 对象中!
  41. mActiveServices.add(
  42. new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
  43. getContext().getMainLooper()));
  44. }
  45. // Attach jobs to their controllers.
  46. // 将每个任务绑定到对应的控制器中。
  47. mJobs.forEachJob(new JobStatusFunctor() {
  48. @Override
  49. public void process(JobStatus job) {
  50. for (int controller = 0; controller < mControllers.size(); controller++) {
  51. final StateController sc = mControllers.get(controller);
  52. sc.maybeStartTrackingJobLocked(job, null);
  53. }
  54. }
  55. });
  56. // GO GO GO!
  57. // 发送 MSG_CHECK_JOB 消息给 JobHandler,检查任务
  58. mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
  59. }
  60. }
  61. }
这里的是根据开机的阶段,进行不同的初始化操作!

3.1 JobServiceContext

创建 JobServiceContext 对象,参数传递:
  • Context context:                        JobSchedulerService.getContext。
  • Object lock:                            JobSchedulerService.getObject。
  • IBatteryStats batteryStats:             JobSchedulerService.mBatteryStats。
  • JobPackageTracker tracker:              JobSchedulerService.tracker。
  • JobCompletedListener completedListener: JobSchedulerService,其本身实现了这个接口。
  • Looper looper:                          getContext().getMainLooper(),JobSchedulerService 所在主线程的 Looper。
  1. JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats,
  2. JobPackageTracker tracker, Looper looper) {
  3. this(service.getContext(), service.getLock(), batteryStats, tracker, service, looper);
  4. }
  5. @VisibleForTesting
  6. JobServiceContext(Context context, Object lock, IBatteryStats batteryStats,
  7. JobPackageTracker tracker, JobCompletedListener completedListener, Looper looper) {
  8. mContext = context;
  9. mLock = lock;
  10. mBatteryStats = batteryStats;
  11. mJobPackageTracker = tracker;
  12. mCallbackHandler = new JobServiceHandler(looper);
  13. mCompletedListener = completedListener;
  14. mAvailable = true;
  15. mVerb = VERB_FINISHED;
  16. mPreferredUid = NO_PREFERRED_UID;
  17. }
此处的 JobServiceHandler 采用的是 system_server 进程的主线程。

3.1.1 JobServiceHandler

每一个 JobServiceContext 都有一个 JobServiceHandler 对象:
  1. /**
  2. * Handles the lifecycle of the JobService binding/callbacks, etc. The convention within this
  3. * class is to append 'H' to each function name that can only be called on this handler. This
  4. * isn't strictly necessary because all of these functions are private, but helps clarity.
  5. */
  6. private class JobServiceHandler extends Handler {
  7. JobServiceHandler(Looper looper) {
  8. super(looper);
  9. }
  10. @Override
  11. public void handleMessage(Message message) {
  12. switch (message.what) {
  13. case MSG_SERVICE_BOUND:
  14. removeOpTimeOut();
  15. handleServiceBoundH();
  16. break;
  17. case MSG_CALLBACK:
  18. if (DEBUG) {
  19. Slog.d(TAG, "MSG_CALLBACK of : " + mRunningJob
  20. + " v:" + VERB_STRINGS[mVerb]);
  21. }
  22. removeOpTimeOut();
  23. if (mVerb == VERB_STARTING) {
  24. final boolean workOngoing = message.arg2 == 1;
  25. handleStartedH(workOngoing);
  26. } else if (mVerb == VERB_EXECUTING ||
  27. mVerb == VERB_STOPPING) {
  28. final boolean reschedule = message.arg2 == 1;
  29. handleFinishedH(reschedule);
  30. } else {
  31. if (DEBUG) {
  32. Slog.d(TAG, "Unrecognised callback: " + mRunningJob);
  33. }
  34. }
  35. break;
  36. case MSG_CANCEL:
  37. if (mVerb == VERB_FINISHED) {
  38. if (DEBUG) {
  39. Slog.d(TAG,
  40. "Trying to process cancel for torn-down context, ignoring.");
  41. }
  42. return;
  43. }
  44. mParams.setStopReason(message.arg1);
  45. if (message.arg1 == JobParameters.REASON_PREEMPT) {
  46. mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
  47. NO_PREFERRED_UID;
  48. }
  49. handleCancelH();
  50. break;
  51. case MSG_TIMEOUT:
  52. handleOpTimeoutH();
  53. break;
  54. case MSG_SHUTDOWN_EXECUTION:
  55. closeAndCleanupJobH(true /* needsReschedule */);
  56. break;
  57. default:
  58. Slog.e(TAG, "Unrecognised message: " + message);
  59. }
  60. }
  61. ... ... ... ... ...
  62. }
啦啦啦啦
4 总结
  • JSS.JobHandler:运行在 system_server 进程的主线程;
  • JobServiceContext.JobServiceHandler:运行在 system_server 进程的主线程;
  • JobSchedulerStub:作为实现接口 IJobScheduler 的 binder 服务端;
  • JobStore:其成员变量 mIoHandler 运行在 "android.io" 线程;
  • JobStatus:JSS 从 /data/system/job/jobs.xml 文件中读取每个 JobInfo,再解析成 JobStatus 对象,添加到 mJobSet。

可见 JobSchedulerService 启动过程,最主要工作是从 jobs.xml 文件收集所有的 jobs,放入到 JobStore 的成员变量 mJobSet。





























0 0
原创粉丝点击