Android系統原始碼分析–Service啟動流程

墨香發表於2019-02-18

在前面文章我們分析了四大元件中的兩個:Broadcast和Activity,這章我們分析四大元件中的服務(Service)的啟動過程。Service的啟動方式有兩種:一種是startService,一種是bindService;第一種通常是開啟一個服務執行後臺任務,不進行通訊,第二章通過是啟動服務進行通訊。下面我們就根據這兩種啟動方式來講Service的啟動流程以及unbindService和stopService流程。

Service啟動流程-startService

首先來看啟動流程時序圖:

Android系統原始碼分析–Service啟動流程

Step0.ContextImpl.startService

    @Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, mUser);
    }
複製程式碼

呼叫當前類中的startServiceCommon方法。

Step1.ContextImpl.startServiceCommon

    private ComponentName startServiceCommon(Intent service, UserHandle user) {
        try {
            // 檢驗Intent,元件和包名不能為空
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            // 通過getDefault方法獲取AMS的一個代理物件(ActivityManagerProxy),然後呼叫這個代理物件
            // 的startService方法來請求AMS啟動Service
            ComponentName cn = ActivityManagerNative.getDefault().startService(
                    mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), getOpPackageName(), user.getIdentifier());
            ...
            return cn;
        } catch (RemoteException e) {
            ...
        }
    }
複製程式碼

首先驗證Intent中傳遞的元件名是否為空,為什麼判斷下面我們介紹,接著通過代理物件ActivityManagerProxy,通過Binder呼叫AMS(ActivityManagerService)中的對應方法startService。我們先看包名驗證。

Step2.ContextImpl.validateServiceIntent

    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));
            }
        }
    }
複製程式碼

這裡給出瞭如果系統在Android5.0及以上版本,啟動服務必須為顯式啟動,否則丟擲異常,這個情況我們在剛開始在高於5.0系統都會遇到過,限制就在這裡,所以,5.0及以上系統必須用顯式的方式啟動服務。

3.AMP.startService

    public ComponentName startService(IApplicationThread caller, Intent service,
                                      String resolvedType, String callingPackage, int userId) throws RemoteException {
        ...
        // 通過Binder物件mRemote向AMS傳送一個型別為START_SERVICE_TRANSACTION的程式間通訊請求,
        // 然後會呼叫AMS中的對應的startService方法
        mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
        ...
        return res;
    }
複製程式碼

看過前面文章的對這一段程式碼應該很熟悉了,這個就是通過Binder呼叫AMS中對應方法的。所以我們直接看AMS。

4.AMS.startService

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

        ...
        synchronized (this) {
            ...
            ComponentName res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid, callingPackage, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }
複製程式碼

這裡的mServices是ActiveServices,因此呼叫的是ActiveServices中的startServiceLocked方法。

5.ActiveServices.startServiceLocked

    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
                                     int callingPid, int callingUid, String callingPackage, final int userId)
            throws TransactionTooLargeException {
        ...
        
        // 解析service這個Intent,就是解析在AndroidManifest.xml定義的Service標籤的intent-filter相關內容
        // 並將其內容儲存在Service的record(ServiceRecord)中
        ServiceLookupResult res =
                retrieveServiceLocked(service, resolvedType, callingPackage,
                        callingPid, callingUid, userId, true, callerFg, false);
        ...

        // 每一個Service元件都使用一個ServiceRecord物件來描述,就像每一個Activity都是用一個ActivityRecord
        // 物件來描述一樣
        ServiceRecord r = res.record;
        ...
        // 加入啟動服務列表
        r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                service, neededGrants));

        final ServiceMap smap = getServiceMap(r.userId);
        boolean addToStarting = false;
        // 如果是非前臺(後臺)程式呼叫
        if (!callerFg && r.app == null
                && mAm.mUserController.hasStartedUserState(r.userId)) {
            // 獲取啟動服務所在程式
            ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
            ...
        } else if (DEBUG_DELAYED_STARTS) {
            ...
        }

        return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    }
複製程式碼

這裡做的主要是啟動服務前的準備工作,首先是解析Intent攜帶的引數,並將這些內容儲存在用來描述Service的ServiceRecord物件中儲存起來,並將該物件放到等待啟動服務的列表中。然後呼叫startServiceInnerLocked啟動服務。在上面呼叫retrieveServiceLocked函式解析的過程中先去判斷AMS中是否存在引數為service對應的ServiceRecord物件,如果存在說明已經啟動過該服務,如果不存在,說明是第一次啟動該服務。

8.ActiveServices.startServiceInnerLocked

   ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
                                          boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
        ServiceState stracker = r.getTracker();// 獲取服務狀態
        ...
        r.callStart = false;// 是否呼叫onStart方法
        ...

        // 啟動ServiceRecord物件r所描述的一個Service元件,即Server元件
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
        ...

        return r.name;
    }
