Android service 启动篇之 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 中:

   @Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, false, mUser);
    }

    @Override
    public ComponentName startForegroundService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, true, mUser);
    }
    @Override
    public ComponentName startServiceAsUser(Intent service, UserHandle user) {
        return startServiceCommon(service, false, user);
    }

    @Override
    public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) {
        return startServiceCommon(service, true, user);
    }

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

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

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

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

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

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

    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), requireForeground,
                            getOpPackageName(), user.getIdentifier());
            if (cn != null) {
                if (cn.getPackageName().equals("!")) {
                    throw new SecurityException(
                            "Not allowed to start service " + service
                            + " without permission " + cn.getClassName());
                } else if (cn.getPackageName().equals("!!")) {
                    throw new SecurityException(
                            "Unable to start service " + service
                            + ": " + cn.getClassName());
                } else if (cn.getPackageName().equals("?")) {
                    throw new IllegalStateException(
                            "Not allowed to start service " + service + ": " + cn.getClassName());
                }
            }
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

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

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

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

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

总结:

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 了:

    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, String callingPackage, int userId)
            throws TransactionTooLargeException {

        ...
        ...

        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res;
            try {
                res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return res;
        }
    }

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

3. ActiveServices startServiceLocked()

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

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

        final boolean callerFg;
        if (caller != null) {
            final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
            if (callerApp == null) {
                throw new SecurityException(
                        "Unable to find app for caller " + caller
                        + " (pid=" + callingPid
                        + ") when starting service " + service);
            }
            callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
        } else {
            callerFg = true;
        }

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

3.2 retrieveServiceLocked()

        ServiceLookupResult res =
            retrieveServiceLocked(service, resolvedType, callingPackage,
                    callingPid, callingUid, userId, true, callerFg, false);
        if (res == null) {
            return null;
        }
        if (res.record == null) {
            return new ComponentName("!", res.permission != null
                    ? res.permission : "private to package");
        }

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

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

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

        ComponentName name = new ComponentName(
                sInfo.applicationInfo.packageName, sInfo.name);
        if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
            if (isBindExternal) {
                if (!sInfo.exported) {
                    throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
                            " is not exported");
                }
                if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
                    throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
                            " is not an isolatedProcess");
                }
                // Run the service under the calling package's application.
                ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo(
                        callingPackage, ActivityManagerService.STOCK_PM_FLAGS, userId);
                if (aInfo == null) {
                    throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " +
                            "could not resolve client package " + callingPackage);
                }
                sInfo = new ServiceInfo(sInfo);
                sInfo.applicationInfo = new ApplicationInfo(sInfo.applicationInfo);
                sInfo.applicationInfo.packageName = aInfo.packageName;
                sInfo.applicationInfo.uid = aInfo.uid;
                name = new ComponentName(aInfo.packageName, name.getClassName());
                service.setComponent(name);
            } else {
                throw new SecurityException("BIND_EXTERNAL_SERVICE required for " +
                        name);
            }
  • 会重新生成一个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

        if (fgRequired) {
            final int mode = mAm.getAppOpsManager().checkOpNoThrow(
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
            switch (mode) {
                case AppOpsManager.MODE_ALLOWED:
                case AppOpsManager.MODE_DEFAULT:
                    // All okay.
                    break;
                case AppOpsManager.MODE_IGNORED:
                    // Not allowed, fall back to normal start service, failing siliently
                    // if background check restricts that.
                    Slog.w(TAG, "startForegroundService not allowed due to app op: service "
                            + service + " to " + r.shortInstanceName
                            + " from pid=" + callingPid + " uid=" + callingUid
                            + " pkg=" + callingPackage);
                    fgRequired = false;
                    forceSilentAbort = true;
                    break;
                default:
                    return new ComponentName("!!", "foreground not allowed as per app op");
            }
        }

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

3.4 getAppStartModeLocked()

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

                final int startMode = (alwaysRestrict)
                        ? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
                        : appServicesRestrictedInBackgroundLocked(uid, packageName,
                                packageTargetSdk);

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

    int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
        // Persistent app?
        if (mPackageManagerInt.isPackagePersistent(packageName)) {
            if (DEBUG_BACKGROUND_CHECK) {
                Slog.i(TAG, "App " + uid + "/" + packageName
                        + " is persistent; not restricted in background");
            }
            return ActivityManager.APP_START_MODE_NORMAL;
        }

        // Non-persistent but background whitelisted?
        if (uidOnBackgroundWhitelist(uid)) {
            if (DEBUG_BACKGROUND_CHECK) {
                Slog.i(TAG, "App " + uid + "/" + packageName
                        + " on background whitelist; not restricted in background");
            }
            return ActivityManager.APP_START_MODE_NORMAL;
        }

        // Is this app on the battery whitelist?
        if (isOnDeviceIdleWhitelistLocked(uid)) {
            if (DEBUG_BACKGROUND_CHECK) {
                Slog.i(TAG, "App " + uid + "/" + packageName
                        + " on idle whitelist; not restricted in background");
            }
            return ActivityManager.APP_START_MODE_NORMAL;
        }

        // None of the service-policy criteria apply, so we apply the common criteria
        return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
    }

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

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

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

