從Activity的啟動流程理解Binder

Jymoon發表於2019-05-24

簡述

關於Activity啟動流程和Binder的文章很多,大多數是分開來講的,本文將二者結合起來,著重分析啟動流程中跨程式方面的細節,其實,啟動流程看似呼叫繁多,主要是複雜在Activity棧管理等方面,如果將其看作一個整體,整個啟動流程就簡單很多。在啟動流程中,App和AMS的跨程式呼叫是其中的重點,理解了這個,會加深對Binder和啟動流程的理解認知,也能窺到Framework層的冰山一角。另外我也發現,很多文章在講啟動流程的時候,關於ActivityMangagerService程式如何呼叫App程式的細節都沒有講清楚,這個問題也是我寫這篇文章的初衷。閱讀本文前建議瞭解一下AIDL,對Binder,Stub,Proxy等有一些印象。建議讀一下這篇文章寫給 Android 應用工程師的 Binder 原理剖析

Binder簡介

對於Binder,初學的人會對裡面的概念比較模糊,因為看起來確實有些繞,我在這兒寫幾點幫助理解。

  • 所謂的“跨程式”能力指的是兩個方面:一個是能夠作為客戶端呼叫遠端服務的能力,一個是能夠作為服務端接收客戶端程式訊息的能力,二者都是跨程式的一部分,分別對應transact和onTransact方法,而這兩個方法的實現,分別位於BinderProxy和Binder兩個類中,這兩個類都在Binder.java這個檔案中,讀者可以自行閱讀。
  • BinderProxy具有傳送訊息的能力,通過transact方法,呼叫底層binder驅動,服務端的Binder具有接收底層binder驅動傳過來的訊息的能力,當接收到訊息會呼叫onTransact方法。
  • 剛開始看AIDL的時候需要反覆記憶理解一下,否則看別的程式碼容易混淆。這裡說幾個比較容易記憶的點:一個類繼承了Stub類,表示這個類是遠端服務端,Stub類有個asInterface的靜態方法,這個方法用在拿到binder驅動傳過來的BinderProxy物件時,將該物件轉化成client端使用的本地代理xxxProxy,客戶端用它呼叫遠端service的方法。該代理跟service實現了同樣的介面,只不過一個是真實現,一個是假實現,這裡假實現指的是它通過Binder驅動呼叫S端方法,真正做工作的在Service端。簡言之,Stub代表service端,Proxy代表service在客戶端的代理。
  • 以AMS為例,看下圖:
    ams.png

AMS繼承了Stub類,而Stub類一共實現了三個介面:IActivityManger,IInterface和IBinder,分別對應了三種能力,管理activity、跨程式、asBinder,前兩者好理解,那麼這裡的asBinder能力是幹嘛的呢?這裡先賣個關子,等下講啟動流程的時候會說明。

  • AMS和APP跨程式的過程簡單說就是C端和S端分別通過二者在對方的代理去互相呼叫對方的方法

    啟動流程

    我們先從巨集觀角度理解這個過程,首先,為什麼要跨程式呢?我自己在客戶端new一個Activity不行嗎?顯然是不可以的,Android的安全機制以及為了統一管理Activity(比如activity棧),需要有個大管家去進行所有Activity的管理和控制,而這個管家是執行在一個單獨程式的,因此App端如果想發起一個Activity的請求,需要先把“申請”提交給大管家,也就是AMS。AMS處理完這個請求之後,需要再次通過跨程式通知App端,去執行剩下的相應的工作。因此這裡的核心就在於兩者如何互相呼叫對方了。好多文章對AMS如何通知App這塊講的不夠清楚,甚至忽略,本文著重會說明。

    App端如何呼叫AMS方法

    下面看程式碼:使用者啟動一個頁面時,會依次呼叫activity的startActivity-->Instrumentation的executestartActivity-->execStartActivitiesAsUser,這幾個呼叫很容易找到,就簡單帶過,在最後這個方法裡,執行了遠端呼叫,即:
 int result = ActivityManager.getService()
                .startActivities(whoThread, who.getBasePackageName(), intents, resolvedTypes,
                        token, options, userId);

ActivityManager.getService獲取的是什麼?看ActivityMangaer.getService()這個程式碼裡面:

    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };

如果對Binder有所瞭解,應該很容易知道,這裡取得的是AMS在客戶端的代理,也就是程式碼中的最後一行返回的am。因為App要頻繁的呼叫AMS的方法,因此用單例模式快取在本地了一個AMS的本地代理,從單例的第一次獲取可以看到,AMS的Binder是通過ServiceManager.getService()獲取到的,那麼ServiceMangaer是個什麼東西,其實這個就是Android系統統一管理所有遠端服務的“大管家”,比如AMS,WMS等系統服務都在這裡註冊了,客戶端想呼叫任意一個服務,只需要知道名字就可以通過SM獲取到相應的Server的Binder。拿到Binder之後便可以通過asInterface靜態方法轉化成本地代理,從而呼叫server的方法了。因此第一次獲取AMS的Binder的過程實際上是客戶端跟ServiceManager的一次跨程式通訊。

AMS如何通知App程式

(1)AMS如何獲取到App程式的Binder的

從上面的分析知道,App獲取AMS的Binder實際上是通過ServiceManager這個大管家間接獲取的,那反過來AMS處理完activity的管理任務(棧操作等)之後又如何通知App的呢?
一個App總不可能像AMS那樣在ServiceManger中註冊吧,而且也沒這個必要。那麼到底是怎麼通知的呢?
答案就是:App跨程式呼叫AMS的方法時,還順便把App程式(這個時候App可以看作是服務端了)的Binder作為引數傳給了AMS,AMS拿到這個APP的Binder之後,通過asInterface方法轉化成在server端可以使用的代理,然後在需要回撥App程式的時候通過這個代理來通知客戶端。其實跟App端邏輯是一致的,只不過C/S調了一下順序,C變成了S,S變成了C。下面我們從程式碼裡驗證:
我們以6.0之前版本的原始碼為例,新版本改成事務了,有些原始碼不容易看到,不如直接看老版本的,便於理解。
首先看APP呼叫startActivity時是如何把App程式的Binder引數傳過去的,剛才說了,startActivity實際上呼叫的是AMS本地代理的startActivity,而AMS本地代理是ActivityMangerProxy,這裡AMP是AIDL自動生成的

class ActivityManagerProxy implements IActivityManager
{
    public ActivityManagerProxy(IBinder remote)
    {
        mRemote = remote;
    }
    
    public IBinder asBinder()
    {
        return mRemote;
    }
    
    public int startActivity(IApplicationThread caller, Intent intent,
            String resolvedType, Uri[] grantedUriPermissions, int grantedMode,
            IBinder resultTo, String resultWho,
            int requestCode, boolean onlyIfNeeded,
            boolean debug, String profileFile, ParcelFileDescriptor profileFd,
            boolean autoStopProfiler) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        intent.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeTypedArray(grantedUriPermissions, 0);
        data.writeInt(grantedMode);
        data.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(requestCode);
        data.writeInt(onlyIfNeeded ? 1 : 0);
        data.writeInt(debug ? 1 : 0);
        data.writeString(profileFile);
        if (profileFd != null) {
            data.writeInt(1);
            profileFd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            data.writeInt(0);
        }
        data.writeInt(autoStopProfiler ? 1 : 0);
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        return result;
    }

startActivity方法的第一個引數caller,這個東西是IApplicationThread,這個IApplicationThread就是AMS去通知App做相應處理的介面,它跟IActivityManger配合組成了App和AMS互動的“協議”。那麼這個傳過來的IApplicationThread的是誰呢,看程式碼:

Instrumentation:
int result = ActivityManager.getService()
                .startActivityAsUser(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, resultWho,
                        requestCode, 0, null, options, user.getIdentifier());


ContextImpl:
        mMainThread.getInstrumentation().execStartActivities(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intents, options);

ActivityThread:
    public ApplicationThread getApplicationThread()
    {
        return mAppThread;
    }

