当前位置:   article > 正文

Android service 启动篇之 startService_android startservice

android startservice

系列博文:

Android 中service 详解

Android service 启动篇之 startService

Android service 启动篇之 bindService

Android service 启动篇之 startForegroundService

 

 

基于版本:Android O

0. 前言

 Android基础总结之六:Sevice 中是应用端对于service 使用的总结,其中看到启动service 需要的接口有startService 和bindService。在Android O 中又添加了一个接口api——startForegroundService。本篇主要围绕对两个start service接口以及中间有可能触发ANR的异常进行解析。

1. 启动入口api

上层启动service 直接接口在Context 中:

  1. @Override
  2. public ComponentName startService(Intent service) {
  3. warnIfCallingFromSystemProcess();
  4. return startServiceCommon(service, false, mUser);
  5. }
  6. @Override
  7. public ComponentName startForegroundService(Intent service) {
  8. warnIfCallingFromSystemProcess();
  9. return startServiceCommon(service, true, mUser);
  10. }
  1. @Override
  2. public ComponentName startServiceAsUser(Intent service, UserHandle user) {
  3. return startServiceCommon(service, false, user);
  4. }
  5. @Override
  6. public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) {
  7. return startServiceCommon(service, true, user);
  8. }

之前我们知道的是在启动一个service 的时候需要调用接口startService 或者bindService,为什么这里多了Foreground service?

在官方文档 Android 8.0 行为变更 中有这样一段话:

Android 8.0 有一项复杂功能;系统不允许后台应用创建后台服务。 因此,Android 8.0 引入了一种全新的方法,即 Context.startForegroundService(),以在前台启动新服务。

在系统创建服务后,应用有五秒的时间来调用该服务的 startForeground() 方法以显示新服务的用户可见通知。

如果应用在此时间限制内调用 startForeground(),则系统将停止服务并声明此应用为 ANR

回过头来对比下startService 和startForegroundService ,主要区别就是第二个参数,如果是前台服务,第二个参数为true。这里留意下,下面进一步解析时会用到。这两个函数最终调用的地方是相同的,都是函数startServiceCommon() :

  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. }

看到这里大概猜到 Android 中service 详解 其中的几个问题都是从这里抛出来的。

根本原因是AMS 调用startService 的返回值ComponentName 不是我们想要的,也就是说后面的处理肯定会创建一个ComponentName,package name 为 ! 、!! 和 ? 三个中的一个,而class name 会作为异常message 抛出。

还有一点,通过函数validateServiceIntent() 可以看到如果SDK 版本大于L 的,要求service 不能隐式启动。

  1. private void validateServiceIntent(Intent service) {
  2. if (service.getComponent() == null && service.getPackage() == null) {
  3. if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
  4. IllegalArgumentException ex = new IllegalArgumentException(
  5. "Service Intent must be explicit: " + service);
  6. throw ex;
  7. } else {
  8. Log.w(TAG, "Implicit intents with startService are not safe: " + service
  9. + " " + Debug.getCallers(2, 3));
  10. }
  11. }
  12. }

总结:

1、service 启动入口startService 和startForegroundService,其中startForegroundService 为O 版本才出现的。(bindService 下一篇介绍)

2、两个接口最终调用的地方是相同的,都是AMS 的startService,对于foreground service 参数requireForeground 为true。

2. AMS startService()

从上面Context 中startServiceCommon() 接口确定下startService 传入的参数:

  • caller thread
  • service 的intent
  • service 的type
  • requireForeground 标记是否为foreground
  • caller 的package name
  • user

确定参数后就可以放心进入source code 了:

  1. public ComponentName startService(IApplicationThread caller, Intent service,
  2. String resolvedType, boolean requireForeground, String callingPackage, int userId)
  3. throws TransactionTooLargeException {
  4. ...
  5. ...
  6. synchronized(this) {
  7. final int callingPid = Binder.getCallingPid();
  8. final int callingUid = Binder.getCallingUid();
  9. final long origId = Binder.clearCallingIdentity();
  10. ComponentName res;
  11. try {
  12. res = mServices.startServiceLocked(caller, service,
  13. resolvedType, callingPid, callingUid,
  14. requireForeground, callingPackage, userId);
  15. } finally {
  16. Binder.restoreCallingIdentity(origId);
  17. }
  18. return res;
  19. }
  20. }

省略掉之前判断的条件,该函数最终调用的应该是ActiveServices 中的startServiceLocked,参数都是直传的,多加了callingPid,callingUid。

3. ActiveServices startServiceLocked()

函数比较多,我们这里分步来解析。

3.1 获取应用是否为前台应用 

  1. final boolean callerFg;
  2. if (caller != null) {
  3. final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
  4. if (callerApp == null) {
  5. throw new SecurityException(
  6. "Unable to find app for caller " + caller
  7. + " (pid=" + callingPid
  8. + ") when starting service " + service);
  9. }
  10. callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
  11. } else {
  12. callerFg = true;
  13. }

这个callerFg 标记当前caller app 是前台还是后台。

3.2 retrieveServiceLocked()

  1. ServiceLookupResult res =
  2. retrieveServiceLocked(service, resolvedType, callingPackage,
  3. callingPid, callingUid, userId, true, callerFg, false);
  4. if (res == null) {
  5. return null;
  6. }
  7. if (res.record == null) {
  8. return new ComponentName("!", res.permission != null
  9. ? res.permission : "private to package");
  10. }

注意最后一个参数,判断是否是bind external service,如果该service 可以运行在外部进程中,那么servcie 在注册的时候需要置上flag FLAG_EXTERNAL_SERVICE。这样bindService 的时候将最后一个参数传入为true,就可以实现external service。

  1. if (r == null && !isBindExternal) {
  2. Intent.FilterComparison filter = new Intent.FilterComparison(service);
  3. r = smap.mServicesByIntent.get(filter);
  4. if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE, "Retrieved by intent: " + r);
  5. }
  6. if (r != null && (r.serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0
  7. && !callingPackage.equals(r.packageName)) {
  8. // If an external service is running within its own package, other packages
  9. // should not bind to that instance.
  10. r = null;
  11. if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Whoops, can't use existing external service");
  12. }

如果service 为FLAG_EXTERNAL_SERVICE,在startService() 的时候isBindExternal 为false,走的是上面一个case,如果是bindService(),最后走的是后面一个case,变量 r 最终为null,需要后面重现生成。

  1. ComponentName name = new ComponentName(
  2. sInfo.applicationInfo.packageName, sInfo.name);
  3. if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
  4. if (isBindExternal) {
  5. if (!sInfo.exported) {
  6. throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
  7. " is not exported");
  8. }
  9. if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
  10. throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
  11. " is not an isolatedProcess");
  12. }
  13. // Run the service under the calling package's application.
  14. ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo(
  15. callingPackage, ActivityManagerService.STOCK_PM_FLAGS, userId);
  16. if (aInfo == null) {
  17. throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " +
  18. "could not resolve client package " + callingPackage);
  19. }
  20. sInfo = new ServiceInfo(sInfo);
  21. sInfo.applicationInfo = new ApplicationInfo(sInfo.applicationInfo);
  22. sInfo.applicationInfo.packageName = aInfo.packageName;
  23. sInfo.applicationInfo.uid = aInfo.uid;
  24. name = new ComponentName(aInfo.packageName, name.getClassName());
  25. service.setComponent(name);
  26. } else {
  27. throw new SecurityException("BIND_EXTERNAL_SERVICE required for " +
  28. name);
  29. }
  • 会重新生成一个ComponentName,package name 为当前bind service 的app 的package,class name是原来service 的name。
  • 而且exported 需要置为true,否则会出现SecurityException。而对于startService 来说这个值直接设为false 即可。

