超簡單的Binder,AIDL和Messenger的原理及使用流程

Binder的原理

要想了解AIDL就需要先了解Binder的原理斗搞,所以這里先說一下Binder原理逗堵,Binder的原理大概是這樣:

服務(wù)器端:當(dāng)我們在服務(wù)端創(chuàng)建好了一個Binder對象后,內(nèi)部就會開啟一個線程用于接收binder驅(qū)動發(fā)送的消息刽漂,收到消息后會執(zhí)行相關(guān)的服務(wù)器代碼渴肉。

Binder驅(qū)動:當(dāng)服務(wù)端成功創(chuàng)建一個Binder對象后,Binder驅(qū)動也會創(chuàng)建一個mRemote對象爽冕,該對象的類型也是Binder類仇祭,客戶就可以借助這個mRemote對象來訪問遠(yuǎn)程服務(wù),注意這里是借助颈畸,真正調(diào)用的時候需要將這個轉(zhuǎn)換成對應(yīng)的對象乌奇,比如使用AIDL的時候就要轉(zhuǎn)換成AIDL對象。

客戶端:客戶端要想訪問Binder的遠(yuǎn)程服務(wù)眯娱,就必須獲取遠(yuǎn)程服務(wù)的Binder對象在binder驅(qū)動層對應(yīng)的mRemote引用礁苗。當(dāng)獲取到mRemote對象的引用后,就可以調(diào)用相應(yīng)Binder對象的暴露給客戶端的方法(如果有方法的話)徙缴。

AIDL

AIDL的本質(zhì)其實(shí)就是系統(tǒng)為我們提供了一種快速實(shí)現(xiàn)Binder的工具试伙,我們完全可以不用AIDL,自己去寫代碼實(shí)現(xiàn)Binder于样,但是當(dāng)你寫出來的時候會發(fā)現(xiàn)其實(shí)和AIDL自動生成的代碼一模一樣疏叨。我們接下來來分析一下原理,因?yàn)?code>AIDL的實(shí)現(xiàn)其實(shí)就是快速實(shí)現(xiàn)Binder穿剖,所以原理自然離不開Binder蚤蔓。但是在分析原理之前,我們先將系統(tǒng)根據(jù)我們定義的AIDL文件自動生成的java文件分析一下糊余。比較重要的就是Stub和它的內(nèi)部代理類Proxy秀又。我們說一下重要的方法:

asInterface(android.os.IBinder obj)
用于將服務(wù)器的Binder對象轉(zhuǎn)換成客戶端所需的AIDL接口類型的對象,這種轉(zhuǎn)換過程是區(qū)分進(jìn)程的贬芥,如果客戶端和服務(wù)端位于統(tǒng)一進(jìn)程吐辙,那么返回服務(wù)器的Stub對象本身,否則返回的是系統(tǒng)封裝后的Stub.proxy對象蘸劈。

onTransact(int code,android.os.Parcel data,android.os.Parcel reply,int flags)
這個方法運(yùn)行在服務(wù)端的Binder線程池中昏苏,當(dāng)客戶端發(fā)起跨進(jìn)程請求時,遠(yuǎn)程請求會通過系統(tǒng)底層封裝后交由服務(wù)端onTransact方法來處理。這個方法有四個參數(shù)捷雕,分別是code 椒丧,datareply救巷,flags.code是確定客戶端請求的方法是哪個壶熏,data是目標(biāo)方法所需的參數(shù),reply是服務(wù)器端執(zhí)行完后的返回值浦译。如果這個方法返回false棒假,那么客戶端的請求會失敗。

Proxy#getBookList
這里的getBookList方法就是在自定義的AIDL文件中定義的方法精盅,這個方法運(yùn)行在客戶端帽哑,當(dāng)客戶端遠(yuǎn)程調(diào)用此方法的時候,內(nèi)部實(shí)現(xiàn)是這樣的:首先在代理類中創(chuàng)建該方法所需要的輸入型Parcel對象_data叹俏,輸出型Parcel對象_reply和返回值對象List;然后把該方法的參數(shù)信息寫入_data中妻枕,接著mRemote調(diào)用transact方法來發(fā)起RPC(遠(yuǎn)程過程調(diào)用)請求, 同時當(dāng)前線程掛起粘驰,然后服務(wù)端的onTransact方法會被調(diào)用屡谐,直到RPC返回后,當(dāng)前線程繼續(xù)執(zhí)行蝌数,并從_reply中取出RPC過程的返回結(jié)果并返回(如果有返回值的話)愕掏,之前創(chuàng)建的參數(shù)其實(shí)就是onTransact()方法需要的參數(shù)。