通過函式呼叫可以查到,首先是instrumentation類裡傳入的whoThread,whoThread是ContextImpl傳進來的mMainThread.getApplicationThread(),而最後這個是mAppThread,這個東西就是ActivityThread這個類的內部類ApplicationThread,我們看程式碼:

 private class ApplicationThread extends IApplicationThread.Stub {

繼承自Stub,因此從AIDL語法看出,是一個服務端,對應的客戶端是誰呢?當然是AMS了,所以ApplicationThread這個類就是AMS向App程式發訊息時的服務端,因此客戶端和服務端都是相對的,A要調B的服務,A就是客戶端,B就是服務端,反之亦然。

思路回到主線上,上面已經說明了,在客戶端呼叫Binder的時候把ApplicationThread傳給了AMS,怎麼傳的呢?這裡回到剛才的ActivityMangerProxy這個類裡面來,引數已經傳給了startActivity方法,接下來會執行到這一行:

data.writeStrongBinder(caller != null ? caller.asBinder() : null);

caller.asBinder,這裡asBinder方法就涉及到之前遺留的那個問題,服務端為什麼要實現IInterface這個介面,就是在這個時候用的,即把S端(相對的)轉成一個binder,之後binder寫入到Parcel裡,然後通過transact方法呼叫底層Binder驅動傳給其他程式,這裡也要注意,transact方法呼叫的是mRemote的transact,而mRemote本質上是一個BinderProxy,千萬不要理解成Binder了,因為這兩個類都實現了IBinder介面,我們看程式碼的時候很可能會誤認為呼叫的Binder的transact。binderProxy的transact會呼叫transactNative函式,傳給jni層,將之前儲存在Parcel裡的資料data傳給Binder驅動,之後在傳給AMS。可以這樣理解,對於Binder驅動來說,它可以看成跨程式的一個“傳送帶”,從A程式傳遞給B程式,只要你實現了IInterface,就可以放到這個傳送帶上傳送(writeStrongBinder方法)。總結一下就是IInterface介面表明了這個類可以轉成一個binder從而在binder驅動中跨程式運輸,IBinder介面表明了類具有跨程式的能力,即可以通過呼叫transact方法“使用”Binder驅動。

(2)獲取到了Binder之後

上面的討論已經知道,AMS其實在App跨程式呼叫AMS的時候就把ApplicationThread的Binder傳過來了,傳過來以後,AMS如果要用,必須得拿到ApplicationThread的代理,怎麼拿到的呢?
剛才說了,客戶端startActivity,通過AMS的代理髮起transact方法,AMS的onTransact方法會監聽,我們看onTransact的程式碼:AMS繼承自IActivityManager.Stub,在原始碼中叫ActivityManagerNative:

    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        switch (code) {
        case START_ACTIVITY_TRANSACTION:
        {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);

可以看到:
IBinder b = data.readStrongBinder();客戶端將binder write到Parcel中,服務端從data中讀了出來,然後通過asInterface轉換成ApplicationThread的代理ApplicationThreadProxy這個類。注意:前面也講了,Binder驅動過來的IBinder不是Binder,而是BinderProxy,但是為什麼我們之前傳的引數是ApplicationThread,這個類是一個binder,為什麼讀出來以後變成了BinderProxy了呢?答案就在這個readStrongBinder裡,看jni層的原始碼可以知道,系統在客戶端收到(readStrongBinder)IBinder以後,會儲存下來,通過Binder驅動傳給Service時,會通過之前儲存的Binder在底層建立BinderProxy,然後傳給上層,其實看framework的原始碼,BinderProxy沒有看到在java層的new方法,原來都在底層建立好了。
有了代理物件後接下來既可以直接用了:

int result = startActivity(app, intent, resolvedType,
                    grantedUriPermissions, grantedMode, resultTo, resultWho,
                    requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler);

即進入到AMS對Activity啟動管理流程中了,經過複雜的跳轉,最後跑到ActivityStackSupervisor這個類的realStartActivityLocked方法中,裡面最終會執行到這行程式碼:

app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                    r.compat, r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState,
                    results, newIntents, !andResume, mService.isNextTransitionForward(),
                    profilerInfo);

這裡的app.thread就是前面ApplicationThread在AMS中的代理,到了這裡大家應該理清楚了,接下來通過代理調起App程式的ApplicationThread裡的相應方法,即:scheduleLaunchActivity方法,這個方法會傳送一個Message給主執行緒的handler :H,然後在handleMessage裡通過類載入器建立出一個Activity物件,並執行onCreate方法.balabala....

最後用圖片總結一下:
binder.png

最後推薦一篇文章,目前發現的講的binder最詳細的,聽說你 Binder 機制學的不錯,來解決下這幾個問題

相關文章