從Activity的啟動(dòng)流程理解Binder

簡(jiǎn)述

關(guān)于Activity啟動(dòng)流程和Binder的文章很多椭微,大多數(shù)是分開來講的洞坑,本文將二者結(jié)合起來,著重分析啟動(dòng)流程中跨進(jìn)程方面的細(xì)節(jié)蝇率,其實(shí)迟杂,啟動(dòng)流程看似調(diào)用繁多,主要是復(fù)雜在Activity棧管理等方面本慕,如果將其看作一個(gè)整體排拷,整個(gè)啟動(dòng)流程就簡(jiǎn)單很多。在啟動(dòng)流程中锅尘,App和AMS的跨進(jìn)程調(diào)用是其中的重點(diǎn)监氢,理解了這個(gè),會(huì)加深對(duì)Binder和啟動(dòng)流程的理解認(rèn)知藤违,也能窺到Framework層的冰山一角浪腐。另外我也發(fā)現(xiàn),很多文章在講啟動(dòng)流程的時(shí)候顿乒,關(guān)于ActivityMangagerService進(jìn)程如何調(diào)用App進(jìn)程的細(xì)節(jié)都沒有講清楚议街,這個(gè)問題也是我寫這篇文章的初衷。閱讀本文前建議了解一下AIDL璧榄,對(duì)Binder,Stub,Proxy等有一些印象特漩。建議讀一下這篇文章寫給 Android 應(yīng)用工程師的 Binder 原理剖析

Binder簡(jiǎn)介

對(duì)于Binder吧雹,初學(xué)的人會(huì)對(duì)里面的概念比較模糊,因?yàn)榭雌饋泶_實(shí)有些繞拾稳,我在這兒寫幾點(diǎn)幫助理解吮炕。

  • 所謂的“跨進(jìn)程”能力指的是兩個(gè)方面:一個(gè)是能夠作為客戶端調(diào)用遠(yuǎn)程服務(wù)的能力,一個(gè)是能夠作為服務(wù)端接收客戶端進(jìn)程消息的能力访得,二者都是跨進(jìn)程的一部分,分別對(duì)應(yīng)transact和onTransact方法陕凹,而這兩個(gè)方法的實(shí)現(xiàn)悍抑,分別位于BinderProxy和Binder兩個(gè)類中,這兩個(gè)類都在Binder.java這個(gè)文件中杜耙,讀者可以自行閱讀搜骡。
  • BinderProxy具有發(fā)送消息的能力,通過transact方法佑女,調(diào)用底層binder驅(qū)動(dòng)记靡,服務(wù)端的Binder具有接收底層binder驅(qū)動(dòng)傳過來的消息的能力,當(dāng)接收到消息會(huì)調(diào)用onTransact方法团驱。
  • 剛開始看AIDL的時(shí)候需要反復(fù)記憶理解一下摸吠,否則看別的代碼容易混淆。這里說幾個(gè)比較容易記憶的點(diǎn):一個(gè)類繼承了Stub類嚎花,表示這個(gè)類是遠(yuǎn)程服務(wù)端寸痢,Stub類有個(gè)asInterface的靜態(tài)方法,這個(gè)方法用在拿到binder驅(qū)動(dòng)傳過來的BinderProxy對(duì)象時(shí)紊选,將該對(duì)象轉(zhuǎn)化成client端使用的本地代理xxxProxy啼止,客戶端用它調(diào)用遠(yuǎn)程service的方法。該代理跟service實(shí)現(xiàn)了同樣的接口兵罢,只不過一個(gè)是真實(shí)現(xiàn)献烦,一個(gè)是假實(shí)現(xiàn),這里假實(shí)現(xiàn)指的是它通過Binder驅(qū)動(dòng)調(diào)用S端方法卖词,真正做工作的在Service端巩那。簡(jiǎn)言之,Stub代表service端坏平,Proxy代表service在客戶端的代理拢操。
  • 以AMS為例,看代碼
public class ActivityManagerService extends IActivityManager.Stub

AMS繼承了Stub類舶替,而Stub類一共實(shí)現(xiàn)了三個(gè)接口:IActivityManger令境,IInterface和IBinder,分別對(duì)應(yīng)了三種能力顾瞪,管理activity舔庶、跨進(jìn)程抛蚁、asBinder,前兩者好理解惕橙,那么這里的asBinder能力是干嘛的呢瞧甩?這里先賣個(gè)關(guān)子,等下講啟動(dòng)流程的時(shí)候會(huì)說明弥鹦。

  • AMS和APP跨進(jìn)程的過程簡(jiǎn)單說就是C端和S端分別通過二者在對(duì)方的代理去互相調(diào)用對(duì)方的方法

啟動(dòng)流程

我們先從宏觀角度理解這個(gè)過程肚逸,首先,為什么要跨進(jìn)程呢彬坏?我自己在客戶端new一個(gè)Activity不行嗎朦促?顯然是不可以的,Android的安全機(jī)制以及為了統(tǒng)一管理Activity(比如activity棧)栓始,需要有個(gè)大管家去進(jìn)行所有Activity的管理和控制,而這個(gè)管家是運(yùn)行在一個(gè)單獨(dú)進(jìn)程的务冕,因此App端如果想發(fā)起一個(gè)Activity的請(qǐng)求,需要先把“申請(qǐng)”提交給大管家幻赚,也就是AMS禀忆。AMS處理完這個(gè)請(qǐng)求之后,需要再次通過跨進(jìn)程通知App端落恼,去執(zhí)行剩下的相應(yīng)的工作箩退。因此這里的核心就在于兩者如何互相調(diào)用對(duì)方了。好多文章對(duì)AMS如何通知App這塊講的不夠清楚领跛,甚至忽略乏德,本文著重會(huì)說明。

App端如何調(diào)用AMS方法

下面看代碼:用戶啟動(dòng)一個(gè)頁(yè)面時(shí)吠昭,會(huì)依次調(diào)用activity的startActivity-->Instrumentation的executestartActivity-->execStartActivitiesAsUser喊括,這幾個(gè)調(diào)用很容易找到,就簡(jiǎn)單帶過矢棚,在最后這個(gè)方法里郑什,執(zhí)行了遠(yuǎn)程調(diào)用,即:

 int result = ActivityManager.getService()
                .startActivities(whoThread, who.getBasePackageName(), intents, resolvedTypes,
                        token, options, userId);

ActivityManager.getService獲取的是什么蒲肋?看ActivityMangaer.getService()這個(gè)代碼里面:

    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;
                }
            };

如果對(duì)Binder有所了解蘑拯,應(yīng)該很容易知道,這里取得的是AMS在客戶端的代理兜粘,也就是代碼中的最后一行返回的am申窘。因?yàn)锳pp要頻繁的調(diào)用AMS的方法,因此用單例模式緩存在本地了一個(gè)AMS的本地代理孔轴,從單例的第一次獲取可以看到剃法,AMS的Binder是通過ServiceManager.getService()獲取到的,那么ServiceMangaer是個(gè)什么東西,其實(shí)這個(gè)就是Android系統(tǒng)統(tǒng)一管理所有遠(yuǎn)程服務(wù)的“大管家”路鹰,比如AMS贷洲,WMS等系統(tǒng)服務(wù)都在這里注冊(cè)了收厨,客戶端想調(diào)用任意一個(gè)服務(wù),只需要知道名字就可以通過SM獲取到相應(yīng)的Server的Binder优构。拿到Binder之后便可以通過asInterface靜態(tài)方法轉(zhuǎn)化成本地代理诵叁,從而調(diào)用server的方法了。因此第一次獲取AMS的Binder的過程實(shí)際上是客戶端跟ServiceManager的一次跨進(jìn)程通信钦椭。

AMS如何通知App進(jìn)程

(1)AMS如何獲取到App進(jìn)程的Binder的

從上面的分析知道拧额,App獲取AMS的Binder實(shí)際上是通過ServiceManager這個(gè)大管家間接獲取的,那反過來AMS處理完activity的管理任務(wù)(棧操作等)之后又如何通知App的呢玉凯?
一個(gè)App總不可能像AMS那樣在ServiceManger中注冊(cè)吧势腮,而且也沒這個(gè)必要。那么到底是怎么通知的呢漫仆?
答案就是:App跨進(jìn)程調(diào)用AMS的方法時(shí),還順便把App進(jìn)程(這個(gè)時(shí)候App可以看作是服務(wù)端了)的Binder作為參數(shù)傳給了AMS泪幌,AMS拿到這個(gè)APP的Binder之后盲厌,通過asInterface方法轉(zhuǎn)化成在server端可以使用的代理,然后在需要回調(diào)App進(jìn)程的時(shí)候通過這個(gè)代理來通知客戶端祸泪。其實(shí)跟App端邏輯是一致的吗浩,只不過C/S調(diào)了一下順序,C變成了S没隘,S變成了C懂扼。下面我們從代碼里驗(yàn)證:
我們以6.0之前版本的源碼為例,新版本改成事務(wù)了右蒲,有些源碼不容易看到阀湿,不如直接看老版本的,便于理解瑰妄。
首先看APP調(diào)用startActivity時(shí)是如何把App進(jìn)程的Binder參數(shù)傳過去的陷嘴,剛才說了,startActivity實(shí)際上調(diào)用的是AMS本地代理的startActivity间坐,而AMS本地代理是ActivityMangerProxy灾挨,這里AMP是AIDL自動(dòng)生成的

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方法的第一個(gè)參數(shù)caller,這個(gè)東西是IApplicationThread,這個(gè)IApplicationThread就是AMS去通知App做相應(yīng)處理的接口竹宋,它跟IActivityManger配合組成了App和AMS交互的“協(xié)議”劳澄。那么這個(gè)傳過來的IApplicationThread的是誰(shuí)呢,看代碼:

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;
    }

通過函數(shù)調(diào)用可以查到蜈七,首先是instrumentation類里傳入的whoThread秒拔,whoThread是ContextImpl傳進(jìn)來的mMainThread.getApplicationThread(),而最后這個(gè)是mAppThread,這個(gè)東西就是ActivityThread這個(gè)類的內(nèi)部類ApplicationThread,我們看代碼:

 private class ApplicationThread extends IApplicationThread.Stub {

繼承自Stub宪潮,因此從AIDL語(yǔ)法看出溯警,是一個(gè)服務(wù)端趣苏,對(duì)應(yīng)的客戶端是誰(shuí)呢?當(dāng)然是AMS了梯轻,所以ApplicationThread這個(gè)類就是AMS向App進(jìn)程發(fā)消息時(shí)的服務(wù)端食磕,因此客戶端和服務(wù)端都是相對(duì)的,A要調(diào)B的服務(wù)喳挑,A就是客戶端彬伦,B就是服務(wù)端,反之亦然伊诵。

思路回到主線上单绑,上面已經(jīng)說明了,在客戶端調(diào)用Binder的時(shí)候把ApplicationThread傳給了AMS曹宴,怎么傳的呢搂橙?這里回到剛才的ActivityMangerProxy這個(gè)類里面來,參數(shù)已經(jīng)傳給了startActivity方法笛坦,接下來會(huì)執(zhí)行到這一行:

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

caller.asBinder,這里asBinder方法就涉及到之前遺留的那個(gè)問題区转,服務(wù)端為什么要實(shí)現(xiàn)IInterface這個(gè)接口,就是在這個(gè)時(shí)候用的版扩,即把S端(相對(duì)的)轉(zhuǎn)成一個(gè)binder废离,之后binder寫入到Parcel里,然后通過transact方法調(diào)用底層Binder驅(qū)動(dòng)傳給其他進(jìn)程礁芦,這里也要注意蜻韭,transact方法調(diào)用的是mRemote的transact,而mRemote本質(zhì)上是一個(gè)BinderProxy,千萬不要理解成Binder了柿扣,因?yàn)檫@兩個(gè)類都實(shí)現(xiàn)了IBinder接口肖方,我們看代碼的時(shí)候很可能會(huì)誤認(rèn)為調(diào)用的Binder的transact。binderProxy的transact會(huì)調(diào)用transactNative函數(shù)窄刘,傳給jni層窥妇,將之前保存在Parcel里的數(shù)據(jù)data傳給Binder驅(qū)動(dòng),之后在傳給AMS娩践』铘妫可以這樣理解,對(duì)于Binder驅(qū)動(dòng)來說翻伺,它可以看成跨進(jìn)程的一個(gè)“傳送帶”材泄,從A進(jìn)程傳遞給B進(jìn)程,只要你實(shí)現(xiàn)了IInterface吨岭,就可以放到這個(gè)傳送帶上傳送(writeStrongBinder方法)拉宗。總結(jié)一下就是IInterface接口表明了這個(gè)類可以轉(zhuǎn)成一個(gè)binder從而在binder驅(qū)動(dòng)中跨進(jìn)程運(yùn)輸,IBinder接口表明了類具有跨進(jìn)程的能力旦事,即可以通過調(diào)用transact方法“使用”Binder驅(qū)動(dòng)魁巩。

(2)獲取到了Binder之后

上面的討論已經(jīng)知道,AMS其實(shí)在App跨進(jìn)程調(diào)用AMS的時(shí)候就把ApplicationThread的Binder傳過來了姐浮,傳過來以后谷遂,AMS如果要用,必須得拿到ApplicationThread的代理卖鲤,怎么拿到的呢肾扰?
剛才說了,客戶端startActivity蛋逾,通過AMS的代理發(fā)起transact方法集晚,AMS的onTransact方法會(huì)監(jiān)聽,我們看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中偷拔,服務(wù)端從data中讀了出來,然后通過asInterface轉(zhuǎn)換成ApplicationThread的代理ApplicationThreadProxy這個(gè)類亏钩。注意:前面也講了条摸,Binder驅(qū)動(dòng)過來的IBinder不是Binder,而是BinderProxy铸屉,但是為什么我們之前傳的參數(shù)是ApplicationThread,這個(gè)類是一個(gè)binder切端,為什么讀出來以后變成了BinderProxy了呢彻坛?答案就在這個(gè)readStrongBinder里,看jni層的源碼可以知道踏枣,系統(tǒng)在客戶端收到(readStrongBinder)IBinder以后昌屉,會(huì)保存下來,通過Binder驅(qū)動(dòng)傳給Service時(shí)茵瀑,會(huì)通過之前保存的Binder在底層創(chuàng)建BinderProxy间驮,然后傳給上層,其實(shí)看framework的源碼马昨,BinderProxy沒有看到在java層的new方法竞帽,原來都在底層創(chuàng)建好了。
有了代理對(duì)象后接下來既可以直接用了:

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

即進(jìn)入到AMS對(duì)Activity啟動(dòng)管理流程中了鸿捧,經(jīng)過復(fù)雜的跳轉(zhuǎn)屹篓,最后跑到ActivityStackSupervisor這個(gè)類的realStartActivityLocked方法中,里面最終會(huì)執(zhí)行到這行代碼:

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中的代理匙奴,到了這里大家應(yīng)該理清楚了堆巧,接下來通過代理調(diào)起App進(jìn)程的ApplicationThread里的相應(yīng)方法,即:scheduleLaunchActivity方法,這個(gè)方法會(huì)發(fā)送一個(gè)Message給主線程的handler :H,然后在handleMessage里通過類加載器創(chuàng)建出一個(gè)Activity對(duì)象谍肤,并執(zhí)行onCreate方法.balabala....

最后用圖片總結(jié)一下:


binder.png

最后推薦一篇文章啦租,目前發(fā)現(xiàn)的講的binder最詳細(xì)的,聽說你 Binder 機(jī)制學(xué)的不錯(cuò)荒揣,來解決下這幾個(gè)問題

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末篷角,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子乳附,更是在濱河造成了極大的恐慌内地,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赋除,死亡現(xiàn)場(chǎng)離奇詭異阱缓,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)举农,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門荆针,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人颁糟,你說我怎么就攤上這事航背。” “怎么了棱貌?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵玖媚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我婚脱,道長(zhǎng)今魔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任障贸,我火速辦了婚禮错森,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘篮洁。我一直安慰自己涩维,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布袁波。 她就那樣靜靜地躺著瓦阐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪锋叨。 梳的紋絲不亂的頭發(fā)上垄分,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音娃磺,去河邊找鬼薄湿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的豺瘤。 我是一名探鬼主播吆倦,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼坐求!你這毒婦竟也來了蚕泽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤桥嗤,失蹤者是張志新(化名)和其女友劉穎须妻,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泛领,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡荒吏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了渊鞋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绰更。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖锡宋,靈堂內(nèi)的尸體忽然破棺而出儡湾,到底是詐尸還是另有隱情,我是刑警寧澤执俩,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布徐钠,位于F島的核電站,受9級(jí)特大地震影響役首,放射性物質(zhì)發(fā)生泄漏丹皱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一宋税、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧讼油,春花似錦杰赛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至瘦赫,卻和暖如春辰晕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背确虱。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工含友, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓窘问,卻偏偏與公主長(zhǎng)得像辆童,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子惠赫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348