天天看點

解析Service的啟動過程Service 的啟動過程   

Service 的啟動過程   

       Service 的啟動過程和根 Activity 啟動過程有部分相似的知識點,另外 Service 啟動過 程涉及上下文 Context 的知識點,這裡隻關注流程而不會詳細介紹 Context ,   Service 的啟動過程将分為兩個部分來進行講解,分别是 Contextimpl到Activity ManageService 調用 過程和 ActivityThread 啟動 Service。本文基于Android8.1.0系統分析Service的啟動過程。  

1. Contextlmpl到AMS 的調用過程

       要啟動 Service ,我們會調用 startService 方法 ,它在 Context Wrapper 中實作,代碼如下所示:   frameworks/base/core/java/android/content/ContextWrapper.java  

public class ContextWrapper extends Context {
    Context mBase;
    
    ...

    @Override
    public ComponentName startService(Intent service) {
        return mBase.startService(service);
    }

    ...

}
           

       在startService方法中會 調用 mBase的 startService 方法 , Context 類型的  mBase 對 象具 體指的是什麼呢?在“解析Activity的啟動過程” 中我們分析過, ActivityThread 啟動 Activity 調用如下代碼建立 Activity 的上下文環境:   frameworks/base/core/java/android/app/ActivityThread.java

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        ContextImpl appContext = createBaseContextForActivity(r); // ... 1
        Activity activity = null;

        try {
           
            if (activity != null) {
                ...
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
                ...
            }
            ... 
        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to start activity " + component
                    + ": " + e.toString(), e);
            }
        }

        return activity;
    }
           

       在注釋1處建立上下文對象appContext,并傳入Activity的attach方法中,将Activity與上下文對象appContext關聯起來,這個上下文對象appContext的具體類型是什麼?接下來我們繼續分析createBaseContextForActivity方法,代碼如下所示:

frameworks/base/core/java/android/app/ActivityThread.java

private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {

        ...

        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);

        ...         

        return appContext;
    }
           

       上下文對象appContext的具體類型就是ContextImpl,在Activity的attach方法中将ContextImpl指派給ContextWrapper的成員變量mBase,是以,mBast具體指向就是ContextImpl。接下來分析ContextImpl的startService方法,代碼如下所示:

frameworks/base/core/java/android/app/Contextlmpl.java  

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

    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()); // ... 1
            ...

            }
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
           

       在startService 方法 中會傳回 startServ iceCommon 方法,在 startServiceCommon 方法中 會在注釋1處調用 AMS 的代理I Activity Manager的 startService 方法,最終調用的是 AMS 的startService 方法,這一過程在 “解析Activity的啟動過程” 中我們也分析過,不懂的同學可以看看Activity的啟動過程,這裡就不再做說明了。  

2. ActivityThread 啟動 Service

       接着我們來分析 AMS 的  startService 方法 ,代碼如下所示:   frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java  

@Override
    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); // ... 1
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return res;
        }
    }
           

       在注釋1 處調用 mServices 的  startServiceLocked 方法 , mServices 的類型是 ActiveServices, ActiveServices 的startServiceLocked 方法代碼如下所示 :   frameworks/base/services/core/java/com/android/server/am/ActiveServices.java  

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
            throws TransactionTooLargeException {

        ...

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

        ServiceRecord r = res.record; // ... 2 

        ...

        ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
        return cmp;
    }
           

       在注釋1處的 retrieveServiceLocked 方法會查找是否有與參數 service 對應的 ServiceRecord ,如果沒有找到,就會調用 PackageManagerService 去擷取參數 service 對應的 Service 資訊,并封裝到 ServiceRecord 中,最後将 ServiceRecord 封裝為 ServiceLookupResult 傳回。其中 ServiceRecord 用于描述一個 Service ,和 ActivityRecord 類似。在注釋2處通過注釋1處傳回的 ServiceLookupResult 得到參數 service 對應的 ServiceRecord ,并傳入到注釋3處的 startServicelnnerLocked 方法中。代碼如下所示:

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {

        ...

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

        ...

        return r.name;
    }
           

       在startServiceInnerLocked方法中又調用了bringUpServiceLocked方法,代碼如下所示:   frameworks/base/services/core/java/com/android/server/am/ActiveServices.java  

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

        // 擷取Service想要在哪個程序中運作
        final String procName = r.processName; // ... 1

        String hostingType = "service";
        ProcessRecord app;

        if (!isolated) {
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false); // ... 2
            if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                        + " app=" + app);

            // 如果運作Service的應用程式程序存在
            if (app != null && app.thread != null) { // ... 3
                try {
                    app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                    // 啟動Service
                    realStartServiceLocked(r, app, execInFg); // ... 4
                    return null;
                } catch (TransactionTooLargeException e) {
                    throw e;
                } catch (RemoteException e) {
                    Slog.w(TAG, "Exception when starting service " + r.shortName, e);
                }

                // If a dead object exception was thrown -- fall through to
                // restart the application.
            }
        } else {
            // If this service runs in an isolated process, then each time
            // we call startProcessLocked() we will get a new isolated
            // process, starting another process if we are currently waiting
            // for a previous process to come up.  To deal with this, we store
            // in the service any current isolated process it is running in or
            // waiting to have come up.
            app = r.isolatedProc;
            if (WebViewZygote.isMultiprocessEnabled()
                    && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
                hostingType = "webview_service";
            }
        }

        // Not running -- get it started, and enqueue this service record
        // to be executed when the app comes up.

        // 如果用來運作Service的應用程式程序不存在
        if (app == null && !permissionsReviewRequired) { // ... 5
  
            // 建立應用程式程序
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    hostingType, r.name, false, isolated, false)) == null) { // ... 6
                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;
            }
        }

        ...

        return null;
    }
           

       在注釋1 處得到 ServiceRecord 的  processName 值并賦 procName, procName用來描述 Service 要在 哪個 程序 中運 行,默 是當 前程序,  我們也可以在  AndroidManif es 檔案中設 android:process 屬 性來新開 啟一 個程序運作 Service  。在注釋2 處将 procName 和 Service 的uid 傳入到 AMS的 getProcessRecordLocked 方法   ,查 詢是否存 在一個與  Service 對應的Process Record 類型 的對象 a pp,  ProcessRecord 主要用來 描述運作的應用 程式 程序的資訊。  注釋5 處判斷 Service  對應的  app 為  null 則 說明用 來運作  Service 序程序不存在 ,則調 用注釋6 處的 AMS 的  startProcessLock 方法來建立 對應的應用 程式 程序,關于建立應用 程式 程序 請檢視" Android系統中應用程式程序的啟動過程 ", 這 裡隻讨論 沒有設定 android: proce ss 屬性,即應用程式程序存在的情況 。 注釋3 處判斷如果用 來運作  Service 的應用 程式程序 存在,則調 用注釋4 處的 realStartServiceLocked 方法 來啟動 Service,realStartServiceLocked方法代碼如下所示:  

 frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        
        ...

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

        ...

    }
           

       在realStartServiceLocked 方法中 調用了 app.thread 的  scheduleCreateService 方法 。其中  app.thread是 IApplicationThread 類型的,它的實作是 Activi tyThread 的内部類 Application Thread 。 Application Thread 的scheduleCreateService 方法如下所示:   frameworks/base/core/java/android/app/ActivityThread.java

public final void scheduleCreateService(IBinder token,
                ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
            updateProcessState(processState, false);
            CreateServiceData s = new CreateServiceData();
            s.token = token;
            s.info = info;
            s.compatInfo = compatInfo;

            sendMessage(H.CREATE_SERVICE, s);
        }
           

        scheduleCreateService 方法将啟動 Service 的參數封裝成 CreateServiceData , sendMessage 方法向H 類發送類型為 CREATE_ SERVICE 的消息,并将 CreateServiceData  傳遞過去,這個過程和 Ac ti vi tyThread 啟動 Activity 的過程是類似的。 sendMessage 方法 有多個重載方法,最終調用的 sendMessage 方法代碼 如下所示:   frameworks/base/core/java/android/app/ActivityThread.java  

private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        if (DEBUG_MESSAGES) Slog.v(
            TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
            + ": " + arg1 + " / " + obj);
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }
           

       這裡 mH 指的是 H ,它是 ActivityThread 的内部類并繼承自 Handler ,是應用程式程序中主線程的消息管理類。 我們接 着檢視 H 的  handleMessage 方法,代碼如下所示:   frameworks/base/core/java/android/app/ActivityThread.java

private class H extends Handler {
        
        ...
       
        public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {

                ...

                case CREATE_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
                    handleCreateService((CreateServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;

                ...
              
            }
            
            ...
        }
    }
           

       handleMessage 方法根據消息類型為 CREA E_SERVICE,會調用 handleCreateService 方法,代碼如下所示:

frameworks/base/core/java/android/app/ActivityThread.java

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

        // 擷取要啟動Service的應用程式的LoadedApk
        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo); // ... 1
        Service service = null;
        try {
            // 擷取類加載器
            java.lang.ClassLoader cl = packageInfo.getClassLoader(); // ... 2
            // 建立Service執行個體
            service = (Service) cl.loadClass(data.info.name).newInstance(); // ... 3
        } 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);

            // 建立Service的上下文環境ContextImpl對象
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo); // ... 4
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);

            // 初始化Service
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService()); // ... 5
            service.onCreate(); // ... 6
            mServices.put(data.token, service); // ... 7
            try {
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(service, e)) {
                throw new RuntimeException(
                    "Unable to create service " + data.info.name
                    + ": " + e.toString(), e);
            }
        }
    }
           

               在注釋1處擷取要啟動 Service 的應用程式的 LoadedApk, LoadedApk是一個 APK 檔案的描述類。在注釋2處通過調 用 LoadedApk的 getClassLoader 方法 來擷取類加載器。接 着在注釋3處根據 CreateServiceData  對象中存儲的 Service 資訊,建立 Service 執行個體。在注釋4處建立 Serv ice 的上下 文環境 Contextlmpl 對象。在注釋5 處通過 Service的 attach方法 來初始化Service 。在注釋6 處調用 Service 的  on Create 方法 ,這樣 Service 就啟動了。在注釋7 處将啟動的 Service 加入到 ActivityThread 的成員變量 mServices 中,其中 mServices 是  ArrayMap 類型。