ActivityManagerService之Service启动过程解析

目录
  • 缘由
  • 启动 Service
  • 宿主进程的启动
  • 宿主进程创建 Service
  • Service 接收参数
  • 结束

缘由

我曾经任职于一家小公司,负责上层一切事务,而公司为了给客户(尤其是小客户)提供开发的便利,会强行去掉一些限制,其中就包括启动 Service 的限制。

本文来分析 Service 的整体启动流程,顺带也会提一提 Service 启动的一些限制。但是,读者请务必自行了解 Service 的启动方式,包括前台 Service 的启动,这些基本知识本文不会过多提及。

启动 Service

Service 的启动最终会调用如下方法

// ContextImpl.java
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
        UserHandle user) {
    try {
        // 从 Android 5.0 ,必须使用显式 Intent 启动 Service
        validateServiceIntent(service);
        service.prepareToLeaveProcess(this);
        // 通过 AMS 启动 Service
        ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service,
                service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
                getOpPackageName(), getAttributionTag(), user.getIdentifier());
        if (cn != null) {
            // ... 处理异常结果 ...
        }
        // 成功启动后,返回已启动的 Service 组件名
        return cn;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

Service 最终是通过 AMS 进行启动的,一旦启动成功,会返回 Service 的组件名,通过这个返回结果,可以判断 Service 是否启动成功,这一点往往被很多开发者忽略。

AMS 最终会调用 ActiveService#startServiceLocked() 来启动 Service

ActivityManagerService 把 Service 的所有事务都交给 ActiveServices 处理。

// ActiveServices.java
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
        int callingPid, int callingUid, boolean fgRequired,
        String callingPackage, @Nullable String callingFeatureId, final int userId,
        boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken)
        throws TransactionTooLargeException {
    // 调用方是否处于前台
    final boolean callerFg;
    if (caller != null) {
        final ProcessRecord callerApp = mAm.getRecordForAppLOSP(caller);
        if (callerApp == null) {
            throw new SecurityException(
                    "Unable to find app for caller " + caller
                    + " (pid=" + callingPid
                    + ") when starting service " + service);
        }
        callerFg = callerApp.mState.getSetSchedGroup() != ProcessList.SCHED_GROUP_BACKGROUND;
    } else {
        callerFg = true;
    }
    // 1. 查询 Service
    // 其实这是是从缓存获取 ServiceRecord,或者建立 ServiceRecord 并缓存
    ServiceLookupResult res =
        retrieveServiceLocked(service, null, resolvedType, callingPackage,
                callingPid, callingUid, userId, true, callerFg, false, false);
    if (res == null) {
        return null;
    }
    if (res.record == null) {
        return new ComponentName("!", res.permission != null
                ? res.permission : "private to package");
    }
    // 从查询的结果中获取 Service 记录
    ServiceRecord r = res.record;
    // 2. 对 Service 启动施加限制
    // ...
    if (forcedStandby || (!r.startRequested && !fgRequired)) {
        // Service 所在的进程处于后台,是无法启动 Service
        final int allowed = mAm.getAppStartModeLOSP(r.appInfo.uid, r.packageName,
                r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);
        if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
            Slog.w(TAG, "Background start not allowed: service "
                    + service + " to " + r.shortInstanceName
                    + " from pid=" + callingPid + " uid=" + callingUid
                    + " pkg=" + callingPackage + " startFg?=" + fgRequired);
            // ...
            UidRecord uidRec = mAm.mProcessList.getUidRecordLOSP(r.appInfo.uid);
            return new ComponentName("?", "app is in background uid " + uidRec);
        }
    }
    // ...
    // 3. 进入下一步 Service 启动过程
    return startServiceInnerLocked(r, service, callingUid, callingPid, fgRequired, callerFg,
            allowBackgroundActivityStarts, backgroundActivityStartsToken);
}

首先为 Service 在服务端建立一个记录 ServiceRecord 并缓存起来,这个过程比较简单,读者自行分析。