3.4.2 appRestrictedInBackgroundLocked()

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

    int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
        // Apps that target O+ are always subject to background check
        if (packageTargetSdk >= Build.VERSION_CODES.O) {
            if (DEBUG_BACKGROUND_CHECK) {
                Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted");
            }
            return ActivityManager.APP_START_MODE_DELAYED_RIGID;
        }
        // ...and legacy apps get an AppOp check
        int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
                uid, packageName);
        if (DEBUG_BACKGROUND_CHECK) {
            Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " bg appop " + appop);
        }
        switch (appop) {
            case AppOpsManager.MODE_ALLOWED:
                return ActivityManager.APP_START_MODE_NORMAL;
            case AppOpsManager.MODE_IGNORED:
                return ActivityManager.APP_START_MODE_DELAYED;
            default:
                return ActivityManager.APP_START_MODE_DELAYED_RIGID;
        }
    

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

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

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

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

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

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

3.5 一些变量的赋值

        if (r.appInfo.targetSdkVersion < Build.VERSION_CODES.O && fgRequired) {
            if (DEBUG_BACKGROUND_CHECK || DEBUG_FOREGROUND_SERVICE) {
                Slog.i(TAG, "startForegroundService() but host targets "
                        + r.appInfo.targetSdkVersion + " - not requiring startForeground()");
            }
            fgRequired = false;
        }

        ...

        r.lastActivity = SystemClock.uptimeMillis();
        r.startRequested = true;
        r.delayedStop = false;
        r.fgRequired = fgRequired;
        r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                service, neededGrants, callingUid));

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

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

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

3.6 startServiceInnerLocked()

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

    ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
        ServiceState stracker = r.getTracker();
        if (stracker != null) {
            stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
        }
        r.callStart = false;
        synchronized (r.stats.getBatteryStats()) {
            r.stats.startRunningLocked();
        }
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
        if (error != null) {
            return new ComponentName("!!", error);
        }

        if (r.startRequested && addToStarting) {
            boolean first = smap.mStartingBackground.size() == 0;
            smap.mStartingBackground.add(r);
            r.startingBgTimeout = SystemClock.uptimeMillis() + mAm.mConstants.BG_START_TIMEOUT;
            if (DEBUG_DELAYED_SERVICE) {
                RuntimeException here = new RuntimeException("here");
                here.fillInStackTrace();
                Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r, here);
            } else if (DEBUG_DELAYED_STARTS) {
                Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r);
            }
            if (first) {
                smap.rescheduleDelayedStartsLocked();
            }
        } else if (callerFg || r.fgRequired) {
            smap.ensureNotStartingBackgroundLocked(r);
        }

        return r.name;
    

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