說完了重要方法顶伞,接下來分析AIDL原理:

服務(wù)端:因?yàn)橐獙?shí)現(xiàn)Binder饵撑,必須在服務(wù)器端創(chuàng)建一個Binder對象,如何創(chuàng)建呢唆貌?就是newAIDL接口中的Stub內(nèi)部類滑潘,代碼示例如:

Binder mBinder=new IBookManager.Stub(){接口方法實(shí)現(xiàn)}

其中IBookManager是系統(tǒng)根據(jù)我們自己定義的IBookManager.AIDL所生成的類。

Binder驅(qū)動:在AIDL中挠锥,Binder驅(qū)動其實(shí)就是Service众羡。

客戶端:要實(shí)現(xiàn)客戶端跨進(jìn)程和服務(wù)端通信侨赡,必須獲得服務(wù)端的Binder對象在binder驅(qū)動層對應(yīng)的mRemote引用蓖租,如何獲得呢?首先綁定遠(yuǎn)程服務(wù)羊壹,綁定成功后的ServiceConnection中的IBinder service其實(shí)就是mRemote引用蓖宦,但是因?yàn)槭鞘褂?code>AIDL方式,所以需要在客戶端中調(diào)用IBookManager.Stub.asInterface(android.os.IBinder obj)方法將服務(wù)器返回的Binder對象轉(zhuǎn)換成AIDL接口油猫,然后就可以通過這個接口去調(diào)用服務(wù)器的遠(yuǎn)程方法了稠茂。

根據(jù)原理,我們得出AIDL的使用流程,其實(shí)很簡單睬关,大致就是在服務(wù)端創(chuàng)建一個Service诱担,然后創(chuàng)建一個Binder對象,最后在客戶端得到這個Binder對象电爹。

AIDL使用流程:
先建立AIDL蔫仙,如果在你建立的AIDL接口中,有自定義的類丐箩,那么摇邦,也需要建立這個類的AIDL,并且名字要完全相同屎勘。同時在使用的時候施籍,一定要顯示的導(dǎo)入這個類。接下來的流程就是跟Binder的一樣了概漱。

服務(wù)器端:創(chuàng)建Binder對象丑慎,并且實(shí)現(xiàn)接口中的方法。

客戶端:綁定service瓤摧,得到Binder對象在驅(qū)動層對應(yīng)的mRemote引用立哑。

重點(diǎn)

1.當(dāng)你在客戶端調(diào)用服務(wù)器的方法的時候,其實(shí)是通過代理去訪問姻灶,詳情可以看上面的重點(diǎn)方法介紹里的Proxy#getBookList铛绰,所以你在客戶端連續(xù)調(diào)用兩次服務(wù)器的同一個方法的時候,比如产喉,這里的getBookList捂掰,你會發(fā)現(xiàn),里面的對象都不一樣曾沈。因?yàn)槊看卧谡{(diào)用方法的時候这嚣,在代理類中都會創(chuàng)建該方法所需要的參數(shù)對象,所以里面的對象會變化塞俱。

2.AIDL中無法使用普通的接口姐帚,只能使用AIDL接口,并且實(shí)現(xiàn)AIDL接口的時候不能用implements障涯,因?yàn)樾枰獙?shí)現(xiàn)的接口其實(shí)是自定義接口.Stub罐旗,而不是自己定義的那個接口。使用implements無法實(shí)現(xiàn)唯蝶。

3.解注冊的時候需要使用到RemoteCallbackList九秀,需要注意的是這個類的beginBroadcast()finishBroadcast()一定要配對使用,否則會出現(xiàn)異常java.lang.IllegalStateException: beginBroadcast() called while already in a broadcast粘我,特別是在使用for循環(huán)的時候鼓蜒。

