Android Binder 機制
講 Android Binder機制
的文章非常多,這篇文章主要是理一下我對 Binder
的理解。本文不是一篇介紹 Binder
的文章,也不是一篇探討 Binder
實現的文章。
本文會以 AndroidStudio
根據 aidl介面
自動產生的 java檔案
來看 Binder
,進而來理解 Binder機制
。
其實Android的Binder機制類似於: ofollow,noindex">RPC(遠端過程呼叫) 。如果你理解它,相信 Binder
機制就更容易理解了。
首先我們使用 AndroidStudio
來定義一個 aidl
介面:
interface IUserManager { int getUserAge(in String userName); }
然後我們來直接看一個由 AndroidStudio
根據自定義的 aidl
介面 IUserManager
產生的 IUserManager.java
檔案。
這個檔案我們來分3個部分看:
IUserManager介面結構
public interface IUserManager extends android.os.IInterface { public static abstract class Stub extends android.os.Binder implements com.susion.demo.aidl.IUserManager {..} public int getUserAge(java.lang.String userName) throws android.os.RemoteException; }
這個介面的結構還是很簡單的:
- 它繼承自
android.os.IInterface
。 - 定義了一個待實現的方法
int getUserAge()
- 定義了一個
Stub
類。這個類繼承自Binder
,並實現了IUserManager
介面。
int getUserAge()
這個方法就是我們 IUserManager介面
的方法。而 android.os.IInterface
是什麼呢?先看一下它在原始碼中的定義:
/** * Base class for Binder interfaces.When defining a new interface, * you must derive it from IInterface. */ public interface IInterface { /** * Retrieve the Binder object associated with this interface. * You must use this instead of a plain cast, so that proxy objects * can return the correct result. */ public IBinder asBinder(); //IBinder是Binder的抽象介面 }
即他是所有 Binder
都要實現的介面, 為什麼呢?舉一個我們都熟悉的場景 :
比如 ApplicationThread
, ActivityManagerService
(執行在服務端程序)就可以通過它來呼叫我們客戶端的方法。我們會把這些方法抽象為一個介面( IApplicationThread
),這個介面可以理解為我們告訴服務端,你可以對客戶端執行哪些操作。
我們還知道 ApplicationThread
其實他就是一個 Binder
。所以這兩者一結合就可以這麼說 ApplicationThread
: 客戶端提供給服務端一個 Binder
,通過這個 Binder
服務端可以對客戶端做一些操作,這些操作具體定義在 IApplicationThread
介面中。
我們稱 IApplicationThread
為 ApplicationThread
這個 Binder
的功能。 所以 Binder
除了可以理解為系統給我們提供的一個跨程序通訊的物件。 我們在用 Binder
通訊時,還可以說 Binder
是一個具有某些功能的一個物件。
那麼怎麼表示 Binder
有功能呢? 即要繼承 IInterface
。 IInterface
可以表示 Binder
有功能, 不然你想一個,那麼多 Binder
都只實現自己的介面, 那麼系統層就不好操作了,它總不能向下強轉為 Binder
吧,所以Android定義了一個更高層級的介面 IInterface
。描述 Binder
功能的介面必須繼承自這個介面。
重點: Binder、Binder的功能(IApplicationThread)、IInterface它們都在同一個物件上 -> ApplicationThread
Stub
它是 IUserManager
的內部靜態類,看一下它的具體宣告:
static abstract class Stub extends android.os.Binder implements com.susion.demo.aidl.IUserManager
即它是一個 Binder
,可以用來跨程序通訊。它具有 IUserManager
定義的功能。
看一下它的具體結構:
public static abstract class Stub extends android.os.Binder implements com.susion.demo.aidl.IUserManager { private static final java.lang.String DESCRIPTOR = "com.susion.demo.aidl.IUserManager"; static final int TRANSACTION_userCount = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); public Stub() { this.attachInterface(this, DESCRIPTOR); } public static com.susion.demo.aidl.IUserManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.susion.demo.aidl.IUserManager))) { return ((com.susion.demo.aidl.IUserManager) iin); } return new com.susion.demo.aidl.IUserManager.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() {retun this;} @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {...} private static class Proxy implements com.susion.demo.aidl.IUserManager {...} }
我們還是一個一個的看一下:
DESCRIPTOR
基於我們前面的解釋,我們知道在跨程序通訊中 Binder
物件具有某種功能-> IInterface
。但是 Binder
通訊機制中那麼多 Binder
都有 IInterface
。那麼系統怎麼識別哪個 Binder
是哪個 Binder
呢?所以 IInterface
只是一個能力的抽象, DESCRIPTOR
就是來表示具體是哪一個功能 IInterface
。
TRANSACTION_userCount
即功能下的哪個操作。
Stub建構函式
public Stub() { this.attachInterface(this, DESCRIPTOR); }
即一個 Stub
( Binder
)在構造的時候,就標識好了自己的具體功能 IInterface(IUserManager)
。來看一下 attachInterface(this, DESCRIPTOR)
做了什麼:
//Binder.java public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) { mOwner = owner; mDescriptor = descriptor; }
即,Binder在內部會用 IInterface
來儲存自己的功能。和這個功能更對應的唯一描述 descriptor
,方便在通訊的時候尋找。
asBinder()
自己返回自己,因為自己本身就是個 Binder
呀。
onTransact()
當其他程序想跨程序呼叫我這個 Binder
的功能時,必須通過這個方法來溝通。這個方法我們最後再來看。
asInterface(android.os.IBinder obj)
即接收一個 IBinder
(這個IBinder是系統傳入的), 把這個 IBinder
轉化為它所具有功能介面。 其實這裡就是 Binder
跨程序通訊的一個核心 。那怎麼轉化的呢?
- 呼叫者和Binder物件位於同一個程序
那麼系統就會直接傳給你在這個程序建立的 Stub
(Binder)。所以 obj.queryLocalInterface(DESCRIPTOR)
:
publicIInterface queryLocalInterface(String descriptor) { if (mDescriptor.equals(descriptor)) { return mOwner; } return null; }
即如果 引數descriptor
和這個 Binder的功能唯一描述相同
。就會返回 Binder
的功能 mOwner
。
- 呼叫者和Binder物件不在同一個程序
這時系統實際傳的是一個 BinderProxy
, 你可以理解為它是另一個程序中的 Binder
的替身。我們就可以把它當成另一個程序的 Binder
。我們看一下 BinderProxy
的 queryLocalInterface()
方法:
/** * Retrieve a local interface - always null in case of a proxy */ public IInterface queryLocalInterface(String descriptor) { return null; }
所以此時 asInterface()
返回的是: IUserManager.Stub.Proxy(obj)
, 即代理物件,它代理了 BinderProxy
。
IUserManager.Stub.Proxy
它是 Stub
的靜態內部類,如果呼叫者和 Binder
不在同一個程序的話,呼叫者拿到的實際是它:
private static class Proxy implements com.didi.virtualapk.demo.aidl.IUserManager { 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 int getUserAge(java.lang.String userName) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); int _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(userName); mRemote.transact(Stub.TRANSACTION_getUserAge, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; } }
我們前面說了它其實是 BinderProxy
的代理。為什麼要對 BinderProxy
加這個代理呢?看一下 getUserAge()
:
public int getUserAge(java.lang.String userName) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); int _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(userName); mRemote.transact(Stub.TRANSACTION_getUserAge, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; }
即是呼叫 mRemote.transact()
(BinderProxy的)方法。 Stub.TRANSACTION_getUserAge
是要呼叫的遠端 Binder
方法的 getUserAge()
對應的描述符。
_data
是序列化後的入參、 _reply
是序列化後的返回值。可以看到 _data
所攜帶的引數是需要序列化的, _reply
所帶的內容是被序列化的,所以讀取要反序列化。
所以 IUserManager.Stub.Proxy
類的作用就是在跨程序呼叫時對傳給 mRemote(BinderProxy)
的引數做序列化,對 mRemote(BinderProxy)
返回值做反序列化。 引數的接受者和返回者是 BinderProxy
具體呼叫 Binder
的能力是使用 BinderProxy
的 transact()
方法, 它是跨程序通訊的核心 , 我們來看一下這個方法:
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { ... return transactNative(code, data, reply, flags); // native 方法 }
省略了不重要的程式碼。即 BinderProxy
是通過 transactNative
來與遠端 Binder
跨程序通訊的。具體怎麼實現,這裡就不追究了。
Stub.onTransact()
我們前面沒有看這個方法,這裡我們來看一下:
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getUserAge: { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); int _result = this.getUserAge(_arg0); reply.writeNoException(); reply.writeInt(_result); return true; } } return super.onTransact(code, data, reply, flags); }
根據 IUserManager.Stub.Proxy
我們知道,如果不在同一個程序,那麼引數是被序列化後傳過來的, 所以這個方法是用來對入參做反序列化,並對返回值做序列化的 。
最後我們用一張圖來總結 Binder程序通訊機制
:

Binder機制.png
參考: