Binder是Android中的一個類,她實現了IBinder接口。從IPC角度來說,Binder是客戶端和服務端進行通訊的媒介!(PS:在Android系統有兩個不同的進程,請求數據的進程為客戶端,接收請求的進程為服務端)
Service與Binder之間的關系
Android開發中,Binder主要用在Service:
- 同一個進程bindService
- Messenger發送Message
- 通過AIDL跨進程bindService
第一種bindService在ServiceConnection中的onServiceConnected(ComponentName name, IBinder service)的service是綁定的Service自身,這就可以直接調用Service中的方法,沒有涉及Binder核心功能。第二種Messenger發送Message底層其實是封裝AIDL,所以我們用AIDL來分析Binder的工作原理。
AIDL進行IPC
- 創建AIDL文件
- User.aidl
由于User類無法在AIDL中直接使用,所以要創建User.aidl對User.java進行描述package com.hk.b.aidl; parcelable User;
- IUserManager.aidl
AIDL不同于Java,即使兩個aidl文件處于同一個包下,也要對引用進行importpackage com.hk.b.aidl; import com.hk.b.aidl.User; interface IUserManager { List<User> getUserList(); void addUser(in User u); }
2.使用.aidl文件生成的.java文件
定義好aidl文件后,系統會自動在Android Studio的ProjectName/ModuleName/build/generated/source/com.hk.b.aidl/IUserManager.java,可以直接在Service中使用這個類,這個類的內容是Binder原理的關鍵,稍后解析。
public class UserService extends Service {
private CopyOnWriteArrayList<User> mUserList = new CopyOnWriteArrayList<>();
public UserService() {
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private IBinder mBinder = new IUserManager.Stub() {
@Override
public List<User> getUserList() throws RemoteException {
Log.d("hvcker","server getUsers");
return mUserList;
}
@Override
public void addUser(User u) throws RemoteException {
Log.d("hvcker","server add");
mUserList.add(u);
}
};
}
另外這個Service在AndroidManifest.xml文件中要配置使該Service運行在新的進程中,這樣才能達到IPC
<service android:name=".s.UserService" android:process=":remote"/>
3.客戶端綁定服務端
-
定義一個IUserManager變量
private IUserManager mUserManager;
-
定義一個ServiceConnection
private ServiceConnection mConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mUserManager = IUserManager.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { mUserManager = null; } };
-
綁定Service
bindService(new Intent(this, UserService.class), mConn, BIND_AUTO_CREATE);
這個時候就可以用mUserManager
來執行addUser
和getUserList
,那么問題來了,mUserManager
是怎么把客戶端的數據添加到服務端,又是怎么獲取服務端的數據呢?
Binder工作原理
首先,我們來看一下根據IUserManager.aidl
文件生成的Java類中有什么乾坤
1.先來大概看一下IUserManager.java
public interface IUserManager extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.hk.b.aidl.IUserManager {
//...
}
public java.util.List<com.hk.b.aidl.User> getUserList() throws android.os.RemoteException;
public void addUser(com.hk.b.aidl.User u) throws android.os.RemoteException;
}
-
IUserManager
繼承IInterface
這個接口,要想在Binder中用,這是必須的。 - 內部類Stub繼承Binder,同時它又實現了IUserManager。Stub他是一個抽象類,是因為它沒有實現IUserManager的未實現方法,這兩個方法要在使用該Stub的時候實現。
2.再來看一下IUserManager.Stub
public static abstract class Stub extends android.os.Binder implements com.hk.b.aidl.IUserManager {
private static final java.lang.String DESCRIPTOR = "com.hk.b.aidl.IUserManager";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static com.hk.b.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.hk.b.aidl.IUserManager))) {
return ((com.hk.b.aidl.IUserManager) iin);
}
return new com.hk.b.aidl.IUserManager.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data,
//...
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.hk.b.aidl.IUserManager {
//...
}
//...
}
- DESCRIPTOR:Binder的唯一標識,一般用當前aidl生成類的類名表示,例如:
com.hk.b.aidl.IUserManager
- Stub():構造函數,把該接口添加到本地接口中
- asBinder():返回當前Binder對象
- asInterface(android.os.IBinder obj):
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
客戶端查詢本地接口,如果本地接口有,說明客戶端和服務端是屬于同一個進程,則返回該進程里的IInterface,如果沒有說明客戶端和服務端不屬于同一個進程,則new com.hk.b.aidl.IUserManager.Stub.Proxy(obj)
,返回Binder代理類Sub.Proxy,Binder跨進程的關鍵來了
3.IUserManager.Stub.Proxy和它用到的一些Stub成員或方法
public static abstract class Stub extends android.os.Binder implements com.hk.b.aidl.IUserManager {
//...
@Override
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_getUserList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.hk.b.aidl.User> _result = this.getUserList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addUser: {
data.enforceInterface(DESCRIPTOR);
com.hk.b.aidl.User _arg0;
if ((0 != data.readInt())) {
_arg0 = com.hk.b.aidl.User.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addUser(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.hk.b.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 java.util.List<com.hk.b.aidl.User> getUserList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.hk.b.aidl.User> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getUserList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.hk.b.aidl.User.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addUser(com.hk.b.aidl.User u) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((u != null)) {
_data.writeInt(1);
u.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getUserList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
TRANSACTION_getUserList
和TRANSACTION_addUser
接口方法id,如果有更多方法,依次遞增-
Proxy
實現了IUserManager
,并實現了它的兩個方法getUserList()
,addUser(com.hk.b.aidl.User u)
,這兩個方法是在客戶端執行的,做了什么事呢?- 創建android.os.Parcel類型的_data和_replay,_data用來存放方法參數,_replay用來存放RPC的返回值。
-
_data.writeInterfaceToken(DESCRIPTOR)
寫入唯一標識,如果方法有參數,寫入1,然后把參數信息寫入_data
-
mRemote.transact(Stub.TRANSACTION_xxx, _data, _reply, 0)
Binder發起RPC,傳入方法id,參數,返回值和標志位,0表示普通RPC -
_result = _reply.createTypedArrayList(com.hk.b.aidl.User.CREATOR)
如果方法有返回值的話,_reply調用.createXxx()方法,Xxx表示返回值類型,比如TypedArrayList返回的類型是帶泛型的ArrayList - 最后回收_data和_reply避免資源的浪費
-
一旦客戶端執行transact,客戶端發起RPC的線程就會掛起,等待服務端的響應,這個時候就會調用服務端Stub的onTransact方法,這個方法又干了什么呢?
- 匹配方法id,不用多說
- 從服務端Binder線程池中用唯一標示符匹配客戶端傳過來android.os.Parcel類型的data和reply,如果
data.readInt()
不為0,取出參數 - 調用與客戶端請求對應的服務端實現IUserManager的方法
- 如果方法有返回值,則寫入reply
- 返回true,如果返回false,則服務端拒絕,可以用這個來做權限判斷。
總結
這樣我們就很清晰的知道了Binder的工作原理,我們完全可以不用aidl文件自己寫一個Binder,但是存在即是合理,還是要用aidl文件來節約開發成本,最后來畫一下Binder的流程圖吧
Client->Server: bindService
Server-->Client: 返回一個Binder
Client->Server: Binder請求數據,寫入參數,調用transact,掛起
Note right of Server:onTransact從Binder線程池中得到與客戶端對應的Binder,處理請求
Server-->Client: 寫入結果,返回給客戶端Binder,并喚醒客戶端