当前位置:   article > 正文

(七十四)Android O Service启动流程梳理——startForegroundService

startforegroundservice

前言:之前梳理了startService和bindService,现在接着梳理下Android O比较有特点的startForegroundService。

  1. (六十四)Android O Service启动流程梳理——startService
  2. (六十五)Android O StartService的 anr timeout 流程分析
  3. (七十)Android O Service启动流程梳理——bindService

 

 

参考:https://blog.csdn.net/lylddinghffw/article/details/80366791

 

1.service启动简述

service启动分三种,比较简单的就是startService,Android O用于后台应用启动前台服务的startForegroundService和绑定服务的bindService。本篇继续梳理startForegroundService,startForegroundService使用需要注意的是Service启动后要在5s之内调用startForeground显示一个通知,不然就会anr。

 

2.startForegroundService流程梳理

2.1 ContextImpl

  1. @Override
  2. public ComponentName startForegroundService(Intent service) {
  3. warnIfCallingFromSystemProcess();
  4. return startServiceCommon(service, true, mUser);
  5. }

 

  1. private ComponentName startServiceCommon(Intent service, boolean requireForeground,
  2. UserHandle user) {
  3. try {
  4. validateServiceIntent(service);
  5. service.prepareToLeaveProcess(this);
  6. ComponentName cn = ActivityManager.getService().startService(
  7. mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
  8. getContentResolver()), requireForeground,
  9. getOpPackageName(), user.getIdentifier());
  10. if (cn != null) {
  11. if (cn.getPackageName().equals("!")) {
  12. throw new SecurityException(
  13. "Not allowed to start service " + service
  14. + " without permission " + cn.getClassName());
  15. } else if (cn.getPackageName().equals("!!")) {
  16. throw new SecurityException(
  17. "Unable to start service " + service
  18. + ": " + cn.getClassName());
  19. } else if (cn.getPackageName().equals("?")) {
  20. throw new IllegalStateException(
  21. "Not allowed to start service " + service + ": " + cn.getClassName());
  22. }
  23. }
  24. return cn;
  25. } catch (RemoteException e) {
  26. throw e.rethrowFromSystemServer();
  27. }
  28. }

对照下之前梳理的startService,发现只是requireForeground参数由false改为true表明需要置为前台Service,其他流程是一样的。注意关注下这个参数带来的变化。

  1. @Override
  2. public ComponentName startService(Intent service) {
  3. warnIfCallingFromSystemProcess();
  4. return startServiceCommon(service, false, mUser);
  5. }

那这里附一下startService的时序图,一样的流程就略过不赘述了。

 

2.2 AMS

  1. @Override
  2. public ComponentName startService(IApplicationThread caller, Intent service,
  3. String resolvedType, boolean requireForeground, String callingPackage, int userId)
  4. throws TransactionTooLargeException {
  5. enforceNotIsolatedCaller("startService");
  6. // Refuse possible leaked file descriptors
  7. if (service != null && service.hasFileDescriptors() == true) {
  8. throw new IllegalArgumentException("File descriptors passed in Intent");
  9. }
  10. if (callingPackage == null) {
  11. throw new IllegalArgumentException("callingPackage cannot be null");
  12. }
  13. if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
  14. "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
  15. synchronized(this) {
  16. final int callingPid = Binder.getCallingPid();
  17. final int callingUid = Binder.getCallingUid();
  18. final long origId = Binder.clearCallingIdentity();
  19. ComponentName res;
  20. try {
  21. res = mServices.startServiceLocked(caller, service,
  22. resolvedType, callingPid, callingUid,
  23. requireForeground, callingPackage, userId);
  24. } finally {
  25. Binder.restoreCallingIdentity(origId);
  26. }
  27. return res;
  28. }
  29. }

可以看到AMS并没有对requireForeground进行特殊处理,只是接着往下传。

 

2.3 ActiveServices

  1. ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
  2. int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
  3. throws TransactionTooLargeException {
  4. if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
  5. + " type=" + resolvedType + " args=" + service.getExtras());
  6. final boolean callerFg;
  7. if (caller != null) {
  8. final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
  9. if (callerApp == null) {
  10. throw new SecurityException(
  11. "Unable to find app for caller " + caller
  12. + " (pid=" + callingPid
  13. + ") when starting service " + service);
  14. }
  15. callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
  16. } else {
  17. callerFg = true;
  18. }
  19. ServiceLookupResult res =
  20. retrieveServiceLocked(service, resolvedType, callingPackage,
  21. callingPid, callingUid, userId, true, callerFg, false);
  22. if (res == null) {
  23. return null;
  24. }
  25. if (res.record == null) {
  26. return new ComponentName("!", res.permission != null
  27. ? res.permission : "private to package");
  28. }
  29. ServiceRecord r = res.record;
  30. if (!mAm.mUserController.exists(r.userId)) {
  31. Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId);
  32. return null;
  33. }
  34. // If this isn't a direct-to-foreground start, check our ability to kick off an
  35. // arbitrary service
  36. if (!r.startRequested && !fgRequired) {
  37. // Before going further -- if this app is not allowed to start services in the
  38. // background, then at this point we aren't going to let it period.
  39. final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
  40. r.appInfo.targetSdkVersion, callingPid, false, false);
  41. if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
  42. Slog.w(TAG, "Background start not allowed: service "
  43. + service + " to " + r.name.flattenToShortString()
  44. + " from pid=" + callingPid + " uid=" + callingUid
  45. + " pkg=" + callingPackage);
  46. if (allowed == ActivityManager.APP_START_MODE_DELAYED) {
  47. // In this case we are silently disabling the app, to disrupt as
  48. // little as possible existing apps.
  49. return null;
  50. }
  51. // This app knows it is in the new model where this operation is not
  52. // allowed, so tell it what has happened.
  53. UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);
  54. return new ComponentName("?", "app is in background uid " + uidRec);
  55. }
  56. }
  57. NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
  58. callingUid, r.packageName, service, service.getFlags(), null, r.userId);
  59. // If permissions need a review before any of the app components can run,
  60. // we do not start the service and launch a review activity if the calling app
  61. // is in the foreground passing it a pending intent to start the service when
  62. // review is completed.
  63. if (mAm.mPermissionReviewRequired) {
  64. if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
  65. callingUid, service, callerFg, userId)) {
  66. return null;
  67. }
  68. }
  69. if (unscheduleServiceRestartLocked(r, callingUid, false)) {
  70. if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "START SERVICE WHILE RESTART PENDING: " + r);
  71. }
  72. r.lastActivity = SystemClock.uptimeMillis();
  73. r.startRequested = true;
  74. r.delayedStop = false;
  75. r.fgRequired = fgRequired;
  76. r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
  77. service, neededGrants, callingUid));
  78. final ServiceMap smap = getServiceMapLocked(r.userId);
  79. boolean addToStarting = false;
  80. if (!callerFg && !fgRequired && r.app == null
  81. && mAm.mUserController.hasStartedUserState(r.userId)) {
  82. ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
  83. if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
  84. // If this is not coming from a foreground caller, then we may want
  85. // to delay the start if there are already other background services
  86. // that are starting. This is to avoid process start spam when lots
  87. // of applications are all handling things like connectivity broadcasts.
  88. // We only do this for cached processes, because otherwise an application
  89. // can have assumptions about calling startService() for a service to run
  90. // in its own process, and for that process to not be killed before the
  91. // service is started. This is especially the case for receivers, which
  92. // may start a service in onReceive() to do some additional work and have
  93. // initialized some global state as part of that.
  94. if (DEBUG_DELAYED_SERVICE) Slog.v(TAG_SERVICE, "Potential start delay of "
  95. + r + " in " + proc);
  96. if (r.delayed) {
  97. // This service is already scheduled for a delayed start; just leave
  98. // it still waiting.
  99. if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Continuing to delay: " + r);
  100. return r.name;
  101. }
  102. if (smap.mStartingBackground.size() >= mMaxStartingBackground) {
  103. // Something else is starting, delay!
  104. Slog.i(TAG_SERVICE, "Delaying start of: " + r);
  105. smap.mDelayedStartList.add(r);
  106. r.delayed = true;
  107. return r.name;
  108. }
  109. if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying: " + r);
  110. addToStarting = true;
  111. } else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
  112. // We slightly loosen when we will enqueue this new service as a background
  113. // starting service we are waiting for, to also include processes that are
  114. // currently running other services or receivers.
  115. addToStarting = true;
  116. if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
  117. "Not delaying, but counting as bg: " + r);
  118. } else if (DEBUG_DELAYED_STARTS) {
  119. StringBuilder sb = new StringBuilder(128);
  120. sb.append("Not potential delay (state=").append(proc.curProcState)
  121. .append(' ').append(proc.adjType);
  122. String reason = proc.makeAdjReason();
  123. if (reason != null) {
  124. sb.append(' ');
  125. sb.append(reason);
  126. }
  127. sb.append("): ");
  128. sb.append(r.toString());
  129. Slog.v(TAG_SERVICE, sb.toString());
  130. }
  131. } else if (DEBUG_DELAYED_STARTS) {
  132. if (callerFg || fgRequired) {
  133. Slog.v(TAG_SERVICE, "Not potential delay (callerFg=" + callerFg + " uid="
  134. + callingUid + " pid=" + callingPid + " fgRequired=" + fgRequired + "): " + r);
  135. } else if (r.app != null) {
  136. Slog.v(TAG_SERVICE, "Not potential delay (cur app=" + r.app + "): " + r);
  137. } else {
  138. Slog.v(TAG_SERVICE,
  139. "Not potential delay (user " + r.userId + " not started): " + r);
  140. }
  141. }
  142. ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
  143. return cmp;
  144. }