複製程式碼

這裡比較簡單主要是呼叫bringUpServiceLocked喚起服務。

Step9.ActiveServices.bringUpServiceLocked

    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
                                        boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {
            
        // 這裡的r.app.thread是一個ApplicationThread物件,ApplicationThread是用來AMS和應用程式通訊的工具,
        // 如果服務中的這個thread不為空說明已經和該Service存在通訊了,也就是說已經啟動了該服務了。
        // 如果服務已經存在,呼叫startService的時候會執行Service.onStartCommand,
        // 只有首次啟動服務才會呼叫onCreate方法
        if (r.app != null && r.app.thread != null) {
            // 執行Service.onStartCommand方法過程
            sendServiceArgsLocked(r, execInFg, false);
            return null;
        }

        ...

        // Make sure that the user who owns this service is started.  If not,
        // we don`t want to allow it to run.
        // 確保正在啟動服務的使用者已經啟動,否則不允許執行
        if (!mAm.mUserController.hasStartedUserState(r.userId)) {
            ...
            bringDownServiceLocked(r);
            return msg;
        }

        ...

        // 是不是獨立程式
        final boolean isolated = (r.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
        // 首先獲取ServiceRecord物件r所描述的Service元件的android:process屬性,並儲存在procName中
        final String procName = r.processName;
        ProcessRecord app;

        if (!isolated) {// 要啟動的服務不是獨立程式
            // 如果不是獨立程式,通過程式名稱和uid查詢是否已經存在一個對應的ProcessRecord物件app,如果存在,
            // 說明用來執行這個Service元件的應用程式已經存在了,因此下面的realStartServiceLocked函式在
            // ProcessRecord物件app所描述的應用程式程式中啟動這個Service元件。
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
            if (app != null && app.thread != null) {// 程式存在,並且該程式已經與AMS通訊過,那麼直接啟動服務
                try {
                    ...
                    // 啟動服務
                    realStartServiceLocked(r, app, execInFg);
                    return null;
                } catch (TransactionTooLargeException e) {
                    ...
                }

                // If a dead object exception was thrown -- fall through to
                // restart the application.
            }
        } else {// 如果要啟動的程式是獨立程式
            ...
        }

        // 如果要啟動的Service所在程式沒有啟動
        if (app == null && !permissionsReviewRequired) {
            // 啟動Service所需要的程式
            if ((app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    "service", r.name, false, isolated, false)) == null) {
                ...
                // 啟動失敗
                bringDownServiceLocked(r);
                return msg;
            }
            ...
        }

        ...

        if (r.delayedStop) {// 如果是延遲停止的服務
            // Oh and hey we`ve already been asked to stop!
            r.delayedStop = false;
            if (r.startRequested) {
                // 停止服務
                stopServiceLocked(r);
            }
        }

        return null;
    }
複製程式碼

這裡是啟動服務最重要的部分,根據不同的情況進行不同的處理。首先是判斷服務所在程式是否存在,如果存在呼叫sendServiceArgsLocked方法,最終根據條件,如果服務存在呼叫服務的onStartCommand方法;然後判斷被啟動服務的使用者是否已經被啟動,如果沒有則停止服務,也就是呼叫bringDownServiceLocked方法,最終呼叫服務的onDestroy方法;然後判斷非獨立程式的服務,如果程式存在並且服務未啟動的開始正式啟動服務,呼叫realStartServiceLocked方法,最終呼叫onCreate方法;然後判斷如果程式不存在,要啟動程式,並且在app啟動後啟動服務,這裡會呼叫啟動失敗,停止啟動,因為程式啟動後會啟動該服務,這個過程在前面我們講過,這裡不再分析這種情況。最後是如果是延遲停止的服務這裡直接停止該服務。下面我們按順序分析這幾種情況。

Step10.ActiveServices.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) {
            ...
            try {
                ..
                // 標記啟動服務開始
                bumpServiceExecutingLocked(r, execInFg, "start");
                ...
                // 傳送訊息,傳動到ApplicationThread中的scheduleServiceArgs方法,最終會呼叫onStartCommand
                r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
            } catch (TransactionTooLargeException e) {
                ...
            } 
        }
    }
複製程式碼

上面Step5提到啟動的服務都要先放到等待啟動服務列表中,因此這裡先判斷服務列表是否存在要啟動的服務,如果不存在則不再繼續執行,如果存在,迴圈啟動服務,這裡呼叫scheduleServiceArgs方法,其實在前面分析了很多遍,最終會傳送訊息ActivityThread中的Handler中的handleMessage中進行處理,然後呼叫ActivityThread中的handleServiceArgs方法。