详细code 这里暂不做分析,需要知道service 所有信息是在这里获取的,包括service 所需的permission check 也是在这里进行。如果出现异常,会导致该函数的返回值为null,或者是ServiceRecord 为null。那如果ServiceRecord 为null,就会出现code 中的package name 为 "!",最终会导致 java.lang.SecurityException: Not allowed to start service

3.3 checkOpNoThrow

  1. if (fgRequired) {
  2. final int mode = mAm.getAppOpsManager().checkOpNoThrow(
  3. AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
  4. switch (mode) {
  5. case AppOpsManager.MODE_ALLOWED:
  6. case AppOpsManager.MODE_DEFAULT:
  7. // All okay.
  8. break;
  9. case AppOpsManager.MODE_IGNORED:
  10. // Not allowed, fall back to normal start service, failing siliently
  11. // if background check restricts that.
  12. Slog.w(TAG, "startForegroundService not allowed due to app op: service "
  13. + service + " to " + r.shortInstanceName
  14. + " from pid=" + callingPid + " uid=" + callingUid
  15. + " pkg=" + callingPackage);
  16. fgRequired = false;
  17. forceSilentAbort = true;
  18. break;
  19. default:
  20. return new ComponentName("!!", "foreground not allowed as per app op");
  21. }
  22. }

如果是启动一个前台service,需要确认该service 是否有作为前台service 的权限。如果没有权限,则会就会出现code 中的package name 为 "!!",最终会导致 java.lang.SecurityException: Unable to start service

3.4 getAppStartModeLocked()

这个函数主要判断当前应用是否可以唤醒后台服务,这个函数还是有些东西的。

  1. final int startMode = (alwaysRestrict)
  2. ? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
  3. : appServicesRestrictedInBackgroundLocked(uid, packageName,
  4. packageTargetSdk);

这里传入的alwaysRestrict 为false,需要通过函数appServicesRestrictedInBackgroundLocked 进一步确认:

  1. int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
  2. // Persistent app?
  3. if (mPackageManagerInt.isPackagePersistent(packageName)) {
  4. if (DEBUG_BACKGROUND_CHECK) {
  5. Slog.i(TAG, "App " + uid + "/" + packageName
  6. + " is persistent; not restricted in background");
  7. }
  8. return ActivityManager.APP_START_MODE_NORMAL;
  9. }
  10. // Non-persistent but background whitelisted?
  11. if (uidOnBackgroundWhitelist(uid)) {
  12. if (DEBUG_BACKGROUND_CHECK) {
  13. Slog.i(TAG, "App " + uid + "/" + packageName
  14. + " on background whitelist; not restricted in background");
  15. }
  16. return ActivityManager.APP_START_MODE_NORMAL;
  17. }
  18. // Is this app on the battery whitelist?
  19. if (isOnDeviceIdleWhitelistLocked(uid)) {
  20. if (DEBUG_BACKGROUND_CHECK) {
  21. Slog.i(TAG, "App " + uid + "/" + packageName
  22. + " on idle whitelist; not restricted in background");
  23. }
  24. return ActivityManager.APP_START_MODE_NORMAL;
  25. }
  26. // None of the service-policy criteria apply, so we apply the common criteria
  27. return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
  28. }

3.4.1 启动后台服务的应用满足条件

  • 判断是否为persistent app
  • 判断uid 是否在白名单中(可以通过函数backgroundWhitelistUid 添加到白名单中)
  • 判断是否位于deivce id 的白名单中

这些条件可以创建条件让应用满足启动后台服务,如果一般应用,这些条件都无法满足了。

3.4.2 appRestrictedInBackgroundLocked()

在该应用不满足上面 3.4.1 条件时,会继续调用appRestrictedInBackgroundLocked()

  1. int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
  2. // Apps that target O+ are always subject to background check
  3. if (packageTargetSdk >= Build.VERSION_CODES.O) {
  4. if (DEBUG_BACKGROUND_CHECK) {
  5. Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted");
  6. }
  7. return ActivityManager.APP_START_MODE_DELAYED_RIGID;
  8. }
  9. // ...and legacy apps get an AppOp check
  10. int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
  11. uid, packageName);
  12. if (DEBUG_BACKGROUND_CHECK) {
  13. Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " bg appop " + appop);
  14. }
  15. switch (appop) {
  16. case AppOpsManager.MODE_ALLOWED:
  17. return ActivityManager.APP_START_MODE_NORMAL;
  18. case AppOpsManager.MODE_IGNORED:
  19. return ActivityManager.APP_START_MODE_DELAYED;
  20. default:
  21. return ActivityManager.APP_START_MODE_DELAYED_RIGID;
  22. }