然后,在 Service 启动之前,施加一些限制,代码中展示了一段后台 app 无法启动 Service 的限制,但是也省略了其他限制的代码,有兴趣的读者可以自行分析。

本文旨在分析 Service 的启动流程,对于一些小细节,限于篇幅原因,就不细致分析,请读者自行研读代码。

最后进入下一步的启动的流程

Service 的启动,会调用好几个类似的函数,但是每一个函数最做了一部分功能,这样就可以把一个庞大的函数分割为几个小的函数,代码阅读性更高,我们要学习这种做法。

private ComponentName startServiceInnerLocked(ServiceRecord r, Intent service,
        int callingUid, int callingPid, boolean fgRequired, boolean callerFg,
        boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken)
        throws TransactionTooLargeException {
    // ...
    // 1. 更新 ServiceRecord 数据
    r.lastActivity = SystemClock.uptimeMillis();
    r.startRequested = true;
    r.delayedStop = false;
    r.fgRequired = fgRequired;
    // 保存即将发送给 Service 的参数
    r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
            service, neededGrants, callingUid));
    // 授予 app 启动前台 Service 的 app op 权限
    if (fgRequired) {
        // ...
    }
    // 下面一段,确定 addToStarting 的值
    final ServiceMap smap = getServiceMapLocked(r.userId);
    boolean addToStarting = false;
    // 注意这里的判断条件
    // !callerFg 表示调用方不处于前台
    // !fgRequired 表示启动的是 非前台Service
    // r.app == null 表示 Service 还没有启动过
    if (!callerFg && !fgRequired && r.app == null
            && mAm.mUserController.hasStartedUserState(r.userId)) {
        ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid);
        if (proc == null || proc.mState.getCurProcState() > PROCESS_STATE_RECEIVER) {
            // app 没有创建,或者已经创建,但是在后台没有运行任何四大组件
            // ...
            // 延时Service不在这里启动,直接返回
            if (r.delayed) {
                return r.name;
            }
            // 如果目前要启动非前台Service超时过了最大数量的,那么当前这个Service,要设置为延时 Service
            // 而延时 Service 不在这里启动,因此直接返回
            if (smap.mStartingBackground.size() >= mMaxStartingBackground) {
                // Something else is starting, delay!
                Slog.i(TAG_SERVICE, "Delaying start of: " + r);
                smap.mDelayedStartList.add(r);
                r.delayed = true;
                return r.name;
            }
            addToStarting = true;
        } else if (proc.mState.getCurProcState() >= ActivityManager.PROCESS_STATE_SERVICE) {
            // app 处于后台,但是只运行了 service 和 receiver
            addToStarting = true;
        }
    }
    // allowBackgroundActivityStarts 目前为 false
    if (allowBackgroundActivityStarts) {
        r.allowBgActivityStartsOnServiceStart(backgroundActivityStartsToken);
    }
    // 2. 继续下一阶段的启动流程
    ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    return cmp;
}

现在没有 Service 的启动限制了,是时候正式启动了,首先更新 ServiceRecord 数据,这里要注意,使用 ServiceRecord#pendingStarts 保存了要发送给 Service 的参数。

接下来有一段关于延时 Service 以及监听 后台Service 相关的代码,这是为了优化 Service 启动过多的情况,而做出的一个优化。对于系统优化的开发者,你可能要重点关注一下,本文先不管这个优化功能。

然后继续看下一步的启动流程