Step13.ActivityThread.handleServiceArgs

    private void handleServiceArgs(ServiceArgsData data) {
        // 獲取Service物件
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                ...
                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;
                }

                ...
            } catch (Exception e) {
                ...
            }
        }
    }
複製程式碼

根據Service對應的token去快取中獲取服務,如果有該服務那麼呼叫服務的onStartCommand方法,如果不存在那麼就要繼續往下走建立服務。

Step15.ActiveServices.bringDownServiceLocked

這個方法主要是處理停止服務的方法,裡面主要是斷開連線,解除繫結,然後銷燬服務,因為這個過程是在服務停止時會呼叫,所以在後面介紹,這裡先不介紹了。

Step16.ActiveServices.realStartServiceLocked

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

        boolean created = false;
        ...
            // 傳送資訊到主執行緒,準備呼叫Service.onCreate方法
            // 請求ProcessRecord物件app描述的應用程式程式將ServiceRecord獨享r所描述的Service元件啟動起來。
            // ServiceRecord物件r所描述的Service元件啟動完成之後,AMS就需要將它連線到一個請求繫結它的一個
            // Activity元件中,這是通過呼叫AMS類的另一個成員函式requestServiceBindingLocked來實現的
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
        ...

        // 準備呼叫Service.onBind方法
        requestServiceBindingsLocked(r, execInFg);

        ...

        // 準備呼叫Service.onStartCommand方法
        sendServiceArgsLocked(r, execInFg, true);

        ...
    }
複製程式碼

啟動服務呼叫三個方法,首先是通過app.thread.scheduleCreateService方法呼叫onCreate方法,然後通過requestServiceBindingsLocked方法呼叫Service.onBind方法,然後通過sendServiceArgsLocked方法呼叫Service.onStartCommand方法,其中最後一個方法我們分析過了,所以我們只分析前兩個。第一很簡單了,最終呼叫ActivityThread中的handleCreateService方法。

Step18.ActivityThread.handleCreateService

    private void handleCreateService(CreateServiceData data) {
        ...

        // 獲取一個用來描述即將要啟動的Service元件所在的應用程式的LoadedApk物件,並將它儲存在packageInfo
        // 變數中(每一個應用程式都使用一個LoadedApk物件來描述,通過它就能方位到它所描述的應用程式的資源)
        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        ...
            // 獲取類載入器
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            // 通過類載入器將CreateServiceData物件data描述的一個Service元件載入的記憶體中,並且建立它的一個
            // 例項,儲存在Service物件service中。因為CreateServiceData物件data描述的Service元件即為應用
            // 程式的Ashmem中的Server元件,因此,Service物件service指向的Service元件實際上是一個Server元件
            service = (Service) cl.loadClass(data.info.name).newInstance();
        ...
            // 初始化一個ContextImpl物件context,用來為前面所建立的Service物件service的執行上下文環境,
            // 通過它可以訪問特定的應用程式資源,以及啟動其他應用程式元件
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);

            // 建立一個Application物件app,用來描述Service物件service所屬的應用程式。
            Application app = packageInfo.makeApplication(false, mInstrumentation);
            // 使用Application物件app,ContextImpl物件context和CreateServiceData物件data來初始化
            // Service物件service
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            // 呼叫Service的onCreate方法
            service.onCreate();
            // 以token為關鍵字儲存Service物件service到mServices中,服務啟動完成
            mServices.put(data.token, service);
            ...
        } catch (Exception e) {
            ...
        }
    }
複製程式碼

首先獲取LoadedApk物件,然後通過類載入器載入Service類,初始化Context,獲取對應的Application,如果存在直接返回,如果該應用還沒啟動則直接建立該Application,然後通過Service的attach方法將對應的資訊放置到Service中,這裡面就包含ActivityThread,因此我們在Step9中可以通過這個來判斷Service是不是被啟動了,然後呼叫onCreate方法,建立完成後,將該服務以token為鍵,Service為值放入到快取中,這樣我們前面獲取的時候就只從這裡獲取的,因此如果服務啟動了換粗就會存在,否則不存在。下面我們分析onBind方法。

Step20.ActiveServices.requestServiceBindingsLocked

    // 引數r指向一個ServiceRecord,表示一個已經啟動的Service元件
    private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)
            throws TransactionTooLargeException {
        for (int i = r.bindings.size() - 1; i >= 0; i--) {
            // 每一個IntentBindRecord物件都用來描述若干個需要將ServiceRecord物件r所描述的Service元件
            // 繫結到它們裡面的應用程式程式
            IntentBindRecord ibr = r.bindings.valueAt(i);
            if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
                break;
            }
        }
    }
複製程式碼

for迴圈呼叫onBind。