如果SDK 版本大于等于Android O,直接放回APP_START_MODE_DELAYED_RIGID,也就是O 以上版本的应用在不满足上面3.4.1 中的条件时,是不允许启动后台服务的。

不过庆幸的是,对于Android O 之前版本的应用,会通过AppOpsManager 确认是否可以启动后台服务,而AppOpsManager 中默认是允许的。

回归到ActiveServices 中,如果不让启动后台服务,或者Android O版本的应用,会进入下面的case :

  1. if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
  2. Slog.w(TAG, "Background start not allowed: service "
  3. + service + " to " + r.name.flattenToShortString()
  4. + " from pid=" + callingPid + " uid=" + callingUid
  5. + " pkg=" + callingPackage);
  6. if (allowed == ActivityManager.APP_START_MODE_DELAYED) {
  7. // In this case we are silently disabling the app, to disrupt as
  8. // little as possible existing apps.
  9. return null;
  10. }
  11. // This app knows it is in the new model where this operation is not
  12. // allowed, so tell it what has happened.
  13. UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);
  14. return new ComponentName("?", "app is in background uid " + uidRec);
  15. }

最终出现了package name为 "?" 的ComponentName。

从而抛出java.lang.IllegalStateException: Not allowed to start service Intentjava.lang.IllegalStateException: Not allowed to start service Intent

3.5 一些变量的赋值

  1. if (r.appInfo.targetSdkVersion < Build.VERSION_CODES.O && fgRequired) {
  2. if (DEBUG_BACKGROUND_CHECK || DEBUG_FOREGROUND_SERVICE) {
  3. Slog.i(TAG, "startForegroundService() but host targets "
  4. + r.appInfo.targetSdkVersion + " - not requiring startForeground()");
  5. }
  6. fgRequired = false;
  7. }
  8. ...
  9. r.lastActivity = SystemClock.uptimeMillis();
  10. r.startRequested = true;
  11. r.delayedStop = false;
  12. r.fgRequired = fgRequired;
  13. r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
  14. service, neededGrants, callingUid));

回归到ActiveServices中,这里有些对于ServiceRecord 的赋值,后面处理的时候很重要。

例如后面在bringUpServiceLocked() 中需要知道有没有pendingStarts。

另外,对于版本小于O 的service apk,都将其作为后台service 启动;

3.6 startServiceInnerLocked()

这个函数是start service 的核心处理部分,在这之前的都是一些条件的过滤。

  1. ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
  2. boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
  3. ServiceState stracker = r.getTracker();
  4. if (stracker != null) {
  5. stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
  6. }
  7. r.callStart = false;
  8. synchronized (r.stats.getBatteryStats()) {
  9. r.stats.startRunningLocked();
  10. }
  11. String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
  12. if (error != null) {
  13. return new ComponentName("!!", error);
  14. }
  15. if (r.startRequested && addToStarting) {
  16. boolean first = smap.mStartingBackground.size() == 0;
  17. smap.mStartingBackground.add(r);
  18. r.startingBgTimeout = SystemClock.uptimeMillis() + mAm.mConstants.BG_START_TIMEOUT;
  19. if (DEBUG_DELAYED_SERVICE) {
  20. RuntimeException here = new RuntimeException("here");
  21. here.fillInStackTrace();
  22. Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r, here);
  23. } else if (DEBUG_DELAYED_STARTS) {
  24. Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r);
  25. }
  26. if (first) {
  27. smap.rescheduleDelayedStartsLocked();
  28. }
  29. } else if (callerFg || r.fgRequired) {
  30. smap.ensureNotStartingBackgroundLocked(r);
  31. }
  32. return r.name;