这里对于fgRequired为true的情况其实也只是赋值了一下

r.fgRequired = fgRequired;

后面流程和startService的onCreate一样的,没啥好重复说的,直到流程到了onStartCommand有点不一样了。

onStartCommand的开始是

  1. private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
  2. boolean oomAdjusted) throws TransactionTooLargeException {
  3. final int N = r.pendingStarts.size();
  4. if (N == 0) {
  5. return;
  6. }
  7. ArrayList<ServiceStartArgs> args = new ArrayList<>();
  8. while (r.pendingStarts.size() > 0) {
  9. ServiceRecord.StartItem si = r.pendingStarts.remove(0);
  10. if (DEBUG_SERVICE) {
  11. Slog.v(TAG_SERVICE, "Sending arguments to: "
  12. + r + " " + r.intent + " args=" + si.intent);
  13. }
  14. if (si.intent == null && N > 1) {
  15. // If somehow we got a dummy null intent in the middle,
  16. // then skip it. DO NOT skip a null intent when it is
  17. // the only one in the list -- this is to support the
  18. // onStartCommand(null) case.
  19. continue;
  20. }
  21. si.deliveredTime = SystemClock.uptimeMillis();
  22. r.deliveredStarts.add(si);
  23. si.deliveryCount++;
  24. if (si.neededGrants != null) {
  25. mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
  26. si.getUriPermissionsLocked());
  27. }
  28. mAm.grantEphemeralAccessLocked(r.userId, si.intent,
  29. r.appInfo.uid, UserHandle.getAppId(si.callingId));
  30. bumpServiceExecutingLocked(r, execInFg, "start");
  31. if (!oomAdjusted) {
  32. oomAdjusted = true;
  33. mAm.updateOomAdjLocked(r.app, true);
  34. }
  35. if (r.fgRequired && !r.fgWaiting) {
  36. if (!r.isForeground) {
  37. if (DEBUG_BACKGROUND_CHECK) {
  38. Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r);
  39. }
  40. scheduleServiceForegroundTransitionTimeoutLocked(r);
  41. } else {
  42. if (DEBUG_BACKGROUND_CHECK) {
  43. Slog.i(TAG, "Service already foreground; no new timeout: " + r);
  44. }
  45. r.fgRequired = false;
  46. }
  47. }
  48. int flags = 0;
  49. if (si.deliveryCount > 1) {
  50. flags |= Service.START_FLAG_RETRY;
  51. }
  52. if (si.doneExecutingCount > 0) {
  53. flags |= Service.START_FLAG_REDELIVERY;
  54. }
  55. args.add(new ServiceStartArgs(si.taskRemoved, si.id, flags, si.intent));
  56. }
  57. ParceledListSlice<ServiceStartArgs> slice = new ParceledListSlice<>(args);
  58. slice.setInlineCountLimit(4);
  59. Exception caughtException = null;
  60. try {
  61. r.app.thread.scheduleServiceArgs(r, slice);
  62. } catch (TransactionTooLargeException e) {
  63. if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large for " + args.size()
  64. + " args, first: " + args.get(0).args);
  65. Slog.w(TAG, "Failed delivering service starts", e);
  66. caughtException = e;
  67. } catch (RemoteException e) {
  68. // Remote process gone... we'll let the normal cleanup take care of this.
  69. if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
  70. Slog.w(TAG, "Failed delivering service starts", e);
  71. caughtException = e;
  72. } catch (Exception e) {
  73. Slog.w(TAG, "Unexpected exception", e);
  74. caughtException = e;
  75. }
  76. if (caughtException != null) {
  77. // Keep nesting count correct
  78. final boolean inDestroying = mDestroyingServices.contains(r);
  79. for (int i = 0; i < args.size(); i++) {
  80. serviceDoneExecutingLocked(r, inDestroying, inDestroying);
  81. }
  82. if (caughtException instanceof TransactionTooLargeException) {
  83. throw (TransactionTooLargeException)caughtException;
  84. }
  85. }
  86. }