Step21.ActiveServices.requestServiceBindingLocked

    // 引數rebind表示是否需要將ServiceRecord物件r所描述的Service元件重新繫結到IntentBindRecord物件i
    // 所描述的應用程式程式中如果為false,則說明為第一繫結
    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
                                                      boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        ...
        // 檢查AMS是否已經為IntentBindRecord物件i所描述的應用程式程式請求過ServiceRecord物件r所描述的
        // Service元件返回其內部的一個Binder本地物件。如果還沒有請求requested為false並且apps的數量大於0
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                ...
                //會執行到Service的onBind方法
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.repProcState);
                if (!rebind) {
                    // 設定為true,防止重複請求
                    i.requested = true;
                }
            ...
            }
        }
        return true;
    }
複製程式碼

這裡通過呼叫r.app.thread.scheduleBindService方法,最終呼叫到ActivityThread中的handleBindService方法。

Step23.ActivityThread.handleBindService

    private void handleBindService(BindServiceData data) {
        // 通過token來獲得一個描述Service元件的Service物件
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
               ...
                    if (!data.rebind) {// 首次繫結
                        // 獲取一個實現了IBinder介面的Binder物件
                        IBinder binder = s.onBind(data.intent);
                        ...
                    } else {
                        s.onRebind(data.intent);
                        ...
                    }
                ...
            } catch (Exception e) {
                ...
            }
        }
    }
複製程式碼

現獲取服務,然後判斷是再次繫結還是首次繫結,如果是首次繫結呼叫Service.onBind方法,如果是再次繫結呼叫Service.onRebind方法。到這裡服務的啟動就完成了,其他一些操作就不分析了。下面我們先分析另外一個啟動流程bindService,最後分析停止服務流程。

Service啟動流程-bindService

首先來看繫結流程時序圖:

Android系統原始碼分析–Service啟動流程

Step1.ContextImpl.bindService

    public boolean bindService(Intent service, ServiceConnection conn,
                               int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
                Process.myUserHandle());
    }
複製程式碼

這裡是繫結服務的入口位置,呼叫bindServiceCommon方法。