代码不是很多,但是里面的信息却不少,我们来分步解析。

3.6.1 bringUpServiceLocked()

Service的start是由函数startServiceInnerLocked完成,而startServiceInnerLocked则是调用bringUpServiceLocked完成Service的start,每次startService都会往ServiceRecord的pendingStarts里填加一项StartItem,即使是被放入Delayed List的Service启动。bringUpServiceLocked做的事就是拉起Service。

  1. if (r.app != null && r.app.thread != null) {
  2. sendServiceArgsLocked(r, execInFg, false);
  3. return null;
  4. }

如果r.app 也就是ServiceRecord 和thread 已经不为null,也就是说该service 已经create,再次调用startService() 函数的时候,会直接调用sendServiceArgsLocked(),这里暂时不介绍,下面会详细说明。

  1. if (!whileRestarting && mRestartingServices.contains(r)) {
  2. // If waiting for a restart, then do nothing.
  3. return null;
  4. }

这里whileRestarting 是传进来的,如果从startService 调用bring up,那么这个值为false,此时不用继续执行,等待restart 处理进来;如果这个值是从restart 调用的bring up,那么这个值为true,也就是进入了restart 流程,会继续往下执行。

  1. if (mRestartingServices.remove(r)) {
  2. clearRestartingIfNeededLocked(r);
  3. }

如果从restart 调用的bring up,上面参数whileRestaring 为true,会继续执行代码。这里如果restart 开始执行,状态就都需要clear,不需要在处于restart 状态。

  1. if (r.delayed) {
  2. if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (bring up): " + r);
  3. getServiceMapLocked(r.userId).mDelayedStartList.remove(r);
  4. r.delayed = false;
  5. }

需要直接start servicce,不在需要delayed 。

  1. if (!isolated) {
  2. app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
  3. if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
  4. + " app=" + app);
  5. if (app != null && app.thread != null) {
  6. try {
  7. app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
  8. realStartServiceLocked(r, app, execInFg);
  9. return null;
  10. } catch (TransactionTooLargeException e) {
  11. throw e;
  12. } catch (RemoteException e) {
  13. Slog.w(TAG, "Exception when starting service " + r.shortName, e);
  14. }
  15. }
  16. } else {
  17. app = r.isolatedProc;
  18. if (WebViewZygote.isMultiprocessEnabled()
  19. && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
  20. hostingType = "webview_service";
  21. }
  22. }

如果service 的ProcessRecord 已经创建了,会直接调用realStartServiceLocked(),进入start service 的最终流程。下面详细解析这个函数。

如果ProcessRecord 还没有创建,那就会跳过上面这一段流程,继续往下执行。

  1. if (app == null && !permissionsReviewRequired) {
  2. if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
  3. hostingType, r.name, false, isolated, false)) == null) {
  4. ...
  5. }
  6. ...
  7. }
  8. ...
  9. if (!mPendingServices.contains(r)) {
  10. mPendingServices.add(r);
  11. }

如果app 为null,也就是service 还没有create,会调用AMS 中startProcessLocked() 创建,这个函数比较长,可以自行跟一下source code,主要是通过startProcessLocked() 创建进程,并加入到mPendingServices,等待attachApplicationLocked后再startService。

  1. if (r.delayedStop) {
  2. // Oh and hey we've already been asked to stop!
  3. r.delayedStop = false;
  4. if (r.startRequested) {
  5. if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
  6. "Applying delayed stop (in bring up): " + r);
  7. stopServiceLocked(r);
  8. }
  9. }

如果之前有stop service 请求,这里会直接stop。

至此,bringUpServiceLocked() 函数解析完,主要确定ProcessRecord 是否创建完成,通过函数realStartServiceLocked()进入启动流程或者通过startProcessLocked()进入创建流程。这个函数的返回值如果不为null,外面会有exception 抛出:

  1. String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
  2. if (error != null) {
  3. return new ComponentName("!!", error);
  4. }

