瞻仰了前輩們的研究成果, 并摻入了自己的理解, 如有不對, 敬請批評.
為什么Android要使用Binder
Binder 作為一種 IPC 機制, 在 Linux 內有很多的前輩, 為什么 google 會創建這么一種新的方式呢?
Linux 現有 IPC 方式有這幾類:
- 管道:在創建時分配一個page大小的內存,緩存區大小比較有限;
- 消息隊列:信息復制兩次,額外的CPU消耗;不合適頻繁或信息量大的通信;
- 共享內存:無須復制,共享緩沖區直接付附加到進程虛擬地址空間,速度快;但進程間的同步問題操作系統無法實現,必須各進程利用同步工具解決;
- 套接字:作為更通用的接口,傳輸效率低,主要用于不通機器或跨網絡的通信;
- 信號量:常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作為進程間以及同一進程內不同線程之間的同步手段。
- 信號: 不適用于信息交換,更適用于進程中斷控制,比如非法內存訪問,殺死某個進程等;
與以上方式相比較, Binder 有以下特點:
- 性能較好: 數據拷貝 Binder 只需要一次, 管道/消息隊列/socket 都需要兩次, Binder 僅次于共享內存(一次都不需要);
- 穩定性較好: 基于C/S架構的Binder在邏輯上更加解耦, 架構清晰, 而共享內存容易出現各種并發同步死鎖問題;
- 安全性好: Android每個App都有自己的UID, 傳統的 IPC 機制的接收方無法拿到發送方可靠的UID, 而 Binder 的 Server 端可以通過 Android 給每個 App 暴露出的 Client 端獲取到, 從而對不同UID的App進行權限判斷等安全性控制;
- 語言層面: Linux基于C, Android基于java, Binder機制更符合面向對象的環境;
- 協議: Linux 受開源代碼許可協議GPL的保護, 如果上層應用調用到 Linux Kernel, 就必須也遵循GPL協議; Android利用Binder隔離了Linux Kernel層, 把GPL控制在內核空間, 而用戶空間采用了允許不反饋源碼的Apache-2.0協議, 并在中間采用BSD授權, 有利于Google實現開源和商業化的共存
綜上所述, Binder是Android系統中IPC的最好方式.
Binder 概述##
Binder是android內獨有的跨進程通信方式, 它在native層有一套完整的C/S架構, framework層也通過jni技術實現了一套鏡像功能的Binder C/S架構, framework層的binder功能最終都交給native的binder來完成. 引用一張Gityuan大神的Binder架構圖:
- 對于Android Driver層: Binder可以理解為一種虛擬的物理設備, 它的設備驅動是/dev/binder, 連接了Video,Camera等設備;
- 對于Android Native層: Binder是創建Service Manager/BpBinder/BBinder模型、搭建與binder驅動的橋梁;
- 對于Android Framework層: Binder是各種Manager(ActivityManager, WindowManager等)和對于ManagerService的橋梁;
- 對于Android App層: Binder是客戶端和服務端通信的橋梁, 服務端包括統一進程下的和需要使用AIDL的不同進程下的服務. bindService的時候, 服務端會返回一個包含服務端方法及數據的Binder對象, 通過這個Binder對象, 客戶端就能與之通信了.
Binder如何工作
Binder的數據傳輸 就是發送端把binder_transaction節點,插入到目標進程或其子線程的todo隊列中,等目標進程或線程不斷循環地從todo隊列中取出數據并進行相應的操作.
在Binder驅動層,每個接收端進程都有一個todo隊列,用于保存發送端進程發送過來的binder請求. 線程在空閑時進入可中斷的休眠狀態,當自己的todo隊列或所屬進程的todo隊列有新的請求到來時便會喚醒,執行新的請求事務, 執行完畢后再次休眠.
再引入一張圖, 并添加了注釋
App層的兩種服務里的binder使用
App層的Service有同進程下的Service和不同進程的Service兩種, 新開進程的Service需要在AndroidManifest.xml里的Service聲明中添加 android:process=":remote"
, 也可以添加服務名稱.
同進程下的Service
Client 端
public class FragSocketPresenter extends BasePresenter<IFragSocketView> {
private PitPatService pitPatService;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
pitPatService = ((PitPatService.ServiceBinder) service).getService();
pitPatService.setSocketStateListener(socketStateListener);
}
@Override
public void onServiceDisconnected(ComponentName name) { // 此方法只有在service異常時才調用
pitPatService = null;
}
};
public void socketConnect() {
if (getView() != null) {
Intent intent = new Intent(getView().getContext(), PitPatService.class);
intent.putExtra("IP", getView().getIp());
// 會觸發onCreate()和onBind(),不觸發onStartCommand; 多次點擊不會多次觸發
getView().getContext().bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
}
}
服務端
public class MyService extends Service {
private String ip = "";
@Override
public IBinder onBind(Intent intent) {
ip = intent.getStringExtra("IP");
return new ServiceBinder();
}
public class ServiceBinder extends Binder {
public PitPatService getService() {
return PitPatService.this;
}
}
}
基于AIDL的跨進程服務
篇幅有限, 寫到另一篇里了 AndroidStudio下使用 AIDL 構建跨進程 Service( 詳細代碼貼圖 ), 填補網上的大多數坑
參考: 源碼大神 Gityuan 的各類文章
http://gityuan.com/2015/10/31/binder-prepare/
https://www.zhihu.com/question/39440766/answer/89210950?from=profile_answer_card