美文网首页系统源码分析Android知识Android开发经验谈
Framework 源码解析知识梳理(5) - startSer

Framework 源码解析知识梳理(5) - startSer

作者: 泽毛 | 来源:发表于2017-06-16 23:01 被阅读556次

    一、前言

    最近在看关于插件化的知识,遇到了如何实现Service插件化的问题,因此,先学习一下Service内部的实现原理,这里面会涉及到应用进程和ActivityManagerService的通信,建议大家先阅读一下之前的这篇文章 Framework 源码解析知识梳理(1) - 应用进程与 AMS 的通信实现,整个Service的启动过程离不开和AMS的通信,从整个宏观上来看,它的模型如下,是不是和我们之前分析的通信过程一模一样:

    二、源码分析

    当我们在Activity/Service/Application中,通过下面这个方法启动Service时:

    public ComponentName startService(Intent service)
    

    与之前分析过的很多源码类似,最终会调用到ContextImpl的同名方法当中,而该方法又会调用内部的startServiceCommon(Intent, UserHandle)


    在该方法中,主要做了两件事:
    • 校验Intent的合法性,对于Android 5.0以后,我们要求Intent中需要制定PackageComponent中至少一个,否则会抛出异常。
    • 通过Binder通信调用到ActivityManagerService的对应方法,关于调用的详细过程可以参考 Framework 源码解析知识梳理(1) - 应用进程与 AMS 的通信实现 中对于应用进程到ActivityManagerService的通信部分分析。

    ActivityManagerService中的实现为:


    这里面,通过mServices变量来去实现Service的启动,而mServices的类型为ActiveServices,它是ActivityManagerService中负责处理Service相关业务的类,无论我们是通过start还是bind方式启动的Service,都是通过它来实现的,它也被称为“应用服务”的管理类。

    从通过ActiveServices调用startServiceLocked,到Service真正被启动的过程如下图所示,下面,我们就来一一分析每一个阶段究竟做了什么:

    我们首先来看入口函数startServiceLocked的内部实现:

        ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
                int callingPid, int callingUid, String callingPackage, final int userId)
                throws TransactionTooLargeException {
    
            final boolean callerFg;
            //caller是调用者进程在AMS的远程代理对象,类型为ApplicationThreadProxy。
            if (caller != null) {
                //获得调用者的进程信息。
                final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
                if (callerApp == null) {
                    throw new SecurityException(
                            "Unable to find app for caller " + caller
                            + " (pid=" + Binder.getCallingPid()
                            + ") when starting service " + service);
                }
                callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
            } else {
                callerFg = true;
            }
    
            //通过传递的信息,解析出要启动的Service,封装在ServiceLookupResult中。
            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");
            }
            //对于每个被启动的Service,它们在AMS端的数据结构为ServiceRecord。
            ServiceRecord r = res.record;
    
            if (!mAm.mUserController.exists(r.userId)) {
                Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId);
                return null;
            }
            //判断是否允许启动该服务。
            if (!r.startRequested) {
                final long token = Binder.clearCallingIdentity();
                try {
                    final int allowed = mAm.checkAllowBackgroundLocked(
                            r.appInfo.uid, r.packageName, callingPid, true);
                    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);
                        return null;
                    }
                } finally {
                    Binder.restoreCallingIdentity(token);
                }
            }
            //是否需要配置相应的权限。
            NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
                    callingUid, r.packageName, service, service.getFlags(), null, r.userId);
    
            if (Build.PERMISSIONS_REVIEW_REQUIRED) {
                if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
                        callingUid, service, callerFg, userId)) {
                    return null;
                }
            }
            //如果该Service正在等待被重新启动,那么移除它。
            if (unscheduleServiceRestartLocked(r, callingUid, false)) {
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "START SERVICE WHILE RESTART PENDING: " + r);
            }
            //给ServiceRecord添加必要的信息。
            r.lastActivity = SystemClock.uptimeMillis();
            r.startRequested = true;
            r.delayedStop = false;
            r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                    service, neededGrants));
            //其它的一些逻辑,与第一次启动无关,就先不分析了。
            return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
        }
    

    简单地来说,就是根据Intent查找需要启动的Service,封装成ServiceRecord对象,初始化其中的关键变量,在这一过程中,加入了一些必要的逻辑判断,最终调用了startServiceInnerLocked方法。

        ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
                boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
            //...
            String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
            //...
            return r.name;
        }
    

    这里面的逻辑我们先忽略掉一部分,只需要知道在正常情况下它会去调用bringUpServiceLocked来启动Service

        private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
                boolean whileRestarting) throws TransactionTooLargeException {
     
            //如果该Service已经启动。
            if (r.app != null && r.app.thread != null) {
                sendServiceArgsLocked(r, execInFg, false);
                return null;
            }
            //如果正在等待被重新启动,那么什么也不做。
            if (!whileRestarting && r.restartDelay > 0) {
                return null;
            }
    
            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent);
    
            //清除等待被重新启动的状态。
            if (mRestartingServices.remove(r)) {
                r.resetRestartCounter();
                clearRestartingIfNeededLocked(r);
            }
    
            //因为我们马上就要启动该Service,因此去掉它的延时属性。
            if (r.delayed) {
                if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (bring up): " + r);
                getServiceMap(r.userId).mDelayedStartList.remove(r);
                r.delayed = false;
            }
    
            //如果该Service所属的用户没有启动,那么调用 bringDownServiceLocked 方法。
            if (mAm.mStartedUsers.get(r.userId) == null) {
                String msg = "Unable to launch app "
                        + r.appInfo.packageName + "/"
                        + r.appInfo.uid + " for service "
                        + r.intent.getIntent() + ": user " + r.userId + " is stopped";
                Slog.w(TAG, msg);
                bringDownServiceLocked(r);
                return msg;
            }
    
            try {
                AppGlobals.getPackageManager().setPackageStoppedState(
                        r.packageName, false, r.userId);
            } catch (RemoteException e) {
            } catch (IllegalArgumentException e) {
                Slog.w(TAG, "Failed trying to unstop package "
                        + r.packageName + ": " + e);
            }
    
            final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
            final String procName = r.processName;
            ProcessRecord app;
            //如果不是运行在独立的进程。
            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);
                //如果该进程已经启动,那么调用realStartServiceLocked方法。
                if (app != null && app.thread != null) {
                    try {
                        app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                        //在Service所属进程已经启动的情况下调用的方法。
                        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;
            }
    
            //如果该Service所对应的进程没有启动,那么首先启动该进程。
            if (app == null) {
                if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                        "service", r.name, false, isolated, false)) == 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);
                    return msg;
                }
                if (isolated) {
                    r.isolatedProc = app;
                }
            }
            //将该ServiceRecord加入到等待的集合当中,等到新的进程启动之后,再去启动它。
            if (!mPendingServices.contains(r)) {
                mPendingServices.add(r);
            }
    
            if (r.delayedStop) {
                r.delayedStop = false;
                if (r.startRequested) {
                    if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
                            "Applying delayed stop (in bring up): " + r);
                    stopServiceLocked(r);
                }
            }
    
            return null;
        }
    

    bringUpServiceLocked的逻辑就比较复杂了,它会根据目标Service及其所属进程的状态,走向不同的分支:

    • 进程已经存在,并且目标Service已经启动:sendServiceArgsLocked
    • 进程已经存在,但是目标Service没有启动:realStartServiceLocked
    • 进程不存在:startProcessLocked,并且将ServiceRecord加入到mPendingServices中,等待进程启动之后再去启动该Service

    下面,我们就来一起分析一下这三种情况。

    2.1 进程已经存在,并且目标 Service 已经启动

    首先来当进程已经存在,且目标Service已经启动时所调用的sendServiceArgsLocked方法:

        private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
                boolean oomAdjusted) throws TransactionTooLargeException {
            final int N = r.pendingStarts.size();
            if (N == 0) {
                return;
            }
    
            while (r.pendingStarts.size() > 0) {
                Exception caughtException = null;
                ServiceRecord.StartItem si;
                try {
                    si = r.pendingStarts.remove(0);
                    if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Sending arguments to: "
                            + r + " " + r.intent + " args=" + si.intent);
                    if (si.intent == null && N > 1) {
                        // If somehow we got a dummy null intent in the middle,
                        // then skip it.  DO NOT skip a null intent when it is
                        // the only one in the list -- this is to support the
                        // onStartCommand(null) case.
                        continue;
                    }
                    si.deliveredTime = SystemClock.uptimeMillis();
                    r.deliveredStarts.add(si);
                    si.deliveryCount++;
                    if (si.neededGrants != null) {
                        mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
                                si.getUriPermissionsLocked());
                    }
                    bumpServiceExecutingLocked(r, execInFg, "start");
                    if (!oomAdjusted) {
                        oomAdjusted = true;
                        mAm.updateOomAdjLocked(r.app);
                    }
                    int flags = 0;
                    if (si.deliveryCount > 1) {
                        flags |= Service.START_FLAG_RETRY;
                    }
                    if (si.doneExecutingCount > 0) {
                        flags |= Service.START_FLAG_REDELIVERY;
                    }
                    r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
                } catch (TransactionTooLargeException e) {
                    if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large: intent="
                            + si.intent);
                    caughtException = e;
                } catch (RemoteException e) {
                    // Remote process gone...  we'll let the normal cleanup take care of this.
                    if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
                    caughtException = e;
                } catch (Exception e) {
                    Slog.w(TAG, "Unexpected exception", e);
                    caughtException = e;
                }
    
                if (caughtException != null) {
                    // Keep nesting count correct
                    final boolean inDestroying = mDestroyingServices.contains(r);
                    serviceDoneExecutingLocked(r, inDestroying, inDestroying);
                    if (caughtException instanceof TransactionTooLargeException) {
                        throw (TransactionTooLargeException)caughtException;
                    }
                    break;
                }
            }
        }
    

    这里面最关键的就是调用了r.app.threadscheduleServiceArgs方法,它其实就是Service所属进程的ApplicationThread对象在AMS端的一个远程代理对象,也就是ApplicationThreadProxy,通过binder通信,它最终会回调到Service所属进程的ApplicationThreadscheduleServiceArgs中:

        private class ApplicationThread extends ApplicationThreadNative {
    
            public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
                int flags ,Intent args) {
                ServiceArgsData s = new ServiceArgsData();
                s.token = token;
                s.taskRemoved = taskRemoved;
                s.startId = startId;
                s.flags = flags;
                s.args = args;
                sendMessage(H.SERVICE_ARGS, s);
            }
       }
    

    sendMessage函数会通过ActivityThread内部的mH对象发送消息到主线程当中,mH实际上是一个Handler的子类,在它的handleMessage回调中,处理H.SERVICE_ARGS这条消息:


    handleServiceArgs方法,就会从mServices去查找对应的Service,调用我们熟悉的onStartCommand方法,至于Service对象是如何被加入到mServices中的,我们在2.2节中进行分析。
        private void handleServiceArgs(ServiceArgsData data) {
            Service s = mServices.get(data.token);
            if (s != null) {
                try {
                    if (data.args != null) {
                        data.args.setExtrasClassLoader(s.getClassLoader());
                        data.args.prepareToEnterProcess();
                    }
                    int res;
                    if (!data.taskRemoved) {
                        //调用 onStartCommand 方法。
                        res = s.onStartCommand(data.args, data.flags, data.startId);
                    } else {
                        s.onTaskRemoved(data.args);
                        res = Service.START_TASK_REMOVED_COMPLETE;
                    }
    
                    QueuedWork.waitToFinish();
    
                    try {
                        //通知AMS启动完成。
                        ActivityManagerNative.getDefault().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
                    } catch (RemoteException e) {
                        // nothing to do.
                    }
                    ensureJitEnabled();
                } catch (Exception e) {
                    if (!mInstrumentation.onException(s, e)) {
                        throw new RuntimeException(
                                "Unable to start service " + s
                                + " with " + data.args + ": " + e.toString(), e);
                    }
                }
            }
        }
    

    这里面,我们会取得onStartCommand的返回值,再通过Binder将该返回值传回到AMS端,在AMSmServicesserviceDoneExecutingLocked中,会根据该返回值来修改ServiceRecord的属性,这也就是我们常说的onStartCommand方法的返回值,会影响到该Services之后的一些行为的原因,关于这些返回值之间的差别,可以查看Service.java的注释,也可以查看网上的一些教程:

    2.2 进程已经存在,但是 Service 没有启动

    下面,我们来看Service没有启动的情况,这里会调用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();
    
            final boolean newService = app.services.add(r);
            bumpServiceExecutingLocked(r, execInFg, "create");
            //更新Service所在进程的oom_adj值。
            mAm.updateLruProcessLocked(app, false, null);
            mAm.updateOomAdjLocked();
    
            boolean created = false;
            try {
                if (LOG_SERVICE_START_STOP) {
                    String nameTerm;
                    int lastPeriod = r.shortName.lastIndexOf('.');
                    nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName;
                    EventLogTags.writeAmCreateService(
                            r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
                }
                synchronized (r.stats.getBatteryStats()) {
                    r.stats.startLaunchedLocked();
                }
                mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
                app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
                //通知应用端创建Service对象。
                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 (!created) {
                    final boolean inDestroying = mDestroyingServices.contains(r);
                    serviceDoneExecutingLocked(r, inDestroying, inDestroying);
                    if (newService) {
                        app.services.remove(r);
                        r.app = null;
                    }
                    if (!inDestroying) {
                        scheduleServiceRestartLocked(r, false);
                    }
                }
            }
            //这里面会遍历 ServiceRecord.bindings 列表,因为我们这里是用startService方式启动的,因此该列表为空,什么也不会调用。
            requestServiceBindingsLocked(r, execInFg);
    
            updateServiceClientActivitiesLocked(app, null, true);
    
            if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
                r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                        null, null));
            }
            //这个在第一种情况中分析过了,会调用 onStartCommand 方法。
            sendServiceArgsLocked(r, execInFg, true);
    
            if (r.delayed) {
                if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (new proc): " + r);
                getServiceMap(r.userId).mDelayedStartList.remove(r);
                r.delayed = false;
            }
    
            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 (from start): " + r);
                    stopServiceLocked(r);
                }
            }
        }
    

    这段逻辑,有三个地方需要注意:

    • app.thread.scheduleCreateService:通过这里会在应用进程创建Service对象
    • requestServiceBindingsLocked:如果是通过bindService方式启动的,那么会去调用它的onBind方法。
    • sendServiceArgsLocked:正如2.1节中所分析,这里最终会触发应用进程的ServiceonStartCommand方法被调用。

    第二点由于我们今天分析的是startService的方式,所以不用在意,而第三点我们之前已经分析过了。所以我们主要关注app.thread.scheduleCreateService这一句,和2.1中的过程类似,它会回调到H中的下面这个消息处理分支当中:


    具体的handleCreateService的处理逻辑如下图所示:
        private void handleCreateService(CreateServiceData data) {
            // If we are getting ready to gc after going to the background, well
            // we are back active so skip it.
            unscheduleGcIdler();
    
            LoadedApk packageInfo = getPackageInfoNoCheck(
                    data.info.applicationInfo, data.compatInfo);
            Service service = null;
            try {
                //根据Service的名字,动态的加载该Service类。
                java.lang.ClassLoader cl = packageInfo.getClassLoader();
                service = (Service) cl.loadClass(data.info.name).newInstance();
            } catch (Exception e) {
                if (!mInstrumentation.onException(service, e)) {
                    throw new RuntimeException(
                        "Unable to instantiate service " + data.info.name
                        + ": " + e.toString(), e);
                }
            }
    
            try {
                if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
                //1.创建ContextImpl对象。
                ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
                //2.将 Service 作为它的成员变量 mOuterContext 。
                context.setOuterContext(service);
                //3.创建Application对象,如果已经创建那么直接返回,否则先调用Application的onCreate方法。
                Application app = packageInfo.makeApplication(false, mInstrumentation);
                //4.调用attach方法,将ContextImpl作为它的成员变量mBase。
                service.attach(context, this, data.info.name, data.token, app,
                        ActivityManagerNative.getDefault());
                //5.调用Service的onCreate方法。
                service.onCreate();
                //6.将该Service对象缓存起来。
                mServices.put(data.token, service);
                try {
                    ActivityManagerNative.getDefault().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                } catch (RemoteException e) {
                    // nothing to do.
                }
            } catch (Exception e) {
                if (!mInstrumentation.onException(service, e)) {
                    throw new RuntimeException(
                        "Unable to create service " + data.info.name
                        + ": " + e.toString(), e);
                }
            }
        }
    

    以上的逻辑分为以下几个部分:

    • 根据Service的名字,通过ClassLoader加载该类,并生成一个Service实例。
    • 创建一个新的ContextImpl对象,并将第一步中创建的Service实例作为它的mOuterContext变量。
    • 创建Application对象,这里会先判断当前进程所对应的Application对象是否已经创建,如果已经创建,那么就直接返回,否则会先创建它,并依次调用它的attachBaseContext(Context context)onCreate()方法。
    • 调用Serviceattach方法,初始化成员变量。
    • 调用ServiceonCreate()方法。
    • 将该Service通过token作为key,缓存在mServices列表当中,之后Service生命周期的回调,都依赖于该列表。

    2.3 进程不存在的情况

    在这种情况下,Service自然也不会存在,我们会走到mAm.startProcessLocked的逻辑,这里会去启动Service所在的进程,它究竟是怎么启动的我们先不去细看,只需要知道它是这个目的就可以了,此外,还需要注意的是它将该ServiceRecord加入到了mPendingServices中。

    对于Service所在的进程,它的入口函数为ActivityThreadmain()方法,在main方法中,会创建一个ApplicationThread对象,并调用它的attach方法:


    而在attach方法中,会调用到AMSattachApplication方法:

    attachApplication方法中,又会去调用内部的attachApplicationLocked

    这里面的逻辑比较长,我们只需要关注下面这句,我们又看到了熟悉的mServices对象:

    ActiveServices中的该方法中,就会去遍历前面谈到的mPendingServices列表,再依次调用realStartServiceLocked方法,至于这个方法做了什么,大家可以回到前面的2.2节去看,这里就不再重复分析了。

    三、小结

    以上就是startService的整个流程,bindService也是类似一个调用过程,其过程并不复杂,本质上还是 Framework 源码解析知识梳理(1) - 应用进程与 AMS 的通信实现 所谈到的通信过程,我们所需要学习的是AMS端和Service所在的应用进程对于Service是如何管理的。

    系统当中的所有Service都是通过AMSmServices变量,也就是ActiveServices类来进行管理的,并且每一个应用进程中的Service都会在AMS端会对应一个ServiceRecord对象,ServiceRecord中维护了应用进程中的Service对象所需要的状态信息。

    并且,无论我们调用多少次startService方法,在应用进程侧都会只存在一个Service的实例,它被存储到ActivityThreadArrayMap类型的mServices变量当中。


    更多文章,欢迎访问我的 Android 知识梳理系列:

    相关文章

      网友评论

        本文标题:Framework 源码解析知识梳理(5) - startSer

        本文链接:https://www.haomeiwen.com/subject/blxpqxtx.html