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

Binder的原理

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

服務器端:當我們在服務端創建好了一個Binder對象后,內部就會開啟一個線程用于接收binder驅動發送的消息,收到消息后會執行相關的服務器代碼。

Binder驅動:當服務端成功創建一個Binder對象后,Binder驅動也會創建一個mRemote對象,該對象的類型也是Binder類,客戶就可以借助這個mRemote對象來訪問遠程服務,注意這里是借助,真正調用的時候需要將這個轉換成對應的對象,比如使用AIDL的時候就要轉換成AIDL對象。

客戶端:客戶端要想訪問Binder的遠程服務,就必須獲取遠程服務的Binder對象在binder驅動層對應的mRemote引用。當獲取到mRemote對象的引用后,就可以調用相應Binder對象的暴露給客戶端的方法(如果有方法的話)。

AIDL

AIDL的本質其實就是系統為我們提供了一種快速實現Binder的工具,我們完全可以不用AIDL,自己去寫代碼實現Binder,但是當你寫出來的時候會發現其實和AIDL自動生成的代碼一模一樣。我們接下來來分析一下原理,因為AIDL的實現其實就是快速實現Binder,所以原理自然離不開Binder。但是在分析原理之前,我們先將系統根據我們定義的AIDL文件自動生成的java文件分析一下。比較重要的就是Stub和它的內部代理類Proxy。我們說一下重要的方法:

asInterface(android.os.IBinder obj)
用于將服務器的Binder對象轉換成客戶端所需的AIDL接口類型的對象,這種轉換過程是區分進程的,如果客戶端和服務端位于統一進程,那么返回服務器的Stub對象本身,否則返回的是系統封裝后的Stub.proxy對象。

onTransact(int code,android.os.Parcel data,android.os.Parcel reply,int flags)
這個方法運行在服務端的Binder線程池中,當客戶端發起跨進程請求時,遠程請求會通過系統底層封裝后交由服務端onTransact方法來處理。這個方法有四個參數,分別是code ,data,reply,flags.code是確定客戶端請求的方法是哪個,data是目標方法所需的參數,reply是服務器端執行完后的返回值。如果這個方法返回false,那么客戶端的請求會失敗。

Proxy#getBookList
這里的getBookList方法就是在自定義的AIDL文件中定義的方法,這個方法運行在客戶端,當客戶端遠程調用此方法的時候,內部實現是這樣的:首先在代理類中創建該方法所需要的輸入型Parcel對象_data,輸出型Parcel對象_reply和返回值對象List;然后把該方法的參數信息寫入_data中,接著mRemote調用transact方法來發起RPC(遠程過程調用)請求, 同時當前線程掛起,然后服務端的onTransact方法會被調用,直到RPC返回后,當前線程繼續執行,并從_reply中取出RPC過程的返回結果并返回(如果有返回值的話),之前創建的參數其實就是onTransact()方法需要的參數。

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

服務端:因為要實現Binder,必須在服務器端創建一個Binder對象,如何創建呢?就是newAIDL接口中的Stub內部類,代碼示例如:

Binder mBinder=new IBookManager.Stub(){接口方法實現}

其中IBookManager是系統根據我們自己定義的IBookManager.AIDL所生成的類。

Binder驅動:在AIDL中,Binder驅動其實就是Service。

客戶端:要實現客戶端跨進程和服務端通信,必須獲得服務端的Binder對象在binder驅動層對應的mRemote引用,如何獲得呢?首先綁定遠程服務,綁定成功后的ServiceConnection中的IBinder service其實就是mRemote引用,但是因為是使用AIDL方式,所以需要在客戶端中調用IBookManager.Stub.asInterface(android.os.IBinder obj)方法將服務器返回的Binder對象轉換成AIDL接口,然后就可以通過這個接口去調用服務器的遠程方法了。

根據原理,我們得出AIDL的使用流程,其實很簡單,大致就是在服務端創建一個Service,然后創建一個Binder對象,最后在客戶端得到這個Binder對象。

AIDL使用流程:
先建立AIDL,如果在你建立的AIDL接口中,有自定義的類,那么,也需要建立這個類的AIDL,并且名字要完全相同。同時在使用的時候,一定要顯示的導入這個類。接下來的流程就是跟Binder的一樣了。

服務器端:創建Binder對象,并且實現接口中的方法。

客戶端:綁定service,得到Binder對象在驅動層對應的mRemote引用。

重點

1.當你在客戶端調用服務器的方法的時候,其實是通過代理去訪問,詳情可以看上面的重點方法介紹里的Proxy#getBookList,所以你在客戶端連續調用兩次服務器的同一個方法的時候,比如,這里的getBookList,你會發現,里面的對象都不一樣。因為每次在調用方法的時候,在代理類中都會創建該方法所需要的參數對象,所以里面的對象會變化。

2.AIDL中無法使用普通的接口,只能使用AIDL接口,并且實現AIDL接口的時候不能用implements,因為需要實現的接口其實是自定義接口.Stub,而不是自己定義的那個接口。使用implements無法實現。

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

