1.bundle :
簡單易用 但是只能傳輸Bundle支持的對象 常用于四大組件間進(jìn)程間通信
2.文件共享:
簡單易用 但不適合在高并發(fā)的情況下 并且讀取文件需要時間 不能即時通信 常用于并發(fā)程度不高 并且實時性要求不高的情況
3.AIDL :
功能強大 支持一對多并發(fā)通信 支持即時通信 但是使用起來比其他的復(fù)雜 需要處理好多線程的同步問題 常用于一對多通信 且有RPC 需求的場合(服務(wù)端和客戶端通信)
4.Messenger :
功能一般 支持一對多串行通信 支持實時通信 但是不能很好處理高并發(fā)情況 只能傳輸Bundle支持的類型 常用于低并發(fā)的無RPC需求一對多的場合
5.ContentProvider :
在數(shù)據(jù)源訪問方面功能強大 支持一對多并發(fā)操作 可擴(kuò)展call方法 可以理解為約束版的AIDL 提供CRUD操作和自定義函數(shù) 常用于一對多的數(shù)據(jù)共享場合
6.Socket :
功能強大 可以通過網(wǎng)絡(luò)傳輸字節(jié)流 支持一對多并發(fā)操作 但是實現(xiàn)起來比較麻煩 不支持直接的RPC 常用于網(wǎng)絡(luò)數(shù)據(jù)交換
總結(jié)起來
當(dāng)僅僅是跨進(jìn)程的四大組件間的傳遞數(shù)據(jù)時 使用Bundle就可以 簡單方便
當(dāng)要共享一個應(yīng)用程序的內(nèi)部數(shù)據(jù)的時候 使用ContentProvider實現(xiàn)比較方便
當(dāng)并發(fā)程度不高 也就是偶爾訪問一次那種 進(jìn)程間通信 用Messenger就可以
當(dāng)設(shè)計網(wǎng)絡(luò)數(shù)據(jù)的共享時 使用socket
當(dāng)需求比較復(fù)雜 高并發(fā) 并且還要求實時通信 而且有RPC(遠(yuǎn)程過程調(diào)用協(xié)議)需求時 就得使用AIDL了
文件共享的方法用于一些緩存共享 之類的功能
Android 從下而上分了內(nèi)核層、硬件抽象層、系統(tǒng)服務(wù)層、Binder IPC 層、應(yīng)用程序框架層
Android 中「應(yīng)用程序框架層」以 SDK 的形式開放給開發(fā)者使用,「系統(tǒng)服務(wù)層」中的核心服務(wù)隨系統(tǒng)啟動而運行,通過應(yīng)用層序框架層提供的 Manager 實時為應(yīng)用程序提供服務(wù)調(diào)用。系統(tǒng)服務(wù)層中每一個服務(wù)運行在自己獨立的進(jìn)程空間中,應(yīng)用程序框架層中的 Manager 通過 Binder IPC 的方式調(diào)用系統(tǒng)服務(wù)層中的服務(wù)。
Binder IPC 的架構(gòu)
下面我們就來看看 Binder IPC 的架構(gòu)是怎樣的