这里面有下面一句比较关键

  1. if (r.fgRequired && !r.fgWaiting) {
  2. if (!r.isForeground) {
  3. if (DEBUG_BACKGROUND_CHECK) {
  4. Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r);
  5. }
  6. scheduleServiceForegroundTransitionTimeoutLocked(r);
  7. } else {
  8. if (DEBUG_BACKGROUND_CHECK) {
  9. Slog.i(TAG, "Service already foreground; no new timeout: " + r);
  10. }
  11. r.fgRequired = false;
  12. }
  13. }
  1. void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) {
  2. if (r.app.executingServices.size() == 0 || r.app.thread == null) {
  3. return;
  4. }
  5. Message msg = mAm.mHandler.obtainMessage(
  6. ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG);
  7. msg.obj = r;
  8. r.fgWaiting = true;
  9. mAm.mHandler.sendMessageDelayed(msg, SERVICE_START_FOREGROUND_TIMEOUT);
  10. }

这里表示在onStartCommand的流程开始时会设置一个5s的anr timeout,超过5s就会anr并且停止Service。

  1. // How long the startForegroundService() grace period is to get around to
  2. // calling startForeground() before we ANR + stop it.
  3. static final int SERVICE_START_FOREGROUND_TIMEOUT = 5*1000;

也附带看下AMS是怎么处理这个消息的:

  1. final class MainHandler extends Handler {
  2. public MainHandler(Looper looper) {
  3. super(looper, null, true);
  4. }
  5. @Override
  6. public void handleMessage(Message msg) {
  7. switch (msg.what) {
  8. case UPDATE_CONFIGURATION_MSG: {
  9. final ContentResolver resolver = mContext.getContentResolver();
  10. Settings.System.putConfigurationForUser(resolver, (Configuration) msg.obj,
  11. msg.arg1);
  12. } break;
  13. case GC_BACKGROUND_PROCESSES_MSG: {
  14. synchronized (ActivityManagerService.this) {
  15. performAppGcsIfAppropriateLocked();
  16. }
  17. } break;
  18. case SERVICE_TIMEOUT_MSG: {
  19. mServices.serviceTimeout((ProcessRecord)msg.obj);
  20. } break;
  21. case SERVICE_FOREGROUND_TIMEOUT_MSG: {
  22. mServices.serviceForegroundTimeout((ServiceRecord)msg.obj);
  23. } break;

还是扔回来调用ActiveServices来处理。

  1. void serviceForegroundTimeout(ServiceRecord r) {
  2. ProcessRecord app;
  3. synchronized (mAm) {
  4. if (!r.fgRequired || r.destroying) {
  5. return;
  6. }
  7. if (DEBUG_BACKGROUND_CHECK) {
  8. Slog.i(TAG, "Service foreground-required timeout for " + r);
  9. }
  10. app = r.app;
  11. r.fgWaiting = false;
  12. stopServiceLocked(r);
  13. }
  14. if (app != null) {
  15. mAm.mAppErrors.appNotResponding(app, null, null, false,
  16. "Context.startForegroundService() did not then call Service.startForeground()");
  17. }
  18. }

直接停止Service并且通过AMS使应用 anr,提示“Context.startForegroundService() did not then call Service.startForeground()”。

 

3. startForeground流程梳理

 

3.1 Service

  1. /**
  2. * If your service is started (running through {@link Context#startService(Intent)}), then
  3. * also make this service run in the foreground, supplying the ongoing
  4. * notification to be shown to the user while in this state.
  5. * By default started services are background, meaning that their process won't be given
  6. * foreground CPU scheduling (unless something else in that process is foreground) and,
  7. * if the system needs to kill them to reclaim more memory (such as to display a large page in a
  8. * web browser), they can be killed without too much harm. You use
  9. * {@link #startForeground} if killing your service would be disruptive to the user, such as
  10. * if your service is performing background music playback, so the user
  11. * would notice if their music stopped playing.
  12. *
  13. * <p>Note that calling this method does <em>not</em> put the service in the started state
  14. * itself, even though the name sounds like it. You must always call
  15. * {@link #startService(Intent)} first to tell the system it should keep the service running,
  16. * and then use this method to tell it to keep it running harder.</p>
  17. *
  18. * @param id The identifier for this notification as per
  19. * {@link NotificationManager#notify(int, Notification)
  20. * NotificationManager.notify(int, Notification)}; must not be 0.
  21. * @param notification The Notification to be displayed.
  22. *
  23. * @see #stopForeground(boolean)
  24. */
  25. public final void startForeground(int id, Notification notification) {
  26. try {
  27. mActivityManager.setServiceForeground(
  28. new ComponentName(this, mClassName), mToken, id,
  29. notification, 0);
  30. } catch (RemoteException ex) {
  31. }
  32. }

 

3.2 AMS

  1. @Override
  2. public void setServiceForeground(ComponentName className, IBinder token,
  3. int id, Notification notification, int flags) {
  4. synchronized(this) {
  5. mServices.setServiceForegroundLocked(className, token, id, notification, flags);
  6. }
  7. }

 

3.3 ActiveServices

  1. public void setServiceForegroundLocked(ComponentName className, IBinder token,
  2. int id, Notification notification, int flags) {
  3. final int userId = UserHandle.getCallingUserId();
  4. final long origId = Binder.clearCallingIdentity();
  5. try {
  6. ServiceRecord r = findServiceLocked(className, token, userId);
  7. if (r != null) {
  8. setServiceForegroundInnerLocked(r, id, notification, flags);
  9. }
  10. } finally {
  11. Binder.restoreCallingIdentity(origId);
  12. }
  13. }

 

  1. private void setServiceForegroundInnerLocked(ServiceRecord r, int id,
  2. Notification notification, int flags) {
  3. if (id != 0) {
  4. if (notification == null) {
  5. throw new IllegalArgumentException("null notification");
  6. }
  7. // Instant apps need permission to create foreground services.
  8. if (r.appInfo.isInstantApp()) {
  9. final int mode = mAm.mAppOpsService.checkOperation(
  10. AppOpsManager.OP_INSTANT_APP_START_FOREGROUND,
  11. r.appInfo.uid,
  12. r.appInfo.packageName);
  13. switch (mode) {
  14. case AppOpsManager.MODE_ALLOWED:
  15. break;
  16. case AppOpsManager.MODE_IGNORED:
  17. Slog.w(TAG, "Instant app " + r.appInfo.packageName
  18. + " does not have permission to create foreground services"
  19. + ", ignoring.");
  20. return;
  21. case AppOpsManager.MODE_ERRORED:
  22. throw new SecurityException("Instant app " + r.appInfo.packageName
  23. + " does not have permission to create foreground services");
  24. default:
  25. try {
  26. if (AppGlobals.getPackageManager().checkPermission(
  27. android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
  28. r.appInfo.packageName, UserHandle.getUserId(r.appInfo.uid))
  29. != PackageManager.PERMISSION_GRANTED) {
  30. throw new SecurityException("Instant app " + r.appInfo.packageName
  31. + " does not have permission to create foreground"
  32. + "services");
  33. }
  34. } catch (RemoteException e) {
  35. throw new SecurityException("Failed to check instant app permission." ,
  36. e);
  37. }
  38. }
  39. }
  40. if (r.fgRequired) {
  41. if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) {
  42. Slog.i(TAG, "Service called startForeground() as required: " + r);
  43. }
  44. r.fgRequired = false;
  45. r.fgWaiting = false;
  46. mAm.mHandler.removeMessages(
  47. ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
  48. }
  49. if (r.foregroundId != id) {
  50. cancelForegroundNotificationLocked(r);
  51. r.foregroundId = id;
  52. }
  53. notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
  54. r.foregroundNoti = notification;
  55. if (!r.isForeground) {
  56. final ServiceMap smap = getServiceMapLocked(r.userId);
  57. if (smap != null) {
  58. ActiveForegroundApp active = smap.mActiveForegroundApps.get(r.packageName);
  59. if (active == null) {
  60. active = new ActiveForegroundApp();
  61. active.mPackageName = r.packageName;
  62. active.mUid = r.appInfo.uid;
  63. active.mShownWhileScreenOn = mScreenOn;
  64. if (r.app != null) {
  65. active.mAppOnTop = active.mShownWhileTop =
  66. r.app.uidRecord.curProcState
  67. <= ActivityManager.PROCESS_STATE_TOP;
  68. }
  69. active.mStartTime = active.mStartVisibleTime
  70. = SystemClock.elapsedRealtime();
  71. smap.mActiveForegroundApps.put(r.packageName, active);
  72. requestUpdateActiveForegroundAppsLocked(smap, 0);
  73. }
  74. active.mNumActive++;
  75. }
  76. r.isForeground = true;
  77. }
  78. r.postNotification();
  79. if (r.app != null) {
  80. updateServiceForegroundLocked(r.app, true);
  81. }
  82. getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r);
  83. mAm.notifyPackageUse(r.serviceInfo.packageName,
  84. PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);
  85. } else {
  86. if (r.isForeground) {
  87. final ServiceMap smap = getServiceMapLocked(r.userId);
  88. if (smap != null) {
  89. decActiveForegroundAppLocked(smap, r);
  90. }
  91. r.isForeground = false;
  92. if (r.app != null) {
  93. mAm.updateLruProcessLocked(r.app, false, null);
  94. updateServiceForegroundLocked(r.app, true);
  95. }
  96. }
  97. if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) {
  98. cancelForegroundNotificationLocked(r);
  99. r.foregroundId = 0;
  100. r.foregroundNoti = null;
  101. } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
  102. r.stripForegroundServiceFlagFromNotification();
  103. if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) {
  104. r.foregroundId = 0;
  105. r.foregroundNoti = null;
  106. }
  107. }
  108. }
  109. }

setServiceForegroundInnerLocked这个方法很关键,关键代码分如下几部分梳理:

1)首先Service创建的notification id不能是0并且notification不能是null,并且Android O 如果创建通知的话还要设置channel的。

  1. if (id != 0) {
  2. if (notification == null) {
  3. throw new IllegalArgumentException("null notification");
  4. }

 

2)设置r.fgRequired为false,表明已经设置Service为foreground了,不需要了。并且移除了之前发给AMS的SERVICE_FOREGROUND_TIMEOUT_MSG,这样就不会anr了。但是这句话要在发出消息的5s之内调用到,否则还是会anr。

由于消息是在onStartCommand流程开始时发出的,如果我们的Service在onCreate就开始调用startForeground,时限也许会长于5s。但由于一个是AMS的流程,一个是APP的流程,是异步的,长也长不了多少,不是等onCreate执行完再发的。

  1. if (r.fgRequired) {
  2. if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) {
  3. Slog.i(TAG, "Service called startForeground() as required: " + r);
  4. }
  5. r.fgRequired = false;
  6. r.fgWaiting = false;
  7. mAm.mHandler.removeMessages(
  8. ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
  9. }

3)取消通知,但会检查应用的其他前台Service是否有相同的notification id,如果一样,则不取消。

  1. if (r.foregroundId != id) {
  2. cancelForegroundNotificationLocked(r);
  3. r.foregroundId = id;
  4. }
  1. private void cancelForegroundNotificationLocked(ServiceRecord r) {
  2. if (r.foregroundId != 0) {
  3. // First check to see if this app has any other active foreground services
  4. // with the same notification ID. If so, we shouldn't actually cancel it,
  5. // because that would wipe away the notification that still needs to be shown
  6. // due the other service.
  7. ServiceMap sm = getServiceMapLocked(r.userId);
  8. if (sm != null) {
  9. for (int i = sm.mServicesByName.size()-1; i >= 0; i--) {
  10. ServiceRecord other = sm.mServicesByName.valueAt(i);
  11. if (other != r && other.foregroundId == r.foregroundId
  12. && other.packageName.equals(r.packageName)) {
  13. // Found one! Abort the cancel.
  14. return;
  15. }
  16. }
  17. }
  18. r.cancelNotification();
  19. }
  20. }