// ActiveServices.java
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
        boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
    synchronized (mAm.mProcessStats.mLock) {
        final ServiceState stracker = r.getTracker();
        if (stracker != null) {
            stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
        }
    }
    // 是否调用 onStart()
    r.callStart = false;
    final int uid = r.appInfo.uid;
    final String packageName = r.name.getPackageName();
    final String serviceName = r.name.getClassName();
    FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, uid, packageName,
            serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__START);
    mAm.mBatteryStatsService.noteServiceStartRunning(uid, packageName, serviceName);
    // 1. 拉起 Service
    String error = bringUpServiceLocked(r, service.getFlags(), callerFg,
            false /* whileRestarting */,
            false /* permissionsReviewRequired */,
            false /* packageFrozen */,
            true /* enqueueOomAdj */);
    /* Will be a no-op if nothing pending */
    mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
    if (error != null) {
        return new ComponentName("!!", error);
    }
    // 优化后台Service启动过多的情况
    if (r.startRequested && addToStarting) {
        boolean first = smap.mStartingBackground.size() == 0;
        smap.mStartingBackground.add(r);
        // 设置一个超时时间
        r.startingBgTimeout = SystemClock.uptimeMillis() + mAm.mConstants.BG_START_TIMEOUT;
        if (first) {
            smap.rescheduleDelayedStartsLocked();
        }
    } else if (callerFg || r.fgRequired) {
        smap.ensureNotStartingBackgroundLocked(r);
    }
    return r.name;
}

很简单,这一步主要是通过 bringUpServiceLocked() 拉起 Service

private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
        boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen,
        boolean enqueueOomAdj)
        throws TransactionTooLargeException {
    // 1. 如果 Service 已经启动过一次,直接发送发送参数给 Service 即可
    // 因为只有 Service 启动过,r.app 才不为 null
    if (r.app != null && r.app.getThread() != null) {
        sendServiceArgsLocked(r, execInFg, false);
        return null;
    }
    // ...
    final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
    final String procName = r.processName;
    HostingRecord hostingRecord = new HostingRecord("service", r.instanceName);
    ProcessRecord app;
    if (!isolated) {
        // 1. Service 进程存在,但是 Service 还没有启动过,那么通知宿主进程启动 Service
        // 由于前面已经判断过 r.app 不为 null 的情况,所以这里处理的就是 Service 没有启动过的情况
        app = mAm.getProcessRecordLocked(procName, r.appInfo.uid);
        if (app != null) { // 进程粗在
            final IApplicationThread thread = app.getThread();
            final int pid = app.getPid();
            final UidRecord uidRecord = app.getUidRecord();
            if (thread != null) { // attach application 已经完成
                try {
                    app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode,
                            mAm.mProcessStats);
                    realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg,
                            enqueueOomAdj);
                    return null;
                }
                // ...
            }
        }
    } else {
        // ...
    }
    // 3. Service 所在的进程没有运行,那么要 fork 一个进程
    if (app == null && !permissionsReviewRequired && !packageFrozen) {
        // TODO (chriswailes): Change the Zygote policy flags based on if the launch-for-service
        //  was initiated from a notification tap or not.
        if ((app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated)) == null) {
            String msg = "Unable to launch app "
                    + r.appInfo.packageName + "/"
                    + r.appInfo.uid + " for service "
                    + r.intent.getIntent() + ": process is bad";
            Slog.w(TAG, msg);
            bringDownServiceLocked(r, enqueueOomAdj);
            return msg;
        }
        if (isolated) {
            r.isolatedProc = app;
        }
    }
    // 对于前台 Service 的 app, 暂时添加到省电模式白名单中
    if (r.fgRequired) {
        mAm.tempAllowlistUidLocked(r.appInfo.uid,
                SERVICE_START_FOREGROUND_TIMEOUT, REASON_SERVICE_LAUNCH,
                "fg-service-launch",
                TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
                r.mRecentCallingUid);
    }
    // 3.1 mPendingServices 保存正在等待进程起来的 Service
    // 进程起来后,执行 attach application 过程时,会自动启动这个 Service
    if (!mPendingServices.contains(r)) {
        mPendingServices.add(r);
    }
    if (r.delayedStop) {
        // ...
    }
    return null;
}

