Android進程間通信基於Proxy(代理)與Stub(樁或存根)的設計模式(如圖1-1所示)。其中,Proxy將特殊性接口轉換成通用性接口,Stub將通用性接口轉換成特殊性接口,二者之間的數據轉換通過Parcel(打包)進行的,Proxy常作為數據發送代理,通過Parcel將數據打包發送,Stub常作為數據接收樁,解包並解析Parcel Data package。Android進程間通信就是通過這樣的 “代理-樁” 的設計模式運作的。
圖 1-1
關於Proxy與Stub注意:
- Stub 跟 Proxy 是一對,俗稱“代理-樁”,一般用在遠程方法調用。
- Proxy 相當於是拿在手里的遙控器,而 Stub 相當於長在電視機里的遙控接收器,它們有着一一對應的接口方法,但操作的方向剛好相反。
- Proxy 的接口供客戶端程序調用,然后它內部會把信息包裝好,以某種方式(比如 RMI)傳遞給 Stub,而后者通過對應的接口作用於服務端系統,從而完成了“遠程調用”。
- 一般不同進程間通信的時候都會用到這種模式。
- 關於Stub的asInterface(Binder), 可以返回Stub或Stub.Proxy(如果客戶端和服務端在同一個進程下,那么asInterface()將返回Stub對象本身,否則返回Stub.Proxy對象)。我們都知道,Binder的工作機制由客戶端,Binder,服務端組成的,客戶端和服務端都是通過Binder來交流的(Binder也是Android中一個java類)。AIDL生成的java代碼中,Stub類是繼承於Binder類的,也就是說Stub實例就是Binder實例。
- Stub和Stub.Proxy的區別:(1)如果在同一個進程下的話,那么asInterface()將返回服務端的Stub對象本身,因為此時根本不需要跨進稱通信,那么直接調用Stub對象的接口就可以了,返回的實現就是服務端的Stub實現,也就是根本沒有跨進程通信;(2)如果不是同一個進程,那么asInterface()返回是Stub.Proxy對象,該對象持有着遠程的Binder引用,因為現在需要跨進程通信,所以如果調用Stub.Proxy的接口的話,那么它們都將是IPC調用,它會通過調用transact方法去與服務端通信。
關於AIDL定義以及實現流程圖:
AIDL是一個縮寫,全稱是Android Interface Definition Language,也就是Android接口定義語言。我們在寫完AIDL文件后,編譯器會幫我們自動生成一個同名的 .java 文件(在gen相應目錄下)。在服務端和客戶端中也可以照常使用這個 .java 類來進行跨進程通信。
關於Proxy類幾個對象及方法(客戶端最終通過這個Proxy類與服務端進行通信):
- 關於 _data 與 _reply 對象:一般來說,我們會將方法的傳參的數據存入_data 中,而將方法的返回值的數據存入 _reply 中—在沒涉及定向 tag 的情況下。如果涉及了定向 tag ,情況將會變得稍微復雜些,具體是怎么回事請參見這篇博文:你真的理解AIDL中的in,out,inout么?
- 關於 Parcel :簡單的來說,Parcel 是一個用來存放和讀取數據的容器。我們可以用它來進行客戶端和服務端之間的數據傳輸,當然,它能傳輸的只能是可序列化的數據。具體 Parcel 的使用方法和相關原理可以參見這篇文章:Android中Parcel的分析以及使用
- 關於 transact() 方法:這是客戶端和服務端通信的核心方法。調用這個方法之后,客戶端將會掛起當前線程,等候服務端執行完相關任務后通知並接收返回的 _reply 數據流。關於這個方法的傳參,這里有兩點需要說明的地方:
- 方法 ID :transact() 方法的第一個參數是一個方法 ID ,這個是客戶端與服務端約定好的給方法的編碼,彼此一一對應。在AIDL文件轉化為 .java 文件的時候,系統將會自動給AIDL文件里面的每一個方法自動分配一個方法 ID。
- 第四個參數:transact() 方法的第四個參數是一個 int 值,它的作用是設置進行 IPC 的模式,為 0 表示數據可以雙向流通,即 _reply 流可以正常的攜帶數據回來,如果為 1 的話那么數據將只能單向流通,從服務端回來的 _reply 流將不攜帶任何數據。
注:AIDL生成的 .java 文件的這個參數均為 0。
關於客戶端一般的工作流程:
- 1,生成 _data 和 _reply 數據流,並向 _data 中存入客戶端的數據。
- 2,通過 transact() 方法將它們傳遞給服務端,並請求服務端調用指定方法。
- 3,接收 _reply 數據流,並從中取出服務端傳回來的數據。
關於服務端的一般工作流程:
- 1,獲取客戶端傳過來的數據,根據方法 ID 執行相應操作。
- 2,將傳過來的數據取出來,調用本地寫好的對應方法。
- 3,將需要回傳的數據寫入 reply 流,傳回客戶端。
關於Android中AIDL的簡單例子,參加如下Demo:
https://blog.csdn.net/jingwen3699/article/details/53400288
(注意:在Android Studio中,客戶端和服務端的AIDL接口文件所在的包未必相同,最好在gradle中配置路徑!)
其它參考鏈接:
https://blog.csdn.net/scnuxisan225/article/details/49970217
https://blog.csdn.net/a910626/article/details/51173668
https://blog.csdn.net/luoyanglizi/article/details/52029091 (詳細原理來分析,good)