4)将Service设为前台Service

  1. notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
  2. r.foregroundNoti = notification;
  3. if (!r.isForeground) {
  4. final ServiceMap smap = getServiceMapLocked(r.userId);
  5. if (smap != null) {
  6. ActiveForegroundApp active = smap.mActiveForegroundApps.get(r.packageName);
  7. if (active == null) {
  8. active = new ActiveForegroundApp();
  9. active.mPackageName = r.packageName;
  10. active.mUid = r.appInfo.uid;
  11. active.mShownWhileScreenOn = mScreenOn;
  12. if (r.app != null) {
  13. active.mAppOnTop = active.mShownWhileTop =
  14. r.app.uidRecord.curProcState
  15. <= ActivityManager.PROCESS_STATE_TOP;
  16. }
  17. active.mStartTime = active.mStartVisibleTime
  18. = SystemClock.elapsedRealtime();
  19. smap.mActiveForegroundApps.put(r.packageName, active);
  20. requestUpdateActiveForegroundAppsLocked(smap, 0);
  21. }
  22. active.mNumActive++;
  23. }
  24. r.isForeground = true;
  25. }

 

4. Service “Context.startForegroundService() did not then call Service.startForeground()” anr原因

结合之前2和3对startForegroundService和startForeground的分析,可以很清楚的判断出Service anr原因:

Service没有在AMS SERVICE_FOREGROUND_TIMEOUT_MSG发出5s内将其移除,简单地对应于应用描述就是调用startForegroundService后5s内没有及时在Service内调用startForeground。(5s在这的描述是不精确的,但差不了多少,startForeground建议在Service onCreate一开始就调用,也许能争取点时间)

 

5. Service “Context.startForegroundService() did not then call Service.startForeground()” crash原因

之前都梳理的anr,其实Android O这边还埋了个crash的坑。如果5s之内没有及时调用startForeground,然后Service destroy了,比如stopService,那Android O很人性化地不让Service anr了,让Service crash。

  1. private final void bringDownServiceLocked(ServiceRecord r) {
  2. //Slog.i(TAG, "Bring down service:");
  3. //r.dump(" ");
  4. // Report to all of the connections that the service is no longer
  5. // available.
  6. for (int conni=r.connections.size()-1; conni>=0; conni--) {
  7. ArrayList<ConnectionRecord> c = r.connections.valueAt(conni);
  8. for (int i=0; i<c.size(); i++) {
  9. ConnectionRecord cr = c.get(i);
  10. // There is still a connection to the service that is
  11. // being brought down. Mark it as dead.
  12. cr.serviceDead = true;
  13. try {
  14. cr.conn.connected(r.name, null, true);
  15. } catch (Exception e) {
  16. Slog.w(TAG, "Failure disconnecting service " + r.name +
  17. " to connection " + c.get(i).conn.asBinder() +
  18. " (in " + c.get(i).binding.client.processName + ")", e);
  19. }
  20. }
  21. }
  22. // Tell the service that it has been unbound.
  23. if (r.app != null && r.app.thread != null) {
  24. for (int i=r.bindings.size()-1; i>=0; i--) {
  25. IntentBindRecord ibr = r.bindings.valueAt(i);
  26. if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing down binding " + ibr
  27. + ": hasBound=" + ibr.hasBound);
  28. if (ibr.hasBound) {
  29. try {
  30. bumpServiceExecutingLocked(r, false, "bring down unbind");
  31. mAm.updateOomAdjLocked(r.app, true);
  32. ibr.hasBound = false;
  33. ibr.requested = false;
  34. r.app.thread.scheduleUnbindService(r,
  35. ibr.intent.getIntent());
  36. } catch (Exception e) {
  37. Slog.w(TAG, "Exception when unbinding service "
  38. + r.shortName, e);
  39. serviceProcessGoneLocked(r);
  40. }
  41. }
  42. }
  43. }
  44. // Check to see if the service had been started as foreground, but being
  45. // brought down before actually showing a notification. That is not allowed.
  46. if (r.fgRequired) {
  47. Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: "
  48. + r);
  49. r.fgRequired = false;
  50. r.fgWaiting = false;
  51. mAm.mHandler.removeMessages(
  52. ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
  53. if (r.app != null) {
  54. Message msg = mAm.mHandler.obtainMessage(
  55. ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
  56. msg.obj = r.app;
  57. mAm.mHandler.sendMessage(msg);
  58. }
  59. }
  60. if (DEBUG_SERVICE) {
  61. RuntimeException here = new RuntimeException();
  62. here.fillInStackTrace();
  63. Slog.v(TAG_SERVICE, "Bringing down " + r + " " + r.intent, here);
  64. }
  65. r.destroyTime = SystemClock.uptimeMillis();
  66. if (LOG_SERVICE_START_STOP) {
  67. EventLogTags.writeAmDestroyService(
  68. r.userId, System.identityHashCode(r), (r.app != null) ? r.app.pid : -1);
  69. }
  70. final ServiceMap smap = getServiceMapLocked(r.userId);
  71. ServiceRecord found = smap.mServicesByName.remove(r.name);
  72. // Note when this method is called by bringUpServiceLocked(), the service is not found
  73. // in mServicesByName and found will be null.
  74. if (found != null && found != r) {
  75. // This is not actually the service we think is running... this should not happen,
  76. // but if it does, fail hard.
  77. smap.mServicesByName.put(r.name, found);
  78. throw new IllegalStateException("Bringing down " + r + " but actually running "
  79. + found);
  80. }
  81. smap.mServicesByIntent.remove(r.intent);
  82. r.totalRestartCount = 0;
  83. unscheduleServiceRestartLocked(r, 0, true);
  84. // Also make sure it is not on the pending list.
  85. for (int i=mPendingServices.size()-1; i>=0; i--) {
  86. if (mPendingServices.get(i) == r) {
  87. mPendingServices.remove(i);
  88. if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Removed pending: " + r);
  89. }
  90. }
  91. cancelForegroundNotificationLocked(r);
  92. if (r.isForeground) {
  93. decActiveForegroundAppLocked(smap, r);
  94. }
  95. r.isForeground = false;
  96. r.foregroundId = 0;
  97. r.foregroundNoti = null;
  98. // Clear start entries.
  99. r.clearDeliveredStartsLocked();
  100. r.pendingStarts.clear();
  101. if (r.app != null) {
  102. synchronized (r.stats.getBatteryStats()) {
  103. r.stats.stopLaunchedLocked();
  104. }
  105. r.app.services.remove(r);
  106. if (r.whitelistManager) {
  107. updateWhitelistManagerLocked(r.app);
  108. }
  109. if (r.app.thread != null) {
  110. updateServiceForegroundLocked(r.app, false);
  111. try {
  112. bumpServiceExecutingLocked(r, false, "destroy");
  113. mDestroyingServices.add(r);
  114. r.destroying = true;
  115. mAm.updateOomAdjLocked(r.app, true);
  116. r.app.thread.scheduleStopService(r);
  117. } catch (Exception e) {
  118. Slog.w(TAG, "Exception when destroying service "
  119. + r.shortName, e);
  120. serviceProcessGoneLocked(r);
  121. }
  122. } else {
  123. if (DEBUG_SERVICE) Slog.v(
  124. TAG_SERVICE, "Removed service that has no process: " + r);
  125. }
  126. } else {
  127. if (DEBUG_SERVICE) Slog.v(
  128. TAG_SERVICE, "Removed service that is not running: " + r);
  129. }
  130. if (r.bindings.size() > 0) {
  131. r.bindings.clear();
  132. }
  133. if (r.restarter instanceof ServiceRestarter) {
  134. ((ServiceRestarter)r.restarter).setService(null);
  135. }
  136. int memFactor = mAm.mProcessStats.getMemFactorLocked();
  137. long now = SystemClock.uptimeMillis();
  138. if (r.tracker != null) {
  139. r.tracker.setStarted(false, memFactor, now);
  140. r.tracker.setBound(false, memFactor, now);
  141. if (r.executeNesting == 0) {
  142. r.tracker.clearCurrentOwner(r, false);
  143. r.tracker = null;
  144. }
  145. }
  146. smap.ensureNotStartingBackgroundLocked(r);
  147. }

上面代码中让Service用crash代替anr的代码如下:

  1. // Check to see if the service had been started as foreground, but being
  2. // brought down before actually showing a notification. That is not allowed.
  3. if (r.fgRequired) {
  4. Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: "
  5. + r);
  6. r.fgRequired = false;
  7. r.fgWaiting = false;
  8. mAm.mHandler.removeMessages(
  9. ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
  10. if (r.app != null) {
  11. Message msg = mAm.mHandler.obtainMessage(
  12. ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
  13. msg.obj = r.app;
  14. mAm.mHandler.sendMessage(msg);
  15. }
  16. }

 

5. 总结

其实startForegroundService的流程和startService总体来讲是差不多的,区别就在于5s 的anr时间限制内Service要调用startForeground。

如果没有及时调用,则会anr。又或者5s之内Service destroy了,那就来一个差不多的crash。

anr和crash中都会带有如下信息:“Context.startForegroundService() did not then call Service.startForeground()”

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/464538
推荐阅读
相关标签
  

闽ICP备14008679号