4.對于AIDL中的inoutinout這里就直接附上一篇別人寫的博客都弹,這篇博客講的很詳細(xì)娇豫,而且我也贊同他的觀點(diǎn),紙上得來終覺淺畅厢,絕知此事要躬行锤躁。

5.當(dāng)使用客戶端調(diào)用服務(wù)器的方法的時候,被調(diào)用的方法運(yùn)行在服務(wù)器的Binder線程池中或详,同時客戶端會被掛起系羞,如果此時服務(wù)端方法執(zhí)行耗時的話,就會導(dǎo)致客戶端線程長時間阻塞霸琴,如果客戶端線程是UI線程的話椒振,就會導(dǎo)致客戶端ANR,注意的是onServiceConnected(ComponentName name, IBinder service)onServiceDisconnected(ComponentName name)都運(yùn)行在UI線程梧乘,所以不能在這里調(diào)用服務(wù)端耗時的方法澎迎。同理,對于服務(wù)端調(diào)用客戶端的方法的情況选调,比如服務(wù)端調(diào)用客戶端的listener中的方法的時候也是一樣夹供。即服務(wù)端掛起,方法運(yùn)行在客戶端的Binder線程池中仁堪。

6.當(dāng)服務(wù)端因?yàn)槟撤N異常原因停止哮洽,我們需要重新啟動服務(wù)端,這里有兩種方式弦聂,因?yàn)?code>AIDL的底層是Binder鸟辅,所以可以使用BinderlinkToDeathunlinkToDeath方法。還有一種方式是在onServiceDisconnected(ComponentName name)重新綁定莺葫。這兩個區(qū)別就是第二種方式可以訪問UI匪凉,第一種不行,因?yàn)橄裰罢f的捺檬,onServiceDisconnected(ComponentName name)是運(yùn)行在UI線程里的再层。而第一種方式使用的時候需要設(shè)置一個IBinder.DeathRecipient接口用于接收服務(wù)端binder因?yàn)樘厥庠蛳У耐ㄖ?dāng)收到通知的時候就會回調(diào)binderDied()方法堡纬,我們在這里unlinkToDeath并且重新綁定service聂受。而這個binderDied()方法是運(yùn)行在客戶端的Binder線程池中的。

Messenger的原理及使用

Messenger大致的原理是這樣的隐轩,因?yàn)镸essenger的底層還是AIDL饺饭,所以,原理和AIDL差不多职车。

服務(wù)器:首先需要在服務(wù)器創(chuàng)建Binder對象,如何創(chuàng)建呢?通過Messenger來創(chuàng)建悴灵,所以我們需要先構(gòu)造Messenger對象,對于Messenger的構(gòu)造方法有兩種扛芽,如下:

public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);
}

 public Messenger(Handler target) {
    mTarget = target.getIMessenger();
}

所以我們需要先構(gòu)造一個Handler,這個Handler的作用其實(shí)就是處理消息积瞒。然后我們再通過這個Handler來構(gòu)造Messenger對象川尖,這個Messenger對象其實(shí)就是將客戶端發(fā)送來的消息傳遞給Handler來處理,然后我們需要得到Binder對象茫孔,通過在ServiceonBind方法中return Messenger.getBinder()叮喳,這樣就得到了Binder對象。

Binder驅(qū)動:跟AIDL一樣缰贝,還是Service馍悟。

客戶端:也是需要得到服務(wù)端的Binder對象在binder驅(qū)動層對應(yīng)的mRemote引用,獲得的方式是將ServiceConnection中的IBinder service當(dāng)做參數(shù)傳入Messenger的構(gòu)造函數(shù)中剩晴,如:

Messenger mService=new Messenger(service)锣咒;

