Android中的常見通信機制和Linux中的通信機制
Handler
Handler是Android系統中的一種消息傳遞機制,起作用是應對多線程場景。將A進程的消息傳遞給B線程,實現異步消息處理。很多情況是將工作線程中需要更新UI的操作消息傳遞給UI主線程,而實現更新UI操作。
因為工作線程和主線程是共享地址空間,即Handler實例對象mHandler位於線程間共享的內存堆上,工作線程和主線程直接使用該對象,只需要註意多線程的同步問題。工作系統通過mHandler向其成員變量MessageQueue中添加Message,而主線程一直處於loop中,當收到新的message時,按照一定規則分發給相應的handlerMessage方法來處理。
Handler用於同進程的線程間通信的核心是線程間共享內存空間,而不同矜持擁有不同的地址空間,也就無法使用handler來實現進程間通信。
圖中,首先Handler通過sendMessage()發送Message到MessageQueue隊列,Looper通過looper()不斷提取出達到觸發條件的Message,並將Message交給target來處理;然後通過displatchMessage()分發給handlerMessage處理。
將Message添加到MessageQueue時,會往管道中寫入字符,這樣就回喚醒loop線程;如果MessageQueue中沒有Message,並處理idle狀態,則會執行idleHandler接口中的方法,往往用於做一些清理性的工作。關於消息分發的優先級:
- Message回調分發:message callback.run()優先級最高。
- Handler回調分發:Handler.mCallback.handleMesage(msg),優先級次之。
- Handler默認分發:Handler.handlerMessage(msg),優先級最低。
Binder
概述
Android中一般每個應用對於一個進程,而涉及到每個應用之間的通信,即進程間通信,Android中采用的最多的IPC機制為Binder機制。首先我們介紹下IPC機制原理,如圖2所示為從進程角度闡述了IPC機制。從圖中可以看出,每個Android進程只能運行在自己所有的虛擬地址空間中。對於一個4G的虛擬地址空間,假設用戶空間為3G,內核空間為1G(可修改)。進程在用戶空間的數據是不可共享的,在內存空間中則可共享。Client進程想server進程發送信息,即利用內核空間可共享機制完成。
- 從IPC角度:Binder是Android中的一種跨進程通信方法,該方法是Android系統獨有的。
- 從Android Driver層:Binder還可以理解為一種虛擬的物理設備,設備驅動為/dev/binder。
- 從Android native層:Binder是創建Service Manager以及BpBinder/BBinder模型,大家與Binder驅動的橋梁。
- 從Android frameworks層:Binder是各種Manager(ActivityManager、WindowManager)和相應xxxManagerService的橋梁。
- 從APP層:Binder是客戶端和服務端進程通信的媒介,當bindServicer時,服務端會返回一個包含了服務端業務調用的Binder對象,通過這個Binder對象,客戶端就可以獲得服務端提供的服務(包括普通服務和基於AIDL的服務)或數據。
架構
如圖3所示,為Binder在Android不同層級的位置和關聯圖,是一種C/S架構的通信機制。在Kernel層,Binder可以看成是一個驅動,其驅動架構與其他驅動相同。Native層中ServiceManager會啟動一個Binder的守護進程,ServiceManager功能簡單,包括獲取服務、註冊服務,大部分同行通過都存在於BpBinder和BBinder之間。Framework層的Binder邏輯是建立在Native層架構基礎上的,核心邏輯都是交給Native層來處理。但需要註意下frameworks層的Binder邏輯是建立在Native層架構基礎上的,核心邏輯都是交給Native層來處理。但需要註意下frameworks層的ServiceManager和Native層的ServiceManager功能並不對應,其最終實現是通過BinderProxy傳遞給Native層來完成的。
Socket
Socket通信也是基於C/S架構,但相比Binder要簡單一些。首先我們來回顧下TCP/IP協議的知識。TCP/IP協議是一個四層的體系結構:應用層、傳輸層、網絡層、網絡接口層。在傳輸層中又有TCP和UDP兩個協議。Socket是工作與TCP/IP協議中應用層和傳輸層之間的一種抽象。Android系統中,又分為流套接字和數據報套接字。其中流套接字將TCP協議作為其端對端協議,提供了一個可信賴的字節流是服務;數據報套接字使用UDP協議,提供數據打包發送服務。在Android系統中使用Socket通信的場景包括:
- Zygote:fork新進程,system_server向zygote發送fork新進程請求是使用socket通信。
- Installd:用戶安裝app的守護進程,PMS安裝應用時向installd發送socket通信來完成安裝過程。
- Adbd:用於服務adb操作。
- Logcatd:用於服務logcat操作
- Vold:存儲類守護進程,用於服務USD、sdcard等存儲設備的事件處理。(在Android9.0中已將這一進程通信修改為Binder實現)
其他幾種通信機制
管道
Linux支持的最初Unix IPC機制之一,特點是:
- 管道是半雙工,數據只能向一個方向流動,需要雙方通信時,建議使用兩個管道。
- 只能用於父子進程或者兄弟進程之間。
- 單獨構成一種獨立的文件系統:管道對於管道兩端的進程而言,就是一個文件,但它不是普通文件,它不屬於某種文件系統,而是自立門戶,單獨構成一種文件系統,並且每次都是從緩沖區都不讀出數據。
- 數據讀出和寫入;一個進程向管道中寫的內容被管道另一端的進程讀出。寫入的內容每次都添加在管道緩沖區的末尾,並且每次都是從緩沖區頭部讀出數據。
消息隊列
消息隊列提供了一種從一個進程向另一個進程發送一個數據塊的方法。每個數據塊都被認為含有一個類型,接受進程可以獨立地接受含有不同類型的數據結構。通過發送消息來避免明明管道的同步和阻塞問題。消息隊列發送的消息有最大長度限制。
共享內存
共享內存,顧名思義即允許兩個不相關的進程訪問同一個邏輯內存。共享內存是在兩個正在運行的進程之間共享和傳遞的一種非常有效的方法。不同進程之間共享的內存通常安排為同一段物理內存。進程可以講同一段共享內存鏈接到他們自己的地址空間中。所有進程都可以訪問共享內存中的地址。
需要註意的是:共享內存沒有使用同步機制,即進程A對共享內存進程寫操作時,並沒有對共享內存加鎖,進程B仍然可以讀取共享內存中的數據。所以通常我們需要使用其他的機制來同步共享內存的訪問。
信號量
為了防止出現多個進程同步訪問一個共享資源而引發一系列問題,我們需要一種方法,它可以通常生成令牌來授權,在任何時刻只能有一個指向線程訪問代碼的臨界區域。臨界區域是指執行數據更新的代碼需要獨占式執行。而信號量就可以提供這樣的一種訪問機制,也就是說信號量是用來協調進程對資源共享的訪問。
信號量是一個特殊變量,程序對其訪問都是原子操作,且只允許對它進程等待和發送信息操作。最簡單的信號量是只能取0和1的變量。這也是信號量最常見的形式。這就是二進制信號量。而中可以取多個正整數的信號量稱為通用信號量。
信號
信號是UNIX和Linux系統響應某些條件而產生的一個事件,接收到該信號的進程會響應地采取一些行動。通常信號是由一個錯誤產生的。但他們還可以作為進程間通信或修改行為的一種形式,明確地由一個進程發送給另一個進程。信號產生叫做生成,接收叫做信號捕獲。
Android中幾種IPC機制對比
名稱 | 優點 | 缺點 | 適用場景 |
---|---|---|---|
Bundle | 簡單易用 | 只能傳輸Bundle支持的數據類型 | 組件間進程間通信 |
文件共享 | 簡單易用 | 不適合並發場景,並且無法做到進程間即時通信 | 無並發訪問情形,交換簡單數據實時性不高的場景 |
AIDL | 功能強大,支持一對多並發通信,支持實時通信 | 使用復雜,需要處理好線程同步 | 一對多通信且有RPC需求 |
Message | 功能一般,支持一對多串行通信,支持實時通信 | 不能很好處理高並發場景,不支持RPC,數據通過Message進程傳輸,因此只能傳輸Bundle支持的數據類型 | 低並發的一對多即時通信,無RPC需求,或者無需要返回結果的RPC需求 |
ContentProvider | 在數據訪問方面功能強大,支持一對多並發數據共享,可通過Call方法擴展其他操作 | 可以理解為受約束AIDL,主要提供數據源的CRUD操作 | 對多進程間的數據共享 |
socket | 功能強大,可以使用網絡傳輸字節流,支持一對多並發實時通信 | 實現細節稍微有點繁瑣,不支持直接RPC | 網絡數據交換,或少數進程間交換 |
Android中的常見通信機制和Linux中的通信機制