拉起一个 Service 要分好几种情况

  • 如果 Service 已经启动过,那么直接发送参数给 Service 去执行任务即可。这是最简单的一种情况。
  • 如果 Service 没有启动过,但是 Service 的宿主进程是存在的,那么通知宿主进程创建 Service,然后发送参数给它。
  • 如果 Service 的宿主进程不存在,那么得先 fork 一个进程,然后用 mPendingServices 保存这个等待进程启动的 Service。当进程起来后,在执行 attach application 的过程中,AMS 会自动完成 Service 的启动流程。

第3种情况,明显是包含前面两种情况,因此下面直接分析这个过程。

宿主进程的启动

Service 的宿主进程起来后,在执行 attach application 的过程中,AMS 会自动启动那些等待进程起来的 Service,如下

    private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
        // ...
        try {
            // ...
            // 进程初始化
            if (app.getIsolatedEntryPoint() != null) {
                // This is an isolated process which should just call an entry point instead of
                // being bound to an application.
                thread.runIsolatedEntryPoint(
                        app.getIsolatedEntryPoint(), app.getIsolatedEntryPointArgs());
            } else if (instr2 != null) {
                thread.bindApplication(processName, appInfo, providerList,
                        instr2.mClass,
                        profilerInfo, instr2.mArguments,
                        instr2.mWatcher,
                        instr2.mUiAutomationConnection, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.isPersistent(),
                        new Configuration(app.getWindowProcessController().getConfiguration()),
                        app.getCompat(), getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial, autofillOptions, contentCaptureOptions,
                        app.getDisabledCompatChanges(), serializedSystemFontMap);
            } else {
                thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,
                        null, null, null, testMode,
                        mBinderTransactionTrackingEnabled, enableTrackAllocation,
                        isRestrictedBackupMode || !normalMode, app.isPersistent(),
                        new Configuration(app.getWindowProcessController().getConfiguration()),
                        app.getCompat(), getCommonServicesLocked(app.isolated),
                        mCoreSettingsObserver.getCoreSettingsLocked(),
                        buildSerial, autofillOptions, contentCaptureOptions,
                        app.getDisabledCompatChanges(), serializedSystemFontMap);
            }
            // ...
        } catch (Exception e) {
            // ...
            return false;
        }
        // ...
        if (!badApp) {
            try {
                // 启动等待进程的 Service
                didSomething |= mServices.attachApplicationLocked(app, processName);
                checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked");
            }
        }
        // ...
        return true;
    }

最终调用 ActiveServices#attachApplicationLocked() 完成 Service 的启动

// ActiveServices.java
boolean attachApplicationLocked(ProcessRecord proc, String processName)
        throws RemoteException {
    boolean didSomething = false;
    // mPendingServices 保存的就是那些等待进程起来的 Service
    if (mPendingServices.size() > 0) {
        ServiceRecord sr = null;
        try {
            // 遍历 mPendingServices ,启动属于该进程的所有 Service
            for (int i=0; i<mPendingServices.size(); i++) {
                sr = mPendingServices.get(i);
                // 过滤掉不属于这个进程的 Service
                if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
                        || !processName.equals(sr.processName))) {
                    continue;
                }
                final IApplicationThread thread = proc.getThread();
                final int pid = proc.getPid();
                final UidRecord uidRecord = proc.getUidRecord();
                mPendingServices.remove(i);
                i--;
                proc.addPackage(sr.appInfo.packageName, sr.appInfo.longVersionCode,
                        mAm.mProcessStats);
                // 启动 Service
                realStartServiceLocked(sr, proc, thread, pid, uidRecord, sr.createdFromFg,
                        true);
                didSomething = true;
                if (!isServiceNeededLocked(sr, false, false)) {
                    bringDownServiceLocked(sr, true);
                }
                mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
            }
        } catch (RemoteException e) {
            // ...
        }
    }
    // 处理进程重启而需要重启的 Service
    if (mRestartingServices.size() > 0) {
        // ...
    }
    return didSomething;
}

终于,我们看到了我们想看到的东西,先从 mPendingServices 中过滤掉不属于进程的 Service,然后启动它

