Service的启动流程分析

Posted zhenjie_chang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Service的启动流程分析相关的知识,希望对你有一定的参考价值。

我们要启动一个Service服务一般都是在Activity类中直接调用startService(Intent)来启动,而Activity类中并没有相关的方法,我们知道Activity继承自ContextWrapperContextWrapper类中的startService方法直接调用了ContextImplstartService方法。

Service的可以在当前的进程中启动,也可以在新的进程中启动。在新的进程中启动的逻辑和前面分析的Activity的启动流程类似,所以此处我们仅仅分析在当前进程中启动Service的情况。

我们分析Service的启动流程首先从ContextImplstartService方法开始分析。

1. ContextImpl.StartService

public ComponentName startService(Intent service) 
        return startServiceCommon(service, mUser);

private ComponentName startServiceCommon(Intent service, UserHandle user) 
        try 
            validateServiceIntent(service);
            service.prepareToLeaveProcess();
            ComponentName cn = ActivityManagerNative.getDefault().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), getOpPackageName(), user.getIdentifier());
            return cn;
         catch (RemoteException e) 
            throw new RuntimeException("Failure from system", e);
        
    

StartService调用了startServiceCommon方法来启动Service,在startServiceCommon方法中通过进程间通信请求,调用了AMS服务的startService方法。以上代码在应用进程中。通过进程间调用,下面代码逻辑进入系统服务进程中。

2. AMS.startService

public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, String callingPackage, int userId)
            throws TransactionTooLargeException 
	……
        synchronized(this) 
	    //获取调用进程的UID和PID
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
	    //调用startServiceLocked来继续处理service启动的逻辑
            ComponentName res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid, callingPackage, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        
    

这个方法处理也是比较简单,获取调用者的UidPid,调用ActiveServicestartServiceLocked方法来继续处理Service启动的逻辑。

??

3. ActiveService.startServiceLocked

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, String callingPackage, int userId)
            throws TransactionTooLargeException 
        //获取调用程序的进程信息
        if (caller != null) 
            final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
            ……
        

        //调用要启动的service的Intent信息,在PMS服务中查找对应的service,并将它封装在一个ServiceLookupResult对象中
        ServiceLookupResult res =
            retrieveServiceLocked(service, resolvedType, callingPackage,
                    callingPid, callingUid, userId, true, callerFg);
        //得到要启动service的ServiceRecord信息
        ServiceRecord r = res.record;

        final ServiceMap smap = getServiceMap(r.userId);
	……
        //调用startServiceInnerLocked方法继续启动service服务
        return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    
在AMS服务中,每个Service都使用一个ServiceRecord对象来描述,就像Activity一样,每个Activity都用一个ActivityRecord对象描述。然后调用retrieveServiceLocked方法来查找一个和目标Service对应的ServiceRecord,最后将这个ServiceRecord对象封装在ServiceLookupResult对象中。

最后条用startServiceInnerLocked方法来继续启动目标Service

    下面在了解下retrieveServiceLocked方法的处理逻辑。

private ServiceLookupResult retrieveServiceLocked(Intent service,
            String resolvedType, String callingPackage, int callingPid, int callingUid, int userId,
            boolean createIfNeeded, boolean callingFromFg) 
        ServiceRecord r = null;
        //获取当前的用户ID
        userId = mAm.handleIncomingUser(callingPid, callingUid, userId,
                false, ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE, "service", null);
        找到该用户下的保存Service的集合ServiceMap
        ServiceMap smap = getServiceMap(userId);
        final ComponentName comp = service.getComponent();
        //根据component信息从ServiceMap中查找
        if (comp != null) 
            r = smap.mServicesByName.get(comp);
        
        //根据IntentFilter信息从ServiceMap中查找
        if (r == null) 
            Intent.FilterComparison filter = new Intent.FilterComparison(service);
            r = smap.mServicesByIntent.get(filter);
        
//如果ServiceMap中没有找到目标Service的ServiceRecord信息,则从PMS中查找并创建一个ServiceRecord对象,然后保存到ServiceMap中
        if (r == null) 
            try 
                ResolveInfo rInfo =
                    AppGlobals.getPackageManager().resolveService(
                                service, resolvedType,
                                ActivityManagerService.STOCK_PM_FLAGS, userId);
                ServiceInfo sInfo =
                    rInfo != null ? rInfo.serviceInfo : null;
                    ……
                    r = new ServiceRecord(mAm, ss, name, filter, sInfo, callingFromFg, res);
                    res.setService(r);
                    smap.mServicesByName.put(name, r);
                    smap.mServicesByIntent.put(filter, r);
                
             catch (RemoteException ex) 
                // pm is in same process, this will never happen.
            
        
       ……
        return null;
    