3.6.1 bringUpServiceLocked()

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

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

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

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

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

    if (mRestartingServices.remove(r)) {
        clearRestartingIfNeededLocked(r);
    }

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

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

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

    if (!isolated) {
        app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
        if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                    + " app=" + app);
        if (app != null && app.thread != null) {
            try {
                app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                realStartServiceLocked(r, app, execInFg);
                return null;
            } catch (TransactionTooLargeException e) {
                throw e;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting service " + r.shortName, e);
            }
        }
    } else {
        app = r.isolatedProc;
        if (WebViewZygote.isMultiprocessEnabled()
                && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
            hostingType = "webview_service";
        }
    }

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

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

    if (app == null && !permissionsReviewRequired) {
        if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                hostingType, r.name, false, isolated, false)) == null) {
            ...
        }
        ...
    }

    ...

    if (!mPendingServices.contains(r)) {
        mPendingServices.add(r);
    }

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

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

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

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

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

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

3.6.1.1 sendServiceArgsLocked()

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

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

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

bumpServiceExecutingLocked(r, execInFg, "start");

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

    private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING "
                + why + " of " + r + " in app " + r.app);
        else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING, ">>> EXECUTING "
                + why + " of " + r.shortName);
        long now = SystemClock.uptimeMillis();
        if (r.executeNesting == 0) {
            r.executeFg = fg;
            ServiceState stracker = r.getTracker();
            if (stracker != null) {
                stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now);
            }
            if (r.app != null) {
                r.app.executingServices.add(r);
                r.app.execServicesFg |= fg;
                if (r.app.executingServices.size() == 1) {
                    scheduleServiceTimeoutLocked(r.app);
                }
            }
        } else if (r.app != null && fg && !r.app.execServicesFg) {
            r.app.execServicesFg = true;
            scheduleServiceTimeoutLocked(r.app);
        }
        r.executeFg |= fg;
        r.executeNesting++;
        r.executingStart = now;
    }

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

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

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

    if (r.fgRequired && !r.fgWaiting) {
        if (!r.isForeground) {
            if (DEBUG_BACKGROUND_CHECK) {
                Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r);
            }
            scheduleServiceForegroundTransitionTimeoutLocked(r);
        } else {
            if (DEBUG_BACKGROUND_CHECK) {
                Slog.i(TAG, "Service already foreground; no new timeout: " + r);
            }
            r.fgRequired = false;
        }
    }

会拉起一个前台服务的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。

    private void handleServiceArgs(ServiceArgsData data) {
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                ...
                int res;
                if (!data.taskRemoved) {
                    res = s.onStartCommand(data.args, data.flags, data.startId);
                } else {
                    s.onTaskRemoved(data.args);
                    res = Service.START_TASK_REMOVED_COMPLETE;
                }

                QueuedWork.waitToFinish();

                try {
                    ActivityManager.getService().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            } catch (Exception e) {
                ...
            }
        }
    }

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()


    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        if (app.thread == null) {
            throw new RemoteException();
        }
        if (DEBUG_MU)
            Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
                    + ", ProcessRecord.uid = " + app.uid);
        r.app = app;
        r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
// create service
        final boolean newService = app.services.add(r);
        bumpServiceExecutingLocked(r, execInFg, "create");
        mAm.updateLruProcessLocked(app, false, null);
        updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
        mAm.updateOomAdjLocked();

        boolean created = false;
        try {
            ...
            ...

            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
            r.postNotification();
            created = true;
        } catch (DeadObjectException e) {
            Slog.w(TAG, "Application dead when creating service " + r);
            mAm.appDiedLocked(app);
            throw e;
        } finally {
            ...
        }

        if (r.whitelistManager) {
            app.whitelistManager = true;
        }

// bind service
        requestServiceBindingsLocked(r, execInFg);

        updateServiceClientActivitiesLocked(app, null, true);

        ...

// start service
        sendServiceArgsLocked(r, execInFg, true);

        ...
    }

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

接着:

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

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

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

        requestServiceBindingsLocked(r, execInFg);

        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炸弹

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

私房菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值