// ActiveServices.java
private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
        IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
        boolean enqueueOomAdj) throws RemoteException {
    // ...
    // 注意,r.app 的值才不为 null,也就是 ServiceRecord#app 不为 null
    // 因此可以通过 r.app 判断 Service 是否已经启动过
    r.setProcess(app, thread, pid, uidRecord);
    r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
    final ProcessServiceRecord psr = app.mServices;
    final boolean newService = psr.startService(r);
    // 注意,这里实现了 Service 超时的功能
    bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */);
    mAm.updateLruProcessLocked(app, false, null);
    updateServiceForegroundLocked(psr, /* oomAdj= */ false);
    // Force an immediate oomAdjUpdate, so the client app could be in the correct process state
    // before doing any service related transactions
    mAm.enqueueOomAdjTargetLocked(app);
    mAm.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
    boolean created = false;
    try {
        // ...
        // 1. 通知宿主进程创建Service
        thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                app.mState.getReportedProcState());
        r.postNotification();
        created = true;
        // ...
    } catch (DeadObjectException e) {
        // ...
    } finally {
        if (!created) {
            // ...
        }
    }
    // ...
    // 2. 发送参数给 Service
    sendServiceArgsLocked(r, execInFg, true);
    if (r.delayed) {
        // ...
    }
    if (r.delayedStop) {
        // ...
    }
}

现在,正式与宿主进程交互来启动 Service

  • 首先通过 IApplicationThread#scheduleCreateService() 来通知宿主进程创建 Service。
  • 然后通过 IApplicationThread#scheduleServiceArgs() 把参数发送给宿主进程,宿主进程会把参数发送给 Service 执行任务。

宿主进程创建 Service

宿主进程接收到创建 Service 的指令后,会通过 Handler 发送一个消息,最终调用如下方法

// ActivityThread.java
private void handleCreateService(CreateServiceData data) {
    unscheduleGcIdler();
    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        // 创建 Application,并调用 Application#onCreate()
        Application app = packageInfo.makeApplication(false, mInstrumentation);
        final java.lang.ClassLoader cl;
        if (data.info.splitName != null) {
            cl = packageInfo.getSplitClassLoader(data.info.splitName);
        } else {
            cl = packageInfo.getClassLoader();
        }
        // 创建 Service
        service = packageInfo.getAppFactory()
                .instantiateService(cl, data.info.name, data.intent);
        ContextImpl context = ContextImpl.getImpl(service
                .createServiceBaseContext(this, packageInfo));
        if (data.info.splitName != null) {
            context = (ContextImpl) context.createContextForSplit(data.info.splitName);
        }
        if (data.info.attributionTags != null &amp;&amp; data.info.attributionTags.length &gt; 0) {
            final String attributionTag = data.info.attributionTags[0];
            context = (ContextImpl) context.createAttributionContext(attributionTag);
        }
        context.getResources().addLoaders(
                app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
        context.setOuterContext(service);
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManager.getService());
        // 执行 Service#onCreate()
        service.onCreate();
        mServicesData.put(data.token, data);
        mServices.put(data.token, service);
        // 通知服务端Service创建成功
        try {
            ActivityManager.getService().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } catch (Exception e) {
        // ...
    }
}

宿主继承会先创建 Application,并执行 Application#onCreate(),然后创建 Service,并执行 Service#onCreate(),最后通知服务端 Service 创建成功。这一套流程,简直行云流水。

Service 接收参数

宿主进程接收到服务端发送过来的 Service 启动参数后,最终调用如下方法