Binder IPC 屬于 C/S 結(jié)構(gòu),Client 部分是用戶代碼,用戶代碼最終會調(diào)用 Binder Driver 的 transact 接口,Binder Driver 會調(diào)用 Server,這里的 Server 與 service 不同,可以理解為 Service 中 onBind 返回的 Binder 對象,請注意區(qū)分下。
- Client:用戶需要實現(xiàn)的代碼,如 AIDL 自動生成的接口類
- Binder Driver:在內(nèi)核層實現(xiàn)的 Driver
- Server:這個 Server 就是 Service 中 onBind 返回的 IBinder 對象
需要注意的是,上面綠色的色塊部分都是屬于用戶需要實現(xiàn)的部分,而藍(lán)色部分是系統(tǒng)去實現(xiàn)了。也就是說 Binder Driver 這塊并不需要知道,Server 中會開啟一個線程池去處理客戶端調(diào)用。為什么要用線程池而不是一個單線程隊列呢?試想一下,如果用單線程隊列,則會有任務(wù)積壓,多個客戶端同時調(diào)用一個服務(wù)的時候就會有來不及響應(yīng)的情況發(fā)生,這是絕對不允許的。
對于調(diào)用 Binder Driver 中的 transact 接口,客戶端可以手動調(diào)用,也可以通過 AIDL 的方式生成的代理類來調(diào)用,服務(wù)端可以繼承 Binder 對象,也可以繼承 AIDL 生成的接口類的 Stub 對象。這些細(xì)節(jié)下面繼續(xù)接著說,這里暫時不展開。
切記,這里 Server 的實現(xiàn)是線程池的方式,而不是單線程隊列的方式,區(qū)別在于,單線程隊列的話,Server 的代碼是線程安全的,線程池的話,Server 的代碼則不是線程安全的,需要開發(fā)者自己做好多線程同步。
小結(jié)
- Binder IPC 屬于 C/S 架構(gòu),包括 Client、Driver、Server 三個部分
- Client 可以手動調(diào)用 Driver 的 transact 接口,也可以通過 AIDL 生成的 Proxy 調(diào)用
- Server 中會啟動一個「線程池」來處理 Client 的調(diào)用請求,處理完成后將結(jié)果返回給 Driver,Driver 再返回給 Client
這里就回答了開篇提問的兩個問題:Service 中通過 AIDL 提供的接口并不是線程安全的,同理 ContentProvider 底層也是使用 Binder,同樣不是線程安全的,至于是否需要做多線程保護(hù),看業(yè)務(wù)而定,最好是做好多線程同步,以防萬一。
什么是Binder
這個問題很多文章都有解釋,比如:Binder是Android跨進(jìn)程通信方式,它實現(xiàn)了IBinder接口,是ServiceManager連接各種Manager(如WindowManager、ActivityManager等)的橋梁。但是我覺得這些說法還是過于抽象。剛接觸Binder時,看到這些定義還是一頭霧水,只是內(nèi)心覺得Binder很牛逼、很底層,僅此而已。
那么應(yīng)該怎么去理解Binder呢?我不打算介紹這個概念,而是介紹Binder是怎么來到Android世界的。我是這樣理解的:Android團(tuán)隊想要實現(xiàn)進(jìn)程之間的通信,需要解決以下幾個問題:
如何知道客戶端需要調(diào)用哪個進(jìn)程以及該進(jìn)程中的函數(shù)
客戶端如何將函數(shù)形參發(fā)送給遠(yuǎn)程進(jìn)程中的函數(shù),以及如何將遠(yuǎn)程進(jìn)程函數(shù)計算結(jié)果返回客戶端
如何去屏蔽底層通信細(xì)節(jié),讓實現(xiàn)客戶端調(diào)用遠(yuǎn)程函數(shù)就像調(diào)用本地函數(shù)一樣
第一個問題,很容易解決,只要給每個需要遠(yuǎn)程通信的類唯一標(biāo)識就可以通過包名+類名的字符串就可以做到,然后在類里面給每個函數(shù)編號即可對函數(shù)唯一編碼。第二個問題,定義一個可打包的接口Parcelable,這個接口提供2個重要函數(shù),分別是將對象中的屬性寫入到數(shù)組和從數(shù)組中的數(shù)據(jù)還原對象,每個可以發(fā)送到遠(yuǎn)程函數(shù)作為形參的對象只需實現(xiàn)Parcelable對象即可。Parcelable具體使用不再本文討論范圍。第三個問題,為了屏蔽進(jìn)程之間的通信細(xì)節(jié),那么Android團(tuán)隊肯定在想,定義一個類,由這個類來實現(xiàn)這些細(xì)節(jié)。這個類應(yīng)該做哪些事情呢?首先,這個類得幫用戶發(fā)送遠(yuǎn)程請求并將拿到返回結(jié)果提交給用戶,這是最重要的功能了,有了這個功能,媽媽再也不用擔(dān)心我的進(jìn)程通信。其次,如果我想實現(xiàn)服務(wù)端,什么時候客戶端調(diào)用我了,這些細(xì)節(jié)不用用戶操心。當(dāng)然,這個類還要幫用戶封裝更多細(xì)節(jié)。既然打算定義這個類了,那總得取個響當(dāng)當(dāng)?shù)拿Q吧,什么?你說取名為Binder,好吧,那就叫Binder吧。Binder類既然封裝很多功能,那該怎么用這個類呢?讓客戶端去繼承還是服務(wù)端繼承呢?答案是服務(wù)端。接下來有個約定,本文后面所指的Binder類都是指遠(yuǎn)程服務(wù)端的對象。服務(wù)端想要實現(xiàn)被跨進(jìn)程訪問,就必須繼承Binder類。
首先我們看看我們的程序跨進(jìn)程調(diào)用系統(tǒng)服務(wù)的簡單示例,實現(xiàn)浮動窗口部分代碼:
//獲取WindowManager服務(wù)引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);
//布局參數(shù)layoutParams相關(guān)設(shè)置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);
//添加view
wm.addView(view, layoutParams);
//獲取WindowManager服務(wù)引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);
//布局參數(shù)layoutParams相關(guān)設(shè)置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);
//添加view
wm.addView(view, layoutParams);
系統(tǒng)服務(wù)都是運行在systemServer進(jìn)程中,因此我們調(diào)用系統(tǒng)服務(wù)都是跨進(jìn)程的調(diào)用。第2行代碼中,得到的wm是WindowManager對象的引用,第6行調(diào)用WindowManager的addView函數(shù),將觸發(fā)遠(yuǎn)程調(diào)用,調(diào)用的是運行在systemServer進(jìn)程中的WindowManager的addView函數(shù)。是不是很想知道addView發(fā)生了什么?我們先看看Binder機(jī)制吧!看完Binder原理,再解釋!
Binder機(jī)制
先看看一般執(zhí)行過程
代碼執(zhí)行過程
假設(shè)你已經(jīng)創(chuàng)建好服務(wù)端類MyService、客戶端類MyClient。在客戶端持有MyService的引用,并且調(diào)用了MyService的func函數(shù),那么Android內(nèi)部調(diào)用過程如下:

看了這個圖以后,相信你對你的代碼在調(diào)用遠(yuǎn)程進(jìn)程函數(shù)時有個全局的認(rèn)識。這張圖有一點很重要,就是客戶端當(dāng)前線程會被掛起!因此,如果遠(yuǎn)程進(jìn)程是執(zhí)行長時間的運算,請不要使用主線程去調(diào)用遠(yuǎn)程函數(shù),以防止ANR。
Binder的C/S架構(gòu)
上面一節(jié)我們對遠(yuǎn)程進(jìn)程調(diào)用代碼執(zhí)行過程有個初步了解,在Android開發(fā)中,我們大量使用到了系統(tǒng)Service,比如媒體播放、各種傳感器以及WindowManagerService等等等等(太多了~)。那么Android是怎么管理這些服務(wù),并且讓用戶跨進(jìn)程調(diào)用這些服務(wù)呢?首先我們看看調(diào)用系統(tǒng)服務(wù)的過程。在Android開機(jī)啟動過程中,Android會初始化系統(tǒng)的各種Service,并將這些Service向ServiceManager注冊(即讓ServiceManager管理)??蛻舳讼胍玫骄唧w的Service直接向ServiceManager要即可??蛻舳耸紫认騍erviceManager查詢得到具體的Service引用,然后通過這個引用向具體的服務(wù)端發(fā)送請求,服務(wù)端執(zhí)行完成后就返回。