Step2.ContextImpl.bindServiceCommon

    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        IServiceConnection sd;
        ...
        // mPackageInfo型別是LoadedApk
        if (mPackageInfo != null) {
            // 將ServiceConnection物件conn封裝成一個實現了IServiceConnection介面的Binder本地物件sd
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        } else {
            ...
        }
        ...
        try {
            IBinder token = getActivityToken();
            ...
            // 通過呼叫代理物件ActivityManagerProxy的bindService方法將前面獲得的sd物件,以及Intent物件
            // service等資訊傳送給AMS,以便AMS可以將ServiceConnection元件啟動起來
            int res = ActivityManagerNative.getDefault().bindService(
                    mMainThread.getApplicationThread(), getActivityToken(), service,
                    service.resolveTypeIfNeeded(getContentResolver()),
                    sd, flags, getOpPackageName(), user.getIdentifier());
            ...
            return res != 0;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
複製程式碼

首先呼叫LoadedApk中的getServiceDispatcher方法獲取實現IServiceConnection介面的Binder物件ServiceDispatcher.InnerConnection。然後呼叫AMP中的bindService方法,然後通過Binder通訊呼叫AMS中的bindService方法。

Step3.LoadedApk.getServiceDispatcher

    // 每一個繫結過Service元件的Activity元件在LoadedApk類中都有一個對應的ServiceDispatcher物件,它負責將
    // 這個被繫結的Service元件與繫結它的Activity元件關聯起來,這些ServiceDispatcher儲存在map中
    public final IServiceConnection getServiceDispatcher(ServiceConnection c,
                                                         Context context, Handler handler, int flags) {
        synchronized (mServices) {
            LoadedApk.ServiceDispatcher sd = null;
            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
            // 檢查成員變數mServices中是否存在一個以ServiceConnection物件c為關鍵字的ServiceDispatcher
            // 物件sd,如果不存在,則建立一個並且以context為關鍵字儲存到mServices中
            if (map != null) {
                sd = map.get(c);
            }
            if (sd == null) {
                sd = new ServiceDispatcher(c, context, handler, flags);
                if (map == null) {
                    map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
                    mServices.put(context, map);
                }
                map.put(c, sd);
            } else {
                sd.validate(context, handler);
            }
            // 呼叫前面獲取到的ServiceDispatcher物件sd的成員函式getIServiceConnection來獲取一個實現了
            // IServiceConnection介面的本地Binder物件
            return sd.getIServiceConnection();
        }
    }
複製程式碼

首先根據ServiceConnection取快取中獲取,如果沒有要初始化一個ServiceDispatcher物件,然後獲取ServiceDispatcher.InnerConnection物件並且返回。

Step6.AMP.bindService

    public int bindService(IApplicationThread caller, IBinder token,
                           Intent service, String resolvedType, IServiceConnection connection,
                           int flags, String callingPackage, int userId) throws RemoteException {
        ...
        // 通過Binder物件mRemote向AMS傳送一個型別為BIND_SERVICE_TRANSACTION的程式間通訊請求
        mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
        ...
        return res;
    }
複製程式碼

這裡通過Binder呼叫AMS中對應的方法。

Step7.AMS.bindService

    public int bindService(IApplicationThread caller, IBinder token, Intent service,
                           String resolvedType, IServiceConnection connection, int flags, String callingPackage,
                           int userId) throws TransactionTooLargeException {
        ...

        synchronized (this) {
            return mServices.bindServiceLocked(caller, token, service,
                    resolvedType, connection, flags, callingPackage, userId);
        }
    }
複製程式碼

這裡呼叫ActiveServices.bindServiceLocked方法。

Step8.ActiveServices.bindServiceLocked

    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
                          String resolvedType, final IServiceConnection connection, int flags,
                          String callingPackage, final int userId) throws TransactionTooLargeException {
        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service
                + " type=" + resolvedType + " conn=" + connection.asBinder()
                + " flags=0x" + Integer.toHexString(flags));
        // 根據caller來獲取一個ProcessRecord物件callerApp用來描述AMS執行繫結Service元件操作的一個Activity
        // 元件所執行在的應用程式程式
        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
        ...

        ActivityRecord activity = null;
        if (token != null) {
            // 通過token來獲得一個ActivityRecord物件activity,用來描述正在請求AMS執行繫結Service元件
            // 操作的一個Activity元件
            activity = ActivityRecord.isInStackLocked(token);
            if (activity == null) {
                Slog.w(TAG, "Binding with unknown activity: " + token);
                return 0;
            }
        }

        ...

        final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
        // 是否是繫結了外部服務,這個服務不是應用中的服務,而是外部獨立的服務(我們通常啟動服務都是應用內部的服務)
        final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;

        // 根據引數service來得到一個ServiceRecord物件s,用來描述即將被繫結的Service元件
        ServiceLookupResult res =
                retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
                        Binder.getCallingUid(), userId, true, callerFg, isBindExternal);
        ...
            // 呼叫ServiceRecord物件s的成員函式retrieveAppBindingLocked來得到一個AppBindRecord物件b,
            // 表示ServiceRecord物件s所描述的Service元件是繫結在ProcessRecord物件callerApp所描述的一個
            // 應用程式程式中的
            AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
            // 將前面獲得的APPBindRecord物件、ActivityRecord物件Activity以及引數connection封裝成一個
            // ConnectionRecord物件s所描述的一個Service元件,並且這個Activity元件是執行在
            // ProcessRecord物件callerApp所描述的一個應用程式中的
            ConnectionRecord c = new ConnectionRecord(b, activity,
                    connection, flags, clientLabel, clientIntent);

            // 由於一個Service元件可能會被同一個應用程式程式中的多個Activity元件使用同一個InnerConnection
            // 物件來繫結,因此,在AMS中,用來描述該Service元件的ServiceRecord物件就有可能會對應有多個
            // ConnectionRecord物件。在這種情況下,這些ConnectionRecord物件就會被儲存在一個列表中。
            // 這個列表最終會儲存在對應的ServiceRecord物件的成員變數Connection所描述的HashMap中,並且以
            // 它裡面的ConnectionRecord物件共同使用的一個InnerConnection代理物件的IBinder介面為關鍵字

            // 引數connection是一個InnerConnection代理物件,因此可以獲取它的一個IBinder介面binder
            IBinder binder = connection.asBinder();
            // 檢測在ServiceRecord物件s中是否存在一個以IBinder介面binder為關鍵字的列表clist,如果不存在
            // 建立一個,並且將clist以binder為關鍵字放到ServiceRecord物件成員變數connections中
            ArrayList<ConnectionRecord> clist = s.connections.get(binder);
            if (clist == null) {// 沒有繫結過
                clist = new ArrayList<ConnectionRecord>();
                s.connections.put(binder, clist);
            }
            clist.add(c);// 新增繫結列表
            b.connections.add(c);// 新增到關聯應用和服務的物件AppBindRecord中記錄繫結列表中
            if (activity != null) {
                if (activity.connections == null) {// 這裡判斷Activity裡面是否繫結過服務
                    activity.connections = new HashSet<ConnectionRecord>();
                }
                // 新增到描述Activity的ActivityRecord物件中記錄繫結服務的列表中
                activity.connections.add(c);
            }
            ...
            // 從記錄該服務所有繫結列表中獲取是否存在繫結列表
            clist = mServiceConnections.get(binder);
            if (clist == null) {
                clist = new ArrayList<ConnectionRecord>();
                mServiceConnections.put(binder, clist);
            }
            clist.add(c);// 新增都所有列表中

            // 從前面可知flags的Context.BIND_AUTO_CREATE位等於1,因此會呼叫bringUpServiceLocked來啟動
            // ServiceRecord物件s所描述的一個Service元件,等到這個Service元件啟動以後,AMS再將它與
            // ActivityRecord物件Activity所描述的一個Activity繫結自來
            if ((flags & Context.BIND_AUTO_CREATE) != 0) {
                s.lastActivity = SystemClock.uptimeMillis();
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                        permissionsReviewRequired) != null) {
                    return 0;
                }
            }

            ...
            
            if (s.app != null && b.intent.received) {
                // Service is already running, so we can immediately
                // publish the connection.
                try {
                    // 這裡的c.conn是ServiceDispatcher.InnerConnection物件,這裡最終呼叫
                    // ServiceDispatcher中的doConnected方法
                    c.conn.connected(s.name, b.intent.binder);
                } catch (Exception e) {
                    Slog.w(TAG, "Failure sending service " + s.shortName
                            + " to connection " + c.conn.asBinder()
                            + " (in " + c.binding.client.processName + ")", e);
                }

                // If this is the first app connected back to this binding,
                // and the service had previously asked to be told when
                // rebound, then do so.
                if (b.intent.apps.size() == 1 && b.intent.doRebind) {// 重新繫結
                    requestServiceBindingLocked(s, b.intent, callerFg, true);
                }
            } else if (!b.intent.requested) {// 首次繫結
                requestServiceBindingLocked(s, b.intent, callerFg, false);
            }

            getServiceMap(s.userId).ensureNotStartingBackground(s);

        } finally {
            Binder.restoreCallingIdentity(origId);
        }

        return 1;
    }