retrieveServiceLocked方法用来查找目标ServiceServiceRecord对象,首先从AMS服务中的ServiceMap中查找,ServiceMap中保存了AMS服务中所有激活的Service信息,如果在ServiceMap中没有找到,说明该Service还没有启动。那么就需要从PMS服务中查找目标Service的信息并创建一个ServiceRecord对象。最后把他保存在对应的ServiceMap集合中。

接着看startServiceInnerLocked方法,该方法中处理逻辑直接较简单,直接调用了bringUpServiceLocked方法来处理

4. ActiveService. bringUpServiceLocked

private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting) throws TransactionTooLargeException 

	       final String procName = r.processName;
        ProcessRecord app;

      //判断目标Service所在的进程是否已经存在,如果已经存在则直接调用目标进程启动Service
       app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
            if (app != null && app.thread != null) 
                try 
                    app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                    realStartServiceLocked(r, app, execInFg);
                    return null;
                 
	        ……
            
        //如果目标进程不存在,则需要先创建目标进程,然后再创建Service
        if (app == null) 
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    "service", r.name, false, isolated, false)) == null) 
               ……

这个方法首先根据目标Serivce的进程信息,来查询对应的进程是否存在,如果对应的目标进程已经存在,则通过通知目标进程来启动Service,如果目标进程还没有创建,则电泳AMS服务的startProcessLocked方法启动一个目标进程,然后在启动service。启动目标进程的方法和Activity中启动目标进程的方法类似,此处不再分析。

我们分析在同一个进程中启动Service的逻辑,目标进程肯定已经存在了,不需要新创建进程,所以直接调用realStartServiceLocked方法来启动service服务。

5. ActiveService.realStartServiceLocked

 private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException 
        ……
        //将即将要启动ServiceRecord和一个进程关联起来
        r.app = app;

	   boolean created = false;
        try 
            ……
           //通过进程间通信请求,调用目标进程的scheduleCreateService方法创建一个Service服务
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
            r.postNotification();
            created = true;
         
	……

将即将要启动的ServiceRecord r的成员变量设置为ProcessRecord对象app,表示这个ServiceRecord对象锁描述的Service组件是在app所描述的进程中启动运行的。

Activity的启动过程中描述过:ProcessRecord的成员变量thread是一个ApplicationThreadProxybinder代理对象,它指向了应用程序进程中一个Applicationthreadbinder服务端对象。因此可以通过thread进行进程间通信请求,调用binder服务端ApplicationThreadscheduleCreateService来创建Service

同样,ApplicationThread接收到请求后,最终发送消息CREATE_SERVICEHander来处理,HandlerhandleMessage又调用了ActivityThreadhandleCreateService方法来处理。

6. ActivityThread.handleCreateService

private void handleCreateService(CreateServiceData data) 
        //获取当前应用程序的LoadedApk对象
        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try 
	    //使用当前应用程序的类加载器加载目标service,并创建它的一个对象
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
         catch (Exception e) 
           ……
        

        try 
            //创建并初始化Context对象
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);
	    //创建一个Application对象,用来描述service所在的应用程序信息
            Application app = packageInfo.makeApplication(false, mInstrumentation);
	    //使用上面的信息初始化刚创建的service对象
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            //调用service的onCreate方法
            service.onCreate();
	    //最后将servcie保存在ActivityThread的mServices变量中。
            mServices.put(data.token, service);
            try 
                ActivityManagerNative.getDefault().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
             catch (RemoteException e) 
                // nothing to do.
            
         ……
    

在该方法中首先获得一个LoadedAPK对象,在之前我们曾经分析过,LoadedApk主要用来描述进程中所加载的一个应用程序,里面保存了应用程序的资源路径等信息。

然后使用LoadedApk对象中保存的ClassLoader来加载目标Service类到内存中,并创建一个目标Service的对象。

第三步,创建ContextImpl对象,并初始化,用来当做Service对象的上下文信息,Service对象就可以通过它来访问程序的资源和方法。

第四步,创建一个Application对象,用来描述Service所在应用程序的信息。

第五步,用上面创建的信息,context,Application等信息来初始化刚创建的service对象。

第六步,service对象初始化完成后就调用了它的onCreate方法。这样Service服务就基本上启动起来了


以上是关于Service的启动流程分析的主要内容,如果未能解决你的问题,请参考以下文章

dubbo service启动流程源码分析

建库测序流程

Android12 am命令的使用及实现流程分析

Android12 am命令的使用及实现流程分析

SDS-PAGE相关知识

Android Framework原理 -- service_manager注册服务流程分析