Binder驅(qū)動實現(xiàn)原理
一直以來,我有個困惑!?。∵@個困惑讓我迷茫了很久:客戶端持有遠(yuǎn)程進(jìn)程的某個對象引用,然后調(diào)用引用類中的函數(shù),遠(yuǎn)程進(jìn)程的函數(shù)就執(zhí)行了。我在想,憑什么?學(xué)過操作系統(tǒng)都知道,不同的進(jìn)程之間是不共享資源的。也就是說,客戶端持有的這個對象跟遠(yuǎn)程進(jìn)程中的實際對象完全是兩個不同的對象??蛻舳苏{(diào)用引用的對象跟遠(yuǎn)程進(jìn)程半毛錢關(guān)系都沒有,憑啥遠(yuǎn)程進(jìn)程就調(diào)用了執(zhí)行了?相信也有一部分人跟我有同樣的困惑!仔細(xì)研讀一下下面這張圖,相信你會豁然開朗!

服務(wù)端跨進(jìn)程的類都要繼承Binder類。我們所持有的Binder引用(即服務(wù)端的類引用)并不是實際真實的遠(yuǎn)程Binder對象,我們的引用在Binder驅(qū)動里還要做一次映射。也就是說,設(shè)備驅(qū)動根據(jù)我們的引用對象找到對應(yīng)的遠(yuǎn)程進(jìn)程??蛻舳艘{(diào)用遠(yuǎn)程對象函數(shù)時,只需把數(shù)據(jù)寫入到Parcel,在調(diào)用所持有的Binder引用的transact()函數(shù),transact函數(shù)執(zhí)行過程中會把參數(shù)、標(biāo)識符(標(biāo)記遠(yuǎn)程對象及其函數(shù))等數(shù)據(jù)放入到Client的共享內(nèi)存,Binder驅(qū)動從Client的共享內(nèi)存中讀取數(shù)據(jù),根據(jù)這些數(shù)據(jù)找到對應(yīng)的遠(yuǎn)程進(jìn)程的共享內(nèi)存,把數(shù)據(jù)拷貝到遠(yuǎn)程進(jìn)程的共享內(nèi)存中,并通知遠(yuǎn)程進(jìn)程執(zhí)行onTransact()函數(shù),這個函數(shù)也是屬于Binder類。遠(yuǎn)程進(jìn)程Binder對象執(zhí)行完成后,將得到的寫入自己的共享內(nèi)存中,Binder驅(qū)動再將遠(yuǎn)程進(jìn)程的共享內(nèi)存數(shù)據(jù)拷貝到客戶端的共享內(nèi)存,并喚醒客戶端線程。
Binder機(jī)制運用
好了,現(xiàn)在對Binder機(jī)制已經(jīng)理解了,我們再看看Android是怎么運用Binder的。再現(xiàn)前面代碼:
//獲取WindowManager服務(wù)引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);
//布局參數(shù)layoutParams相關(guān)設(shè)置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);
//添加view
wm.addView(view, layoutParams);
這段代碼前面已經(jīng)出現(xiàn)過。getSystemService(getApplication().WINDOW_SERVICE);函數(shù)內(nèi)部原理就是向ServiceManager查詢標(biāo)識符為getApplication().WINDOW_SERVICE的遠(yuǎn)程對象的引用。即WindowManager對象的引用,這個引用的真正實現(xiàn)是WindowManager的某個代理。得到這個引用后,在調(diào)用addView時,真正的實現(xiàn)是在代理里面,代理把參數(shù)打包到Parcel對象中,然后調(diào)用transact函數(shù)(該函數(shù)繼承自Binder),再觸發(fā)Binder驅(qū)動的一系列調(diào)用過程,在Binder驅(qū)動實現(xiàn)原理一節(jié)中有具體介紹,忘記了的同學(xué)可以返回繼續(xù)看。關(guān)于Binder的代理對象,可以參考AIDL工具生成的代碼,這里不再具體介紹。