複製程式碼

這裡程式碼比較多,所以寫的註釋也比較多,前面主要是判斷是否繫結過該服務,並對InnerConnection進行快取,放置到各個列表中,讓Activity,Service進行聯絡。最後呼叫requestServiceBindingLocked方法,這個方法呼叫兩次,一次是首次繫結,一個是重新繫結。

Step9.AMS.requestServiceBindingLocked

    // 引數rebind表示是否需要將ServiceRecord物件r所描述的Service元件重新繫結到IntentBindRecord物件i
    // 所描述的應用程式程式中如果為false,則說明為第一繫結
    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
                                                      boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        ...
        // 檢查AMS是否已經為IntentBindRecord物件i所描述的應用程式程式請求過ServiceRecord物件r所描述的
        // Service元件返回其內部的一個Binder本地物件。如果還沒有請求requested為false並且apps的數量大於0
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                bumpServiceExecutingLocked(r, execInFg, "bind");
                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
                // 會執行到Service的onBind方法
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.repProcState);
                if (!rebind) {
                    // 設定為true,防止重複請求
                    i.requested = true;
                }
                i.hasBound = true;
                i.doRebind = false;
            ...
        }
        return true;
    }
複製程式碼

這裡是判斷首次繫結還是重新繫結,這兩種繫結都會執行繫結步驟,這裡有個引數i.requested,代表是否是首次繫結,如果預設值是false,如果不是重新繫結,那麼執行完繫結就會設定為true。然後呼叫thread.scheduleBindServic方法,這個方法講了很多次了,最終呼叫ApplicationThread.scheduleBindService方法,然後通過handler呼叫ActivityThread.handleBindService方法。

