android 中的ipc(進程間通信)

1.bundle :
簡單易用 但是只能傳輸Bundle支持的對象 常用于四大組件間進程間通信
2.文件共享:
簡單易用 但不適合在高并發的情況下 并且讀取文件需要時間 不能即時通信 常用于并發程度不高 并且實時性要求不高的情況
3.AIDL :
功能強大 支持一對多并發通信 支持即時通信 但是使用起來比其他的復雜 需要處理好多線程的同步問題 常用于一對多通信 且有RPC 需求的場合(服務端和客戶端通信)
4.Messenger :
功能一般 支持一對多串行通信 支持實時通信 但是不能很好處理高并發情況 只能傳輸Bundle支持的類型 常用于低并發的無RPC需求一對多的場合
5.ContentProvider :
在數據源訪問方面功能強大 支持一對多并發操作 可擴展call方法 可以理解為約束版的AIDL 提供CRUD操作和自定義函數 常用于一對多的數據共享場合
6.Socket :
功能強大 可以通過網絡傳輸字節流 支持一對多并發操作 但是實現起來比較麻煩 不支持直接的RPC 常用于網絡數據交換

總結起來
當僅僅是跨進程的四大組件間的傳遞數據時 使用Bundle就可以 簡單方便
當要共享一個應用程序的內部數據的時候 使用ContentProvider實現比較方便
當并發程度不高 也就是偶爾訪問一次那種 進程間通信 用Messenger就可以
當設計網絡數據的共享時 使用socket
當需求比較復雜 高并發 并且還要求實時通信 而且有RPC(遠程過程調用協議)需求時 就得使用AIDL了
文件共享的方法用于一些緩存共享 之類的功能

Android 從下而上分了內核層、硬件抽象層、系統服務層、Binder IPC 層、應用程序框架層
Android 中「應用程序框架層」以 SDK 的形式開放給開發者使用,「系統服務層」中的核心服務隨系統啟動而運行,通過應用層序框架層提供的 Manager 實時為應用程序提供服務調用。系統服務層中每一個服務運行在自己獨立的進程空間中,應用程序框架層中的 Manager 通過 Binder IPC 的方式調用系統服務層中的服務。

Binder IPC 的架構

下面我們就來看看 Binder IPC 的架構是怎樣的

2628445-8e398eb2bcd029ba.png.jpg

Binder IPC 屬于 C/S 結構,Client 部分是用戶代碼,用戶代碼最終會調用 Binder Driver 的 transact 接口,Binder Driver 會調用 Server,這里的 Server 與 service 不同,可以理解為 Service 中 onBind 返回的 Binder 對象,請注意區分下。

  • Client:用戶需要實現的代碼,如 AIDL 自動生成的接口類
  • Binder Driver:在內核層實現的 Driver
  • Server:這個 Server 就是 Service 中 onBind 返回的 IBinder 對象

需要注意的是,上面綠色的色塊部分都是屬于用戶需要實現的部分,而藍色部分是系統去實現了。也就是說 Binder Driver 這塊并不需要知道,Server 中會開啟一個線程池去處理客戶端調用。為什么要用線程池而不是一個單線程隊列呢?試想一下,如果用單線程隊列,則會有任務積壓,多個客戶端同時調用一個服務的時候就會有來不及響應的情況發生,這是絕對不允許的。

對于調用 Binder Driver 中的 transact 接口,客戶端可以手動調用,也可以通過 AIDL 的方式生成的代理類來調用,服務端可以繼承 Binder 對象,也可以繼承 AIDL 生成的接口類的 Stub 對象。這些細節下面繼續接著說,這里暫時不展開。

切記,這里 Server 的實現是線程池的方式,而不是單線程隊列的方式,區別在于,單線程隊列的話,Server 的代碼是線程安全的,線程池的話,Server 的代碼則不是線程安全的,需要開發者自己做好多線程同步。