下面对其中碰到的两个函数进行进一步解析:sendServiceArgsLocked 和 realStartServiceLocked。

3.6.1.1 sendServiceArgsLocked()

从代码中可以看到,如果进入bringUpServiceLocked(),发现service 已经create,这个时候会直接进入sendServiceArgsLocked函数,也就是说startService() 剩下来的处理都是在这里。

  1. final int N = r.pendingStarts.size();
  2. if (N == 0) {
  3. return;
  4. }

里面过滤一下,确认有service 需要start。

bumpServiceExecutingLocked(r, execInFg, "start");

这里最后一个参数是引入start 流程,后面还会碰到create

  1. private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
  2. if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING "
  3. + why + " of " + r + " in app " + r.app);
  4. else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING, ">>> EXECUTING "
  5. + why + " of " + r.shortName);
  6. long now = SystemClock.uptimeMillis();
  7. if (r.executeNesting == 0) {
  8. r.executeFg = fg;
  9. ServiceState stracker = r.getTracker();
  10. if (stracker != null) {
  11. stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now);
  12. }
  13. if (r.app != null) {
  14. r.app.executingServices.add(r);
  15. r.app.execServicesFg |= fg;
  16. if (r.app.executingServices.size() == 1) {
  17. scheduleServiceTimeoutLocked(r.app);
  18. }
  19. }
  20. } else if (r.app != null && fg && !r.app.execServicesFg) {
  21. r.app.execServicesFg = true;
  22. scheduleServiceTimeoutLocked(r.app);
  23. }
  24. r.executeFg |= fg;
  25. r.executeNesting++;
  26. r.executingStart = now;
  27. }

上面bringUpServiceLocked() 最开始的时候说过,就是将DelayList 中的service 一个一个的拉起。

在这里会拉起一个timeout,一般的后台服务,默认是20秒。如果被触发,那么就只有ANR 等待了。当然,如果状态正常,这个ANR 的schedule 是需要取消的,下面会分析到。

如果等待的是一个前台服务:

  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. }

会拉起一个前台服务的timeout,默认时间为 5 秒(在Android P 时改成10秒,一直延续到R)。如果被触发,那么就只有ANR 等待了。

如果该service 还没有进入fg,则会立即产生个 ANR 的schedule(炸弹),后面紧接着需要进入start 流程,即会调用service 的onStartCommand。也就是要求一个等待的service 在以fg service 启动时,必须要在5 秒内调用startForeground,这里就通过代码的方式回答了第一节中官方的一段话。而也是在startForeground调用的时候才会拆除之前埋下的ANR炸弹(5s timeout)。详细看接口startForeground。

继续分析,接着会创建一个ServiceStartArgs:

args.add(new ServiceStartArgs(si.taskRemoved, si.id, flags, si.intent));

为了后面的启动:

r.app.thread.scheduleServiceArgs(r, slice);

app.thread是Service所在进程的IApplicationThread Binder对象,用于AMS的SystemServer进程到Client App端的跨进程调用,IApplicationThread的实现是在ActivityThread的内部类ApplicationThread,AMS -> ActivityThread的调用通过IApplicationThread,ActivityThread -> AMS的调用就是ActivityManagerNative,这样就打通了一条从AMS到ActivityThread的跨进程调用之路。

scheduleServiceArgs在ActivityThread里的对应就是ActivityThread.handleServiceArgs,这就执行到了我们所熟悉的onStartCommand。

  1. private void handleServiceArgs(ServiceArgsData data) {
  2. Service s = mServices.get(data.token);
  3. if (s != null) {
  4. try {
  5. ...
  6. int res;
  7. if (!data.taskRemoved) {
  8. res = s.onStartCommand(data.args, data.flags, data.startId);
  9. } else {
  10. s.onTaskRemoved(data.args);
  11. res = Service.START_TASK_REMOVED_COMPLETE;
  12. }
  13. QueuedWork.waitToFinish();
  14. try {
  15. ActivityManager.getService().serviceDoneExecuting(
  16. data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
  17. } catch (RemoteException e) {
  18. throw e.rethrowFromSystemServer();
  19. }
  20. } catch (Exception e) {
  21. ...
  22. }
  23. }
  24. }

onStartCommand后会调用AMS.serviceDoneExecuting 进行收尾工作,也是在这里取消了ANR 的schedule,拆除了这个ANR 炸弹(20s timeout)

至此可以解释两个我们对于Service的认知:

  • 每次startService,都会对应一次onStartCommand,就算Service已经onCreate成功。
  • Service的回调函数都是在主线程,这个和ApplicationThread这个Binder Client的执行线程一致。

sendServiceArgsLocked之后,pendingStarts里的StartItem就被加入到了deliveredStarts里,等待后续stopService或者Service restart的时候用。

3.6.1.2 realStartServiceLocked()

  1. private final void realStartServiceLocked(ServiceRecord r,
  2. ProcessRecord app, boolean execInFg) throws RemoteException {
  3. if (app.thread == null) {
  4. throw new RemoteException();
  5. }
  6. if (DEBUG_MU)
  7. Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
  8. + ", ProcessRecord.uid = " + app.uid);
  9. r.app = app;
  10. r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
  11. // create service
  12. final boolean newService = app.services.add(r);
  13. bumpServiceExecutingLocked(r, execInFg, "create");
  14. mAm.updateLruProcessLocked(app, false, null);
  15. updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
  16. mAm.updateOomAdjLocked();
  17. boolean created = false;
  18. try {
  19. ...
  20. ...
  21. app.thread.scheduleCreateService(r, r.serviceInfo,
  22. mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
  23. app.repProcState);
  24. r.postNotification();
  25. created = true;
  26. } catch (DeadObjectException e) {
  27. Slog.w(TAG, "Application dead when creating service " + r);
  28. mAm.appDiedLocked(app);
  29. throw e;
  30. } finally {
  31. ...
  32. }
  33. if (r.whitelistManager) {
  34. app.whitelistManager = true;
  35. }
  36. // bind service
  37. requestServiceBindingsLocked(r, execInFg);
  38. updateServiceClientActivitiesLocked(app, null, true);
  39. ...
  40. // start service
  41. sendServiceArgsLocked(r, execInFg, true);
  42. ...
  43. }

上面sendServiceArgsLocked() 是在service 已经被创建的情况下触发,这里是第一次start service时候,此时service 还没有被create。此时会调用 bumpServiceExecutingLocked(r, execInFg, "create");

接着:

  1. app.thread.scheduleCreateService(r, r.serviceInfo,
  2. mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
  3. app.repProcState);

这里的app.thread 在上面 3.6.1.1 中已经解释过。这里主要是调用了scheduleCreateService(),最终触发的是我们熟悉的onCreate()。

bindService() 最后也会进入这个函数,会通过:

  1. requestServiceBindingsLocked(r, execInFg);
  2. updateServiceClientActivitiesLocked(app, null, true);

详细看 Android service 启动篇之 bindService

接着:

sendServiceArgsLocked(r, execInFg, true);

同样这里也会有这个调用,通过 3.6.1.1 我么知道,这里会拉起一个timeout,最终调用的熟悉的onStartCommand()。

注意:

bind service 的时候也会进入该函数,但是 3.5节中变量是没有赋值,所以最终进入函数也会return。

详细看 Android service 启动篇之 bindService

至此,startService 的整个过程大概分析完成。

4、总结

  • SDK 版本L 以上的应用,不能隐式启动service,必须指定package 或者 component。
  • 启动后台服务必须满足一定的条件
  • bringUpServiceLocked 函数是最终会将startService 一个一个拉起来
  • startService 有可能引发炸弹,要求:
    • onCreate、onStartCommand 需要在20s 内处理完成
    • 如果是前台service,需要在onStartCommand中调用startForeground 拆除ANR炸弹

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

闽ICP备14008679号