Step10.ActivityThread.handleBindService

    private void handleBindService(BindServiceData data) {
        // 通過token來獲得一個描述Service元件的Service物件
        Service s = mServices.get(data.token);
        ...
                    if (!data.rebind) {// 首次繫結
                        // 獲取一個實現了IBinder介面的Binder物件
                        IBinder binder = s.onBind(data.intent);
                        // 呼叫AMS代理物件的成員函式publishService,將前面得到的Binder本地物件傳遞給AMS
                        ActivityManagerNative.getDefault().publishService(
                                data.token, data.intent, binder);
                    } else {
                        s.onRebind(data.intent);
                        ActivityManagerNative.getDefault().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
    }
複製程式碼

這裡如果是首次繫結呼叫onBind方法,否則呼叫onRebind方法,在首次呼叫繫結後還有publishService方法,自己看一下,不在詳細分析了。這樣繫結流程就分析完了,難度不大。下面我們開分析解綁過程。

Service解綁流程-unbindService

首先來看解綁流程時序圖:

Android系統原始碼分析–Service啟動流程

Step1.ContextImpl.unbindService

    public void unbindService(ServiceConnection conn) {
        ...
        if (mPackageInfo != null) {
            // 獲取將ServiceConnection物件conn封裝成一個實現了IServiceConnection介面的Binder
            // 本地物件sd(ServiceDispatcher.InnerConnection)
            IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
                    getOuterContext(), conn);
            try {
                ActivityManagerNative.getDefault().unbindService(sd);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } else {
            throw new RuntimeException("Not supported in system context");
        }
    }
複製程式碼

IServiceConnection物件的獲取我們前面分析過了,這裡直接過,然後呼叫AMP.unbindService方法,最終呼叫AMS.unbindService方法。

Step3.AMS.unbindService

    public boolean unbindService(IServiceConnection connection) {
        synchronized (this) {
            return mServices.unbindServiceLocked(connection);
        }
    }
複製程式碼

這裡呼叫ActiveServices.unbindServiceLocked方法。

Step4.ActiveServices.unbindServiceLocked

    boolean unbindServiceLocked(IServiceConnection connection) {
        IBinder binder = connection.asBinder();
        // 獲取快取中繫結列表
        ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
        ...
        try {
            while (clist.size() > 0) {
                ConnectionRecord r = clist.get(0);
                removeConnectionLocked(r, null, null);
                if (clist.size() > 0 && clist.get(0) == r) {
                    // In case it didn`t get removed above, do it now.
                    Slog.wtf(TAG, "Connection " + r + " not removed for binder " + binder);
                    clist.remove(0);
                }

                ....
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }

        return true;
    }
複製程式碼

首先從AMS中獲取繫結過的列表,如果存在說明繫結過,然後呼叫removeConnectionLocked移除連線。

Step5.ActiveServices.removeConnectionLocked

    void removeConnectionLocked(
            ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
        // 獲取連線中的Binder物件
        IBinder binder = c.conn.asBinder();
        // 獲取連線應用、服務和連線列表的客戶端
        AppBindRecord b = c.binding;
        // 獲取描述服務的物件
        ServiceRecord s = b.service;
        // 根據Binder物件獲取服務繫結的連線列表
        ArrayList<ConnectionRecord> clist = s.connections.get(binder);
        if (clist != null) {
            clist.remove(c);// 移除該連線物件
            if (clist.size() == 0) {// 如果列表為空,移除該列表
                s.connections.remove(binder);
            }
        }
        // 從連線應用、服務和連線列表的客戶端中的連線列表中移除
        b.connections.remove(c);
        ...
        // 從總的快取列表中移除
        clist = mServiceConnections.get(binder);
        if (clist != null) {
            clist.remove(c);
            if (clist.size() == 0) {
                mServiceConnections.remove(binder);
            }
        }

        mAm.stopAssociationLocked(b.client.uid, b.client.processName, s.appInfo.uid, s.name);

        // 如果連線應用、服務和連線列表的客戶端中的連線列表為空了,說明沒有繫結了,那麼移除該客戶端
        if (b.connections.size() == 0) {
            b.intent.apps.remove(b.client);
        }

        if (!c.serviceDead) {// 服務還存在
            if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
                    && b.intent.hasBound) {
                try {
                    ...
                    b.intent.doRebind = false;
                    s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
                } catch (Exception e) {
                    Slog.w(TAG, "Exception when unbinding service " + s.shortName, e);
                    serviceProcessGoneLocked(s);
                }
            }

            ...
        }
    }
複製程式碼

在繫結服務的時候進行各種快取,加入各種列表,那麼在接觸繫結的時候就有從之前加入的列表中刪除,然後執行接觸繫結。

Step7.ActivityThread.handleUnbindService

    private void handleUnbindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                ...
                boolean doRebind = s.onUnbind(data.intent);// 呼叫解綁回撥
                ...
            } catch (Exception e) {
                ...
            }
        }
    }
複製程式碼

呼叫Service的onUnbind接觸繫結。到這裡接觸繫結就分析完了,過程比較簡單,只是上面的各種列表搞清楚要多看看。最後就剩下了stopService,我們一口氣就將它分析完。

Service停止流程-stopService

首先來看停止服務流程時序圖:

Android系統原始碼分析–Service啟動流程

Step1.ContextImpl.stopSevice

    // 停止服務
    @Override
    public boolean stopService(Intent service) {
        warnIfCallingFromSystemProcess();
        return stopServiceCommon(service, mUser);
    }
複製程式碼

呼叫stopServiceCommon停止服務。