// ActivityThread.java
private void handleServiceArgs(ServiceArgsData data) {
    CreateServiceData createData = mServicesData.get(data.token);
    Service s = mServices.get(data.token);
    if (s != null) {
        try {
            if (data.args != null) {
                data.args.setExtrasClassLoader(s.getClassLoader());
                data.args.prepareToEnterProcess(isProtectedComponent(createData.info),
                        s.getAttributionSource());
            }
            int res;
            if (!data.taskRemoved) {
                // 调用 Service#onStartCommand() 接收参数,并执行任务
                // 注意,这里是在主线程
                res = s.onStartCommand(data.args, data.flags, data.startId);
            } else {
                s.onTaskRemoved(data.args);
                res = Service.START_TASK_REMOVED_COMPLETE;
            }
            QueuedWork.waitToFinish();
            try {
                // 把 Service#onStartCommand() 的执行结果反馈给服务端
                // 因为这个结果影响到 Service 重启的方式
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } catch (Exception e) {
            // ...
        }
    }
}

当宿主进程收到 Service 的启动参数后,调用 Service#onStartCommand() 接收参数,并执行任务。

注意,Service#onStartCommand() 是在主线程中执行,其实四大组件的生命周期都是在主线程中执行的,这一点大家要牢记。

然后把 Service#onStartCommand() 的返回结果返回给服务端。这个方法的返回结果很重要,它影响了 Service 重启的方式。这里简单展示下结果的处理流程

// ActiveServices.java
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res,
        boolean enqueueOomAdj) {
    boolean inDestroying = mDestroyingServices.contains(r);
    if (r != null) {
        if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
            // 注意了,ServiceRecord#callStart , 是在 Service 执行完 onStartCommand() 并反馈结果后,才设置为 true
            r.callStart = true;
            switch (res) {
                case Service.START_STICKY_COMPATIBILITY:
                // Service 会重启,但是不会收到原来的启动参数
                case Service.START_STICKY: {
                    // 注意最后一个参数为 true
                    // 它表示从已发送的参数 Service#deliveredStarts 中移除此次执行的参数
                    // 因为Service重启时,不需要这个参数
                    r.findDeliveredStart(startId, false, true);
                    // Don't stop if killed.
                    r.stopIfKilled = false;
                    break;
                }
                case Service.START_NOT_STICKY: {
                    // ...
                    break;
                }
                // Service 会重启,并且也会收到原来的启动参数
                case Service.START_REDELIVER_INTENT: {
                    // 注意最后一个参数 true
                    // 它表示不需要从已发送的参数 Service#deliveredStarts 中移除此次执行的参数
                    // 因此Service重启时,需要这个参数
                    ServiceRecord.StartItem si = r.findDeliveredStart(startId, false, false);
                    if (si != null) {
                        si.deliveryCount = 0;
                        si.doneExecutingCount++;
                        // Don't stop if killed.
                        r.stopIfKilled = true;
                    }
                    break;
                }
                // ...
            }
            if (res == Service.START_STICKY_COMPATIBILITY) {
                r.callStart = false;
            }
        } else if (type == ActivityThread.SERVICE_DONE_EXECUTING_STOP) {
            // ...
        }
        final long origId = Binder.clearCallingIdentity();
        // 主要更新 oom ajd
        serviceDoneExecutingLocked(r, inDestroying, inDestroying, enqueueOomAdj);
        Binder.restoreCallingIdentity(origId);
    }
}

这里就涉及到 Service 的基本功,上面展示了两个返回值的处理过程,这两个返回值都表示,由于内存不足而杀掉的 Service,当内存充足时,会重启 Service,但是一个返回值需要再次发送之前的启动参数,而另外一个返回值不需要。

对于需要发送参数的返回值,不需要从 Service#deliveredStart 列表中删除已经发送的参数,因为 Service 重启时,需要发送它。相反,就需要从列表中删除已经发送的参数,因为 Service 重启不再需要它了。

结束

本文完整了分析了 Service 的启动流程,这个是最基本的,并且需要掌握的。

本文还顺带提了一下 Service 启动的限制,对于一个专业的开发者来说,遵守这些限制即可,但是对于系统开发者来说,你可能想 去掉/修改/增加 一些限制,那么你得自己研究下。