小結

  • Binder IPC 屬于 C/S 架構,包括 Client、Driver、Server 三個部分
  • Client 可以手動調用 Driver 的 transact 接口,也可以通過 AIDL 生成的 Proxy 調用
  • Server 中會啟動一個「線程池」來處理 Client 的調用請求,處理完成后將結果返回給 Driver,Driver 再返回給 Client

這里就回答了開篇提問的兩個問題:Service 中通過 AIDL 提供的接口并不是線程安全的,同理 ContentProvider 底層也是使用 Binder,同樣不是線程安全的,至于是否需要做多線程保護,看業務而定,最好是做好多線程同步,以防萬一。

什么是Binder

這個問題很多文章都有解釋,比如:Binder是Android跨進程通信方式,它實現了IBinder接口,是ServiceManager連接各種Manager(如WindowManager、ActivityManager等)的橋梁。但是我覺得這些說法還是過于抽象。剛接觸Binder時,看到這些定義還是一頭霧水,只是內心覺得Binder很牛逼、很底層,僅此而已。

那么應該怎么去理解Binder呢?我不打算介紹這個概念,而是介紹Binder是怎么來到Android世界的。我是這樣理解的:Android團隊想要實現進程之間的通信,需要解決以下幾個問題:

    如何知道客戶端需要調用哪個進程以及該進程中的函數
    客戶端如何將函數形參發送給遠程進程中的函數,以及如何將遠程進程函數計算結果返回客戶端
    如何去屏蔽底層通信細節,讓實現客戶端調用遠程函數就像調用本地函數一樣

第一個問題,很容易解決,只要給每個需要遠程通信的類唯一標識就可以通過包名+類名的字符串就可以做到,然后在類里面給每個函數編號即可對函數唯一編碼。第二個問題,定義一個可打包的接口Parcelable,這個接口提供2個重要函數,分別是將對象中的屬性寫入到數組和從數組中的數據還原對象,每個可以發送到遠程函數作為形參的對象只需實現Parcelable對象即可。Parcelable具體使用不再本文討論范圍。第三個問題,為了屏蔽進程之間的通信細節,那么Android團隊肯定在想,定義一個類,由這個類來實現這些細節。這個類應該做哪些事情呢?首先,這個類得幫用戶發送遠程請求并將拿到返回結果提交給用戶,這是最重要的功能了,有了這個功能,媽媽再也不用擔心我的進程通信。其次,如果我想實現服務端,什么時候客戶端調用我了,這些細節不用用戶操心。當然,這個類還要幫用戶封裝更多細節。既然打算定義這個類了,那總得取個響當當的名稱吧,什么?你說取名為Binder,好吧,那就叫Binder吧。Binder類既然封裝很多功能,那該怎么用這個類呢?讓客戶端去繼承還是服務端繼承呢?答案是服務端。接下來有個約定,本文后面所指的Binder類都是指遠程服務端的對象。服務端想要實現被跨進程訪問,就必須繼承Binder類。
首先我們看看我們的程序跨進程調用系統服務的簡單示例,實現浮動窗口部分代碼:

//獲取WindowManager服務引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);
//布局參數layoutParams相關設置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);
//添加view
wm.addView(view, layoutParams);

//獲取WindowManager服務引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);  
//布局參數layoutParams相關設置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);  
//添加view
wm.addView(view, layoutParams);  

系統服務都是運行在systemServer進程中,因此我們調用系統服務都是跨進程的調用。第2行代碼中,得到的wm是WindowManager對象的引用,第6行調用WindowManager的addView函數,將觸發遠程調用,調用的是運行在systemServer進程中的WindowManager的addView函數。是不是很想知道addView發生了什么?我們先看看Binder機制吧!看完Binder原理,再解釋!

Binder機制

先看看一般執行過程

代碼執行過程

假設你已經創建好服務端類MyService、客戶端類MyClient。在客戶端持有MyService的引用,并且調用了MyService的func函數,那么Android內部調用過程如下:


2154124-bd83d477ef791b81.png

看了這個圖以后,相信你對你的代碼在調用遠程進程函數時有個全局的認識。這張圖有一點很重要,就是客戶端當前線程會被掛起!因此,如果遠程進程是執行長時間的運算,請不要使用主線程去調用遠程函數,以防止ANR。

Binder的C/S架構

上面一節我們對遠程進程調用代碼執行過程有個初步了解,在Android開發中,我們大量使用到了系統Service,比如媒體播放、各種傳感器以及WindowManagerService等等等等(太多了~)。那么Android是怎么管理這些服務,并且讓用戶跨進程調用這些服務呢?首先我們看看調用系統服務的過程。在Android開機啟動過程中,Android會初始化系統的各種Service,并將這些Service向ServiceManager注冊(即讓ServiceManager管理)。客戶端想要得到具體的Service直接向ServiceManager要即可。客戶端首先向ServiceManager查詢得到具體的Service引用,然后通過這個引用向具體的服務端發送請求,服務端執行完成后就返回。


2154124-746b33a4cfad1b93.png

Binder驅動實現原理

一直以來,我有個困惑!!!這個困惑讓我迷茫了很久:客戶端持有遠程進程的某個對象引用,然后調用引用類中的函數,遠程進程的函數就執行了。我在想,憑什么?學過操作系統都知道,不同的進程之間是不共享資源的。也就是說,客戶端持有的這個對象跟遠程進程中的實際對象完全是兩個不同的對象。客戶端調用引用的對象跟遠程進程半毛錢關系都沒有,憑啥遠程進程就調用了執行了?相信也有一部分人跟我有同樣的困惑!仔細研讀一下下面這張圖,相信你會豁然開朗!


2154124-8788ce62c0c7384e.png

服務端跨進程的類都要繼承Binder類。我們所持有的Binder引用(即服務端的類引用)并不是實際真實的遠程Binder對象,我們的引用在Binder驅動里還要做一次映射。也就是說,設備驅動根據我們的引用對象找到對應的遠程進程。客戶端要調用遠程對象函數時,只需把數據寫入到Parcel,在調用所持有的Binder引用的transact()函數,transact函數執行過程中會把參數、標識符(標記遠程對象及其函數)等數據放入到Client的共享內存,Binder驅動從Client的共享內存中讀取數據,根據這些數據找到對應的遠程進程的共享內存,把數據拷貝到遠程進程的共享內存中,并通知遠程進程執行onTransact()函數,這個函數也是屬于Binder類。遠程進程Binder對象執行完成后,將得到的寫入自己的共享內存中,Binder驅動再將遠程進程的共享內存數據拷貝到客戶端的共享內存,并喚醒客戶端線程。

Binder機制運用

好了,現在對Binder機制已經理解了,我們再看看Android是怎么運用Binder的。再現前面代碼:

//獲取WindowManager服務引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);  
//布局參數layoutParams相關設置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);  
//添加view
wm.addView(view, layoutParams); 

這段代碼前面已經出現過。getSystemService(getApplication().WINDOW_SERVICE);函數內部原理就是向ServiceManager查詢標識符為getApplication().WINDOW_SERVICE的遠程對象的引用。即WindowManager對象的引用,這個引用的真正實現是WindowManager的某個代理。得到這個引用后,在調用addView時,真正的實現是在代理里面,代理把參數打包到Parcel對象中,然后調用transact函數(該函數繼承自Binder),再觸發Binder驅動的一系列調用過程,在Binder驅動實現原理一節中有具體介紹,忘記了的同學可以返回繼續看。關于Binder的代理對象,可以參考AIDL工具生成的代碼,這里不再具體介紹。

http://blog.csdn.net/flyingqr/article/details/71411795

http://blog.csdn.net/singleton1900/article/details/8434643

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

推薦閱讀更多精彩內容