4.對于AIDL中的in,outinout這里就直接附上一篇別人寫的博客,這篇博客講的很詳細,而且我也贊同他的觀點,紙上得來終覺淺,絕知此事要躬行。

5.當使用客戶端調用服務器的方法的時候,被調用的方法運行在服務器的Binder線程池中,同時客戶端會被掛起,如果此時服務端方法執行耗時的話,就會導致客戶端線程長時間阻塞,如果客戶端線程是UI線程的話,就會導致客戶端ANR,注意的是onServiceConnected(ComponentName name, IBinder service)onServiceDisconnected(ComponentName name)都運行在UI線程,所以不能在這里調用服務端耗時的方法。同理,對于服務端調用客戶端的方法的情況,比如服務端調用客戶端的listener中的方法的時候也是一樣。即服務端掛起,方法運行在客戶端的Binder線程池中。

6.當服務端因為某種異常原因停止,我們需要重新啟動服務端,這里有兩種方式,因為AIDL的底層是Binder,所以可以使用BinderlinkToDeathunlinkToDeath方法。還有一種方式是在onServiceDisconnected(ComponentName name)重新綁定。這兩個區別就是第二種方式可以訪問UI,第一種不行,因為像之前說的,onServiceDisconnected(ComponentName name)是運行在UI線程里的。而第一種方式使用的時候需要設置一個IBinder.DeathRecipient接口用于接收服務端binder因為特殊原因消失的通知,當收到通知的時候就會回調binderDied()方法,我們在這里unlinkToDeath并且重新綁定service。而這個binderDied()方法是運行在客戶端的Binder線程池中的。

Messenger的原理及使用

Messenger大致的原理是這樣的,因為Messenger的底層還是AIDL,所以,原理和AIDL差不多。

服務器:首先需要在服務器創建Binder對象,如何創建呢?通過Messenger來創建,所以我們需要先構造Messenger對象,對于Messenger的構造方法有兩種,如下:

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

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

所以我們需要先構造一個Handler,這個Handler的作用其實就是處理消息。然后我們再通過這個Handler來構造Messenger對象,這個Messenger對象其實就是將客戶端發送來的消息傳遞給Handler來處理,然后我們需要得到Binder對象,通過在ServiceonBind方法中return Messenger.getBinder(),這樣就得到了Binder對象。

Binder驅動:跟AIDL一樣,還是Service。

客戶端:也是需要得到服務端的Binder對象在binder驅動層對應的mRemote引用,獲得的方式是將ServiceConnection中的IBinder service當做參數傳入Messenger的構造函數中,如:

Messenger mService=new Messenger(service);

然后就可以用mService.send(msg)給服務器發消息。實現跨進程通信。
因為這里是借助Messenger,所以無法調用服務器端的方法,只能通過message來傳遞消息。而當服務器需要回應客戶端的時候,就需要客戶端提供一個Messenger,然后服務器得到這個Messenger,因為在就像客戶端向服務端發送請求的時候,也是服務器提供一個Messenger,然后客戶端得到這個Messenger。那么如何實現呢?因為客戶端和服務器已經建立了連接,所以只需要在客戶端發送消息的時候,通過消息的replyTo參數向服務器傳入一個Messenger,然后服務器在接收到客戶端的消息的時候得到通過messagereplyTo參數得到這個Messenger,然后利用這個向客戶端發送消息就可以了。主要代碼如下:
在客戶端發送消息給服務器的時候:

message.replyTo=clientMessenger;

服務器接收消息的時候

 Messenger clientMessenger=msg.replyTo;

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

重點

1.對于使用Messenger而言,底層其實是AIDL,但是沒有AIDL靈活,因為這是借助Messenger來發送消息從而進行消息的傳遞,不能直接調用服務端的方法,而使用AIDL是直接可以調用服務端的方法。
2.對于服務端的Messenger的作用是將客戶端傳遞的消息傳遞給Handler來處理,而客戶端的是發送消息給服務端。
3.Messenger是以串行的方式處理客戶端發來的消息,當消息多的時候就就不合適了。而AIDL是可以并發處理客戶端傳來的消息。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,036評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,846評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,025評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,016評論 2 375

推薦閱讀更多精彩內容

  • Jianwei's blog 首頁 分類 關于 歸檔 標簽 巧用Android多進程,微信,微博等主流App都在用...
    justCode_閱讀 5,943評論 1 23
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,789評論 18 139
  • Android跨進程通信IPC整體內容如下 1、Android跨進程通信IPC之1——Linux基礎2、Andro...
    隔壁老李頭閱讀 10,800評論 13 43
  • 一、Android IPC簡介 IPC是Inter-Process Communication的縮寫,含義就是進程...
    SeanMa閱讀 1,881評論 0 8
  • 簡單說Binder Binder算是Android中比較難懂的一部分內容了,但是非常的重要,要想研究Framewo...
    EsonJack閱讀 1,566評論 0 4