然后就可以用mService.send(msg)給服務(wù)器發(fā)消息。實(shí)現(xiàn)跨進(jìn)程通信赞弥。
因?yàn)檫@里是借助Messenger毅整,所以無法調(diào)用服務(wù)器端的方法,只能通過message來傳遞消息绽左。而當(dāng)服務(wù)器需要回應(yīng)客戶端的時候悼嫉,就需要客戶端提供一個Messenger,然后服務(wù)器得到這個Messenger拼窥,因?yàn)樵诰拖窨蛻舳讼蚍?wù)端發(fā)送請求的時候承粤,也是服務(wù)器提供一個Messenger,然后客戶端得到這個Messenger闯团。那么如何實(shí)現(xiàn)呢辛臊?因?yàn)榭蛻舳撕头?wù)器已經(jīng)建立了連接,所以只需要在客戶端發(fā)送消息的時候房交,通過消息的replyTo參數(shù)向服務(wù)器傳入一個Messenger彻舰,然后服務(wù)器在接收到客戶端的消息的時候得到通過messagereplyTo參數(shù)得到這個Messenger,然后利用這個向客戶端發(fā)送消息就可以了候味。主要代碼如下:
在客戶端發(fā)送消息給服務(wù)器的時候:

message.replyTo=clientMessenger;

服務(wù)器接收消息的時候

 Messenger clientMessenger=msg.replyTo;

這樣就在服務(wù)器端得到了客戶端的Messenger刃唤,然后在服務(wù)器端通過clientMessenger.send(message);就向客戶端發(fā)送了消息。

重點(diǎn)

1.對于使用Messenger而言白群,底層其實(shí)是AIDL尚胞,但是沒有AIDL靈活,因?yàn)檫@是借助Messenger來發(fā)送消息從而進(jìn)行消息的傳遞帜慢,不能直接調(diào)用服務(wù)端的方法笼裳,而使用AIDL是直接可以調(diào)用服務(wù)端的方法唯卖。
2.對于服務(wù)端的Messenger的作用是將客戶端傳遞的消息傳遞給Handler來處理,而客戶端的是發(fā)送消息給服務(wù)端躬柬。
3.Messenger是以串行的方式處理客戶端發(fā)來的消息拜轨,當(dāng)消息多的時候就就不合適了。而AIDL是可以并發(fā)處理客戶端傳來的消息允青。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末橄碾,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子颠锉,更是在濱河造成了極大的恐慌法牲,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件琼掠,死亡現(xiàn)場離奇詭異拒垃,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)眉枕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門恶复,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人速挑,你說我怎么就攤上這事谤牡。” “怎么了姥宝?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵翅萤,是天一觀的道長。 經(jīng)常有香客問我腊满,道長套么,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任碳蛋,我火速辦了婚禮胚泌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肃弟。我一直安慰自己玷室,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布笤受。 她就那樣靜靜地躺著穷缤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪箩兽。 梳的紋絲不亂的頭發(fā)上津肛,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音汗贫,去河邊找鬼身坐。 笑死秸脱,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的掀亥。 我是一名探鬼主播撞反,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼妥色,長吁一口氣:“原來是場噩夢啊……” “哼搪花!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嘹害,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤撮竿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后笔呀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體幢踏,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年许师,在試婚紗的時候發(fā)現(xiàn)自己被綠了房蝉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡微渠,死狀恐怖搭幻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情逞盆,我是刑警寧澤檀蹋,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站云芦,受9級特大地震影響俯逾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜舅逸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一桌肴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧琉历,春花似錦坠七、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至换团,卻和暖如春悉稠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背艘包。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工的猛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耀盗,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓卦尊,卻偏偏與公主長得像叛拷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子岂却,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Jianwei's blog 首頁 分類 關(guān)于 歸檔 標(biāo)簽 巧用Android多進(jìn)程忿薇,微信,微博等主流App都在用...
    justCode_閱讀 5,900評論 1 23
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理躏哩,服務(wù)發(fā)現(xiàn)署浩,斷路器,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • Android跨進(jìn)程通信IPC整體內(nèi)容如下 1扫尺、Android跨進(jìn)程通信IPC之1——Linux基礎(chǔ)2筋栋、Andro...
    隔壁老李頭閱讀 10,721評論 13 43
  • 一、Android IPC簡介 IPC是Inter-Process Communication的縮寫正驻,含義就是進(jìn)程...
    SeanMa閱讀 1,770評論 0 8
  • 簡單說Binder Binder算是Android中比較難懂的一部分內(nèi)容了弊攘,但是非常的重要,要想研究Framewo...
    EsonJack閱讀 1,540評論 0 4