Step2.ContextImpl.stopServiceCommon

    private boolean stopServiceCommon(Intent service, UserHandle user) {
        try {
            // 檢驗Intent,元件和包名不能為空
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            int res = ActivityManagerNative.getDefault().stopService(
                    mMainThread.getApplicationThread(), service,
                    service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
            ...
            return res != 0;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
複製程式碼

這裡最終呼叫AMS的stopService方法。

Step3.AMS.stopService

    public int stopService(IApplicationThread caller, Intent service,
                           String resolvedType, int userId) {
        ...

        synchronized (this) {
            return mServices.stopServiceLocked(caller, service, resolvedType, userId);
        }
    }
複製程式碼

這裡就是簡單的呼叫ActiveServices.stopServiceLocked方法。

Step4.ActiveServices.stopServiceLocked

    int stopServiceLocked(IApplicationThread caller, Intent service,
                          String resolvedType, int userId) {

        // 根據caller獲取呼叫者程式
        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
        ...
        if (r != null) {
            ...
                    stopServiceLocked(r.record);
            ...
            return -1;
        }

        return 0;
    }
複製程式碼

簡單的呼叫了stopServiceLocked方法。

Step6.ActiveServices.stopServiceLocked

    private void stopServiceLocked(ServiceRecord service) {
        ...
        bringDownServiceIfNeededLocked(service, false, false);
    }
複製程式碼

我們前面分析過bringUp的是啟動服務,因此對應的bringDown的是結束服務。

Step7.ActiveServices.bringDownServiceIfNeededLocked

    // 停止服務
    private final void bringDownServiceIfNeededLocked(ServiceRecord r, boolean knowConn,
                                                      boolean hasConn) {
        ...

        bringDownServiceLocked(r);
    }
複製程式碼

這裡只是簡單的呼叫了bringDownServiceLocked方法,這個方法我們在服務啟動時也遇到過,只是沒有分析,我們放到了停止服務的流程中來分析,下面我們看看詳細程式碼。

Step8.ActiveServices.bringDownServiceLocked

    private final void bringDownServiceLocked(ServiceRecord r) {
        
        for (int conni = r.connections.size() - 1; conni >= 0; conni--) {
            ArrayList<ConnectionRecord> c = r.connections.valueAt(conni);
            for (int i = 0; i < c.size(); i++) {
                ...
                    // 斷開服務連線
                    cr.conn.connected(r.name, null);
                ...
            }
        }

        // Tell the service that it has been unbound.
        if (r.app != null && r.app.thread != null) {
            for (int i = r.bindings.size() - 1; i >= 0; i--) {
                IntentBindRecord ibr = r.bindings.valueAt(i);
                if (ibr.hasBound) {// 所有服務與客戶端已經解綁
                    try {
                        ...
                        // 呼叫Service的onUnbind方法
                        r.app.thread.scheduleUnbindService(r,
                                ibr.intent.getIntent());
                    } catch (Exception e) {
                        ...
                    }
                }
            }
        }

        ...

        if (r.app != null) {// 服務程式存在
            ...
            if (r.app.thread != null) {
                updateServiceForegroundLocked(r.app, false);
                try {
                    ...
                    // 停止服務,呼叫onDestroy方法
                    r.app.thread.scheduleStopService(r);
                } catch (Exception e) {
                   ...
        ...
    }
複製程式碼

這裡主要有三步,第一步,斷開服務連線,這個方法我們前面提到過,可以根據前面提到的去看看如何斷開連線的;第二步,如果已經繫結了服務要解除繫結,這個在上面解除繫結的時候分析了該過程,因此這裡就不再重複了;第三步,如果服務存在,則停止服務。我們開始分析第三步,第三步這裡最終呼叫ActivityThread的handleStopService方法。

Step13.ActivityThread.handleStopService

    private void handleStopService(IBinder token) {
        Service s = mServices.remove(token);
        ...
                s.onDestroy();
                ...
    }
複製程式碼

這裡如果服務存在則呼叫服務的onDestroy方法,到這裡服務的停止也就結束了。從上面看服務的整個流程相對於Activity簡單的多。很容易就看懂了。其實還有一個IntentService,整個服務繼承Service,只不過裡面多了一個Handler,我們看看這段程式碼:

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }
複製程式碼

如果有Handler就有傳送訊息的地方,那麼傳送訊息在哪裡呢,我們知道當你呼叫服務的時候會走onStartCommand方法:

    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
複製程式碼

這裡會呼叫onStart方法:

    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
複製程式碼

這裡就是傳送訊息的時候,訊息發出後會由上面的ServiceHandler來處理,我們看到建構函式裡傳入了一個Looper,這個是在onCreate方法中初始化的:

    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
複製程式碼

這裡建立了一個Handler執行緒,並且獲取了它的Looper,用來迴圈處理訊息,因此我們知道同時只能處理一個訊息,等前一個訊息處理完了才會處理第二個,以此類推,因此需要同時處理的不能用這個Service,另外在ServiceHandler中有個stopSelf用來在訊息處理完成後停止自己,因此該服務可以說是用完自動停止,不會一直存在,佔用資源。

程式碼地址:

直接拉取匯入開發工具(Intellij idea或者Android studio)

git.coding.net/codemx/Andr…

注:

首發地址:www.codemx.cn

Android開發群:192508518

微信公眾賬號:Code-MX

Android系統原始碼分析–Service啟動流程

注:本文原創,轉載請註明出處,多謝。

相關文章