Android進階(六)Binder機制
程序隔離是為保護作業系統中程序互不干擾而設計的一組不同硬體和軟體的技術。程序資料不共享,程序A的虛擬地址和程序B的虛擬地址不同,這樣就防止程序A將資料資訊寫入程序B,保證了資料的安全性。

- 程序空間分為核心空間和使用者空間,核心空間(Kernel)是系統核心執行的空間。使用者空間(User Space)是使用者程式執行的空間,他們之間是隔離的。
- 核心有訪問的所有許可權,使用者空間也可以通過系統介面去訪問核心空間。使用者空間可以通過核心空間(類似於中介者)進行相互訪問。
2、Linux程序間IPC方式:
- 管道
- 訊息佇列
- 共享記憶體
- 套接字
- 訊號量
- 訊號
Linux 下的傳統 IPC 通訊原理:

傳輸過程:
記憶體快取區 --> 核心快取區 --> 記憶體快取區,需要 2 次資料拷貝;
3、Binder機制的特點

(1)傳輸效能
- Binder資料拷貝只需要一次,而訊息佇列、管道、Socket等需要兩次,共享記憶體一次拷貝都不需要。Binder效能僅次於共享記憶體。
(2)穩定性
- Binder基於C/S架構,Server端和Client端相對獨立,穩定性好。
- 共享記憶體沒有Server端和Client端的區分,可能存在同步死鎖等問題。Binder穩定性優於共享記憶體。
(3)安全性
- 傳統的Linux通訊方式無法獲取對方程序的UID,所以訪問接入點是開放的,不安全。
- Android為每個應用程式分配了自己的UID,作為自身的標識。Binder的方式可以通過UID建立私有通道,Binder的安全性更高。
二、Binder機制實現原理
1、記憶體對映
Binder IPC正是基於記憶體對映(mmap)來實現的

Binder通訊過程:
- 首先 Binder 驅動在核心空間建立一個數據接收快取區;
- 接著在核心空間開闢一塊核心快取區, 建立核心快取區和核心中資料接收快取區之間的對映關係,以及核心中資料接收快取區和接收程序使用者空間地址的對映關係;
- 傳送方程序通過系統呼叫 copy_from_user() 將資料 copy 到核心中的核心快取區 ,由於核心快取區和接收程序的使用者空間存在記憶體對映,因此也就 相當於把資料傳送到了接收程序的使用者空間 ,這樣便完成了一次程序間的通訊。
2、Binder通訊模型

- 包括 Client、Server、ServiceManager、Binder 驅動。
- 其中 Client、Server、Service Manager 執行在使用者空間,Binder 驅動執行在核心空間。
- 對於Client,Binder是Server本地物件的一個引用,這個引用實際上是一個代理物件,Client通過這個代理物件來間接訪問Server的本地物件。
- 對於Server,Binder是提供具體實現的本地物件,需向ServiceManager註冊。
- Binder驅動是連線Client來Server的橋樑,負責將代理物件轉化為本地物件,並將Server的執行結果返回給Client。
- ServiceManager它儲存了Server Binder字元名稱和Binder引用的對映,Client通過它來找到Server的Binder引用。
3、Binder通訊過程:
- 一個程序使用 BINDERSETCONTEXT_MGR 命令通過 Binder 驅動將自己註冊成為 ServiceManager;
- Server 通過驅動向 ServiceManager 中註冊 Binder ,表明可以對外提供服務。驅動為這個 Binder 建立位於核心中的實體節點以及 ServiceManager 對實體的引用,將名字以及新建的引用打包傳給 ServiceManager,ServiceManger 將其填入查詢表。
- Client 通過名字,在 Binder 驅動的幫助下從 ServiceManager 中獲取到對 Binder 實體的引用 ,通過這個引用就能實現和 Server 程序的通訊。
三、AIDL
1、AIDL使用的基本步驟
(1)生成AIDL介面(new->AIDL->AIDL File)
interface MyWindowManager { void sysout(); } 複製程式碼
生成AIDL檔案之後,比起以前多了一個叫做 aidl 的包,而且他的層級是和 java 包相同的。
(2)編譯MyWindowManager.aidl生成Java檔案
public interface MyWindowManager extends android.os.IInterface { /** Local-side IPC implementation stub class. */ //Stub 繼承 Binder, 說明它是一個 Binder 本地物件;實現 IInterface 介面,表明Server可以提供的方法 public static abstract class Stub extends android.os.Binder implements com.example.myview.binder.MyWindowManager { private static final java.lang.String DESCRIPTOR = "com.example.myview.binder.MyWindowManager"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } public static com.example.myview.binder.MyWindowManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.myview.binder.MyWindowManager))) { return ((com.example.myview.binder.MyWindowManager) iin); } return new com.example.myview.binder.MyWindowManager.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { ...... return super.onTransact(code, data, reply, flags); } //Binder本地代理物件 private static class Proxy implements com.example.myview.binder.MyWindowManager { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public void sysout() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_sysout, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_sysout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public void sysout() throws android.os.RemoteException; } 複製程式碼
- MyWindowManager繼承了IInterface,是Client和Server通訊的介面
- Stub為靜態抽象內部類,繼承了Binder。其子類需要實現MyWindowManager介面, 是Server的Binder的本地物件
- Stub.Proxy為靜態內部類,內部包含了IBinder物件, 是Server在Client中的本地代理物件 ,將引數序列化後交給mRemote處理,實現了跟遠端Stub的通訊
- asInterface方法通常是Client在bindService成功後,由Client來呼叫的,作用是將繫結成功後返回的IBinder物件轉換為具體的IInterface介面。Client拿到這個IInterface介面後跟Server進行通訊
(3)Server端提供方法的具體實現
public class MyWindowManagerService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { return mWindowManager; } private final MyWindowManager.Stub mWindowManager = new MyWindowManager.Stub() { @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { } @Override public void sysout() throws RemoteException { Log.e("hj", "sysout: " ); } }; } 複製程式碼
(4)其他程序的Activity實現跟Service的通訊
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(this, MyWindowManagerService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { MyWindowManager windowManager = MyWindowManager.Stub.asInterface(service); try { windowManager.sysout(); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; } 複製程式碼
2、IBinder/IInterface/Binder/BinderProxy/Stub
- IBinder : IBinder 是一個介面,代表了一種跨程序通訊的能力。只要實現了這個藉口,這個物件就能跨程序傳輸。
- IInterface : IInterface 代表的就是 Server 程序物件提供了哪些方法
- Binder : Java 層的 Binder 類,代表的其實就是 Binder 本地物件。 Proxy 類是 Binder 類的一個內部類,它代表遠端程序的 Binder 物件的本地代理 ;這兩個類都繼承自 IBinder, 因而都具有跨程序傳輸的能力;實際上,在跨越程序的時候,Binder 驅動會自動完成這兩個物件的轉換。
- Stub : AIDL 的時候,編譯工具會給我們生成一個名為 Stub 的靜態內部類;這個類繼承了 Binder, 說明它是一個 Binder 本地物件,它實現了 IInterface 介面,表明它具有 Server 承諾給 Client 的能力;Stub 是一個抽象類,具體的 IInterface 的相關實現需要自己實現。