其实本文还遗留了一个很值得探讨的话题,那就是优化大量Service启动,对于系统优化的开发者来说,应该是必须要掌握的,后面如果有兴趣了,也有时间了,我会补一篇文章,更多关于ActivityManagerService Service启动的资料请关注我们其它相关文章!

(0)

相关推荐

  • android Service基础(启动服务与绑定服务)

         Service是Android中一个类,它是Android 四大组件之一,使用Service可以在后台执行耗时的操作(注意需另启子线程),其中Service并不与用户产生UI交互.其他的应用组件可以启动Service,即便用户切换了其他应用,启动的Service仍可在后台运行.一个组件可以与Service绑定并与之交互,甚至是跨进程通信.通常情况下Service可以在后台执行网络请求.播放音乐.执行文件读写操作或者与contentprovider交互等.   本文主要讲述service

  • Android Service启动流程刨析

    强调一下阅读系统源码,起码要对进程间通信要了解,对binder机制非常非常清楚,binder就是指南针,要不然你会晕头转向:强行阅读,就容易睡着. Service启动先来一张图感受一下 这张图能够说明一个大致的流程,但是服务的启动肯定不是这么简单,但是我们先简单的总结一下,逐渐深入.服务的启动形式有两种,startService()和 binderService(),我们看startService()这一种.startService是ContextWrapper里面的方法. ContextWra

  • Android开发InputManagerService创建与启动流程

    目录 前言 启动流程 创建输入系统 启动输入系统 输入系统就绪 结束 前言 之前写过几篇关于输入系统的文章,但是还没有写完,后来由于工作的变动,这个事情就一直耽搁了.而现在,在工作中,遇到输入系统相关的事情也越来越多,其中有一个非常有意思的需求,因此是时候继续分析 InputManagerService. InputManagerService 系统文章,基于 Android 12 进行分析. 本文将以 IMS 简称 InputManagerService. 启动流程 InputManagerS

  • Android ServiceManager的启动和工作原理

    ServiceManager启动 所有的系统服务都是需要在ServiceManager中进行注册的,而ServiceManager作为一个起始的服务,是通过init.rc来启动的. //system\core\rootdir\init.rc //启动的服务,这里是用的服务名称.服务名称是在对应的rc文件中注册并启动的 start servicemanager 具体的服务信息是在servicemanger.rc命名并定义的 //\frameworks\native\cmds\servicemana

  • Android 10 启动之servicemanager源码解析

    目录 正文 获取服务 注册服务 正文 上一篇文章: Android 10 启动分析之Init篇 (一) 在前文提到,init进程会在在Trigger 为init的Action中,启动servicemanager服务,这篇文章我们就来具体分析一下servicemanager服务,它到底做了哪些事情. servicemanager服务的源码位于/frameworks/native/cmds/servicemanager/service_manager.c,我们将从这个类的入口开始看起. int ma

  • Android ActivityManagerService启动流程详解

    目录 概述 AMS的启动流程 启动流程图 概述 AMS是系统的引导服务,应用进程的启动.切换和调度.四大组件的启动和管理都需要AMS的支持.从这里可以看出AMS的功能会十分的繁多,当然它并不是一个类承担这个重责,它有一些关联类. AMS的启动流程 1:SystemServer#main -> 2:SystemServer#run -> 3:SystemServiceManager#startBootstrapServices 1:首先SystemServer进程运行main函数, main函数

  • ActivityManagerService之Service启动过程解析

    目录 缘由 启动 Service 宿主进程的启动 宿主进程创建 Service Service 接收参数 结束 缘由 我曾经任职于一家小公司,负责上层一切事务,而公司为了给客户(尤其是小客户)提供开发的便利,会强行去掉一些限制,其中就包括启动 Service 的限制. 本文来分析 Service 的整体启动流程,顺带也会提一提 Service 启动的一些限制.但是,读者请务必自行了解 Service 的启动方式,包括前台 Service 的启动,这些基本知识本文不会过多提及. 启动 Servic

  • Android Service启动过程完整分析

    刚开始学习Service的时候以为它是一个线程的封装,也可以执行耗时操作.其实不然,Service是运行在主线程的.直接执行耗时操作是会阻塞主线程的.长时间就直接ANR了. 我们知道Service可以执行一些后台任务,是后台任务不是耗时的任务,后台和耗时是有区别的喔. 这样就很容易想到音乐播放器,天气预报这些应用是要用到Service的.当然如果要在Service中执行耗时操作的话,开个线程就可以了. 关于Service的运行状态有两种,启动状态和绑定状态,两种状态可以一起. 启动一个Servi

  • Java虚拟机启动过程探索

    目录 一.序言 二.Java虚拟机 (一)配置JVM装载环境 (二)命令行参数解析 (三)执行main方法 1.新建JVM实例 2.加载入口类 3.查找main方法 4.执行main方法 三.解析字节码 (一)解释字节码 1.基于栈指令集 2.基于寄存器指令集 (二)编译字节码 1.C1 编译器 2.C2 编译器 3.分层编译 四.小结 一.序言 当我们在编写Java应用的时候,很少会注意Java程序是如何被运行的,如何被操作系统管理和调度的.带着好奇心,探索一下Java虚拟机启动过程. 1.素

  • Spring Boot启动过程完全解析(二)

    上篇给大家介绍了Spring Boot启动过程完全解析(一),大家可以点击参考下 该说refreshContext(context)了,首先是判断context是否是AbstractApplicationContext派生类的实例,之后调用了强转为AbstractApplicationContext类型并调用它的refresh方法.由于AnnotationConfigEmbeddedWebApplicationContext继承自EmbeddedWebApplicationContext,所以会

  • Spring Boot启动过程完全解析(一)

    之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringApplication.run(Application.class, args); --> public static ConfigurableApplicationContext run(Object source, String... args) { return run(new Object[]

  • Spring Boot启动过程全面解析(三)

    我已经很精简了,两篇(Spring Boot启动过程(一).spring Boot启动过程(二))依然没写完,接着来. refreshContext之后的方法是afterRefresh,这名字起的真...好.afterRefresh方法内只调用了callRunners一个方法,这个方法从上下文中获取了所有的ApplicationRunner和CommandLineRunner接口的实现类,并执行这些实现类的run方法.例如Spring Batch的JobLauncherCommandLineRu

  • Spring Boot启动流程断点过程解析

    这篇文章主要介绍了Spring Boot启动流程断点过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 启动入口 跟进run方法 : 一个用来使用默认的配置从特定的源运行SpringApplication的静态帮助类. 这个类有两个重载方法,另一个用来传入多个源.通常,单个参数方法是数组方法的一个特例 创建一个新的SpringApplication实例.这个应用程序上下文会从特定的源加载Beans,这个实例会在调用run方法之前被定制化.

  • 分析Android Activity的启动过程

    分析Android Activity的启动过程 对于Android Activity 的启动过程,我在Android源码中读了好久的源码,以下是我整理出来的Activity启动过程和大家分享下: Activity作为Android的四大组件之一,也是最基本的组件,负责与用户交互的所有功能.Activity的启动过程也并非一件神秘的事情,接下来就简单的从源码的角度分析一下Activity的启动过程. 根Activity一般就是指我们项目中的MainActivity,代表了一个android应用程序

  • Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析

    在前面一篇文章Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路中,介绍了在Android系统中Binder进程间通信机制中的Server角色是如何获得Service Manager远程接口的,即defaultServiceManager函数的实现.Server获得了Service Manager远程接口之后,就要把自己的Service添加到Service Manager中去,然后把自己启动起来,等待Client的请求.

  • Spring Boot启动过程(四)之Spring Boot内嵌Tomcat启动

    之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void createEmbeddedServletContainer() { EmbeddedServletContainer localContainer = this.embeddedServletContainer; ServletContext localServletContext = getServ

随机推荐