在上一篇文章——藉助 AIDL 理解 Android Binder 機制——Binder 來龍去脈中我們已經分析了使用 Binder 機制的原因以及分析了 Binder 機制,本章我們將繼續從 AIDL 的使用過程體驗 Binder 在應用層的使用和原理。
AIDL 使用步驟
1.建立 UserManager.aidl 介面檔案,宣告作為 Server 端的遠端 Service 具有哪些能力
UserManager.aidl:
package com.me.guanpj.binder;
import com.me.guanpj.binder.User;
// Declare any non-default types here with import statements
interface UserManager {
void addUser(in User user);
List<User> getUserList();
}
複製程式碼
對於物件引用,還需要引入實體類
User.aidl:
// User.aidl
package com.me.guanpj.binder;
// Declare any non-default types here with import statements
parcelable User;
複製程式碼
跨程式傳輸物件必須實現 Parcelable 介面
User.java
public class User implements Parcelable {
public int id;
public String name;
public User() {}
public User(int id, String name) {
this.id = id;
this.name = name;
}
protected User(Parcel in) {
id = in.readInt();
name = in.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
}
複製程式碼
生成的 UserManager 類如下:
UserManager.java:
package com.me.guanpj.binder;
// Declare any non-default types here with import statements
public interface UserManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.me.guanpj.binder.UserManager
{
private static final java.lang.String DESCRIPTOR = "com.me.guanpj.binder.UserManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.me.guanpj.binder.UserManager interface,
* generating a proxy if needed.
*/
public static com.me.guanpj.binder.UserManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.me.guanpj.binder.UserManager))) {
return ((com.me.guanpj.binder.UserManager)iin);
}
return new com.me.guanpj.binder.UserManager.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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_addUser:
{
data.enforceInterface(descriptor);
com.me.guanpj.binder.User _arg0;
if ((0!=data.readInt())) {
_arg0 = com.me.guanpj.binder.User.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addUser(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getUserList:
{
data.enforceInterface(descriptor);
java.util.List<com.me.guanpj.binder.User> _result = this.getUserList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.me.guanpj.binder.UserManager
{
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 addUser(com.me.guanpj.binder.User user) 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 ((user!=null)) {
_data.writeInt(1);
user.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.util.List<com.me.guanpj.binder.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.me.guanpj.binder.User> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getUserList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.me.guanpj.binder.User.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getUserList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public void addUser(com.me.guanpj.binder.User user) throws android.os.RemoteException;
public java.util.List<com.me.guanpj.binder.User> getUserList() throws android.os.RemoteException;
}
複製程式碼
3.建立 Service,實現 UserManager.Stub 類並將該實現類的例項在 onBind 方法返回
MyService.java:
public class MyService extends Service {
class UserManagerNative extends UserManager.Stub {
List<User> users = new ArrayList<>();
@Override
public void addUser(User user) {
Log.e("gpj", "程式:" + Utils.getProcessName(getApplicationContext())
+ ",執行緒:" + Thread.currentThread().getName() + "————" + "Server 執行 addUser");
users.add(user);
}
@Override
public List<User> getUserList() {
Log.e("gpj", "程式:" + Utils.getProcessName(getApplicationContext())
+ ",執行緒:" + Thread.currentThread().getName() + "————" + "Server 執行 getUserList");
return users;
}
}
private UserManagerNative mUserManagerNative = new UserManagerNative();
@Override
public IBinder onBind(Intent intent) {
Log.e("gpj", "程式:" + Utils.getProcessName(getApplicationContext())
+ ",執行緒:" + Thread.currentThread().getName() + "————" + "Server onBind");
return mUserManagerNative;
}
}
複製程式碼
4.在作為 Client 端的 Activity 中,繫結遠端 Service 並得到 Server 的代理物件
5.通過 Server 代理物件,呼叫 Server 的具體方法
MainActivity.java:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button btnBind;
Button btnAddUser;
Button btnGetSize;
TextView tvResult;
IUserManager mUserManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnBind = (Button) findViewById(R.id.btn_bind);
btnAddUser = (Button) findViewById(R.id.btn_add_user);
btnGetSize = (Button) findViewById(R.id.btn_get_size);
btnBind.setOnClickListener(this);
btnAddUser.setOnClickListener(this);
btnGetSize.setOnClickListener(this);
tvResult = (TextView) findViewById(R.id.txt_result);
}
@Override
protected void onDestroy() {
unbindService(mConn);
super.onDestroy();
}
private ServiceConnection mConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("gpj", "程式:" + Utils.getProcessName(getApplicationContext())
+ ",執行緒:" + Thread.currentThread().getName() + "————" + "Client onServiceConnected");
mUserManager = UserManagerImpl.asInterface(service);
try {
//註冊遠端服務死亡通知
service.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mUserManager = null;
}
};
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mUserManager != null) {
mUserManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mUserManager = null;
//重新繫結服務
bindService();
}
}
};
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_bind:
bindService();
break;
case R.id.btn_add_user:
if (null != mUserManager) {
try {
Log.e("gpj", "執行緒:" + Thread.currentThread().getName() + "————" +"Client 呼叫 addUser");
mUserManager.addUser(new User(111, "gpj"));
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
Toast.makeText(MainActivity.this, "先繫結 Service 才能呼叫方法", Toast.LENGTH_LONG).show();
}
break;
case R.id.btn_get_size:
if (null != mUserManager) {
try {
Log.e("gpj", "執行緒:" + Thread.currentThread().getName() + "————" +"Client 呼叫 getUserList");
List<User> userList = mUserManager.getUserList();
tvResult.setText("getUserList size:" + userList.size());
Log.e("gpj", "執行緒:" + Thread.currentThread().getName() + "————" +"呼叫結果:" + userList.size());
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
Toast.makeText(MainActivity.this, "先繫結 Service 才能呼叫方法", Toast.LENGTH_LONG).show();
}
break;
default:
}
}
private void bindService() {
Intent intent = new Intent();
intent.setAction("com.me.guanpj.binder");
intent.setComponent(new ComponentName("com.me.guanpj.binder", "com.me.guanpj.binder.MyService"));
Log.e("gpj", "程式:" + Utils.getProcessName(getApplicationContext())
+ ",執行緒:" + Thread.currentThread().getName() + "————" + "開始繫結服務");
bindService(intent, mConn, Context.BIND_AUTO_CREATE);
}
}
複製程式碼
AIDL 的實現過程
為了便於理解,這裡用一個 Demo 來展示 AIDL 的實現過程:Activity 作為 Client 與作為 Server 端的遠端 Service 實現資料互動,在繫結遠端 Service 之後,點選 AddUser 後 Service 會將 Client 端傳進來的 User 物件加入列表中,點選 GetSize 後遠端 Service 將會把列表的長度返回給客戶端。建議在繼續閱讀之前先檢視或者執行一下專案原始碼:
在專案中建立 UserManager.aidl 檔案之後,系統會自動在 build 目錄生成一個與 UserManager.java 介面類,它繼承了 IInterface 介面,UserManager 介面只有一個靜態抽象類 Stub,Stub 繼承自 Binder 並實現了 UserManager 介面,Stub 裡面也有一個靜態內部類 Proxy,Proxy 也繼承了 UserManager(是不是有點亂,亂就對了,我也很亂)。
如此巢狀是為了避免有多個 .aidl 檔案的時候自動生成這些類的類名不會重複,為了提高程式碼可讀性,我們將生成的 UserManager 和 Stub 類 拆解並重新命名成了 IUserManager 類和 UserManagerImpl 類並在關鍵方法上新增了註釋或者 Log。
IUserManager.java:
public interface IUserManager extends android.os.IInterface {
//唯一性標識
static final java.lang.String DESCRIPTOR = "com.me.guanpj.binder.IUserManager";
//方法標識,用十六進位制表示
int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
int TRANSACTION_getUserList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
//Server 具有的能力
void addUser(User user) throws android.os.RemoteException;
List<User> getUserList() throws android.os.RemoteException;
}
複製程式碼
UserManagerImpl.java:
public abstract class UserManagerImpl extends Binder implements IUserManager {
/**
* Construct the mLocalStub at attach it to the interface.
*/
public UserManagerImpl() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* 根據 Binder 本地物件或者代理物件返回 IUserManager 介面
*/
public static IUserManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//查詢本地物件
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IUserManager))) {
Log.e("gpj", "執行緒:" + Thread.currentThread().getName() + "————" + "返回本地物件");
return ((IUserManager) iin);
}
Log.e("gpj", "執行緒:" + Thread.currentThread().getName() + "————" + "返回代理物件");
return new UserManagerImpl.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 {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_addUser: {
Log.e("gpj", "執行緒:" + Thread.currentThread().getName() + "————" + "本地物件通過 Binder 執行 addUser");
data.enforceInterface(DESCRIPTOR);
User arg0;
if ((0 != data.readInt())) {
//取出客戶端傳遞過來的資料
arg0 = User.CREATOR.createFromParcel(data);
} else {
arg0 = null;
}
//呼叫 Binder 本地物件
this.addUser(arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getUserList: {
Log.e("gpj", "執行緒:" + Thread.currentThread().getName() + "————" + "本地物件通過 Binder 執行 getUserList");
data.enforceInterface(DESCRIPTOR);
//呼叫 Binder 本地物件
List<User> result = this.getUserList();
reply.writeNoException();
//將結果返回給客戶端
reply.writeTypedList(result);
return true;
}
default:
break;
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements 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 void addUser(User user) 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 (user != null) {
_data.writeInt(1);
user.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
Log.e("gpj", "執行緒:" + Thread.currentThread().getName() + "————" + "代理物件通過 Binder 呼叫 addUser");
mRemote.transact(UserManagerImpl.TRANSACTION_addUser, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public List<User> getUserList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
List<User> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
Log.e("gpj", "執行緒:" + Thread.currentThread().getName() + "————" + "代理物件通過 Binder 呼叫 getUserList");
mRemote.transact(UserManagerImpl.TRANSACTION_getUserList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(User.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
複製程式碼
再進行分析之前,先了解幾個概念:
- IInterface : 從註釋中的說明看出,宣告(自動生成或者手動建立)AIDL 性質的介面必須繼承這個介面,這個介面只有一個 IBinder asBinder() 方法,實現它的類代表它能夠程式跨程式傳輸( Binder 本地物件)或者持有能夠程式跨程式傳輸的物件的引用(Binder 代理物件)。
- IUserManager : 它同樣是一個介面,它繼承了 IInterface 類,並宣告瞭 Server 承諾給 Client 的能力
- IBinder : 它也是一個介面,實現這個介面的物件就具有了跨程式傳輸的能力,在跨程式資料流經驅動的時候,驅動會識別IBinder型別的資料,從而自動完成不同程式Binder本地物件以及Binder代理物件的轉換。
- Binder : 代表 Binder 本地物件,BinderProxy 類是它的內部類,是 Server 端 Binder 物件的本地代理,它們都繼承了 IBinder 介面,因此都能跨程式進行傳輸,Binder 驅動在跨程式傳輸的時候會將這兩個物件自動進行轉換。
- UserManagerImpl : 它繼承了 Binder 並實現了 IInterface 介面,說明它是 Server 端的 Binder 本地物件,並擁有 Server 承諾給 Client 的能力。
先從 MainActivity 中繫結服務後的回撥方法著手:
private ServiceConnection mConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mUserManager = UserManagerImpl.asInterface(service);
try {
//註冊遠端服務死亡通知
service.linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mUserManager = null;
}
};
複製程式碼
onServiceConnected 的引數中,第一個是 Service 元件的名字,表示哪個服務被啟動了,重點是型別為 IBinder 的第二個引數,在 Service.java 中的 onBind 方法中,已經把 Server 端的本地物件 UserManagerNative 例項返回給 Binder 驅動了:
private UserManagerNative mUserManagerNative = new UserManagerNative();
@Override
public IBinder onBind(Intent intent) {
return mUserManagerNative;
}
複製程式碼
因此,當該服務被繫結的時候,Binder 驅動會為根據該服務所在的程式決定 是返回本地物件還是代理物件給客戶端,當 Service 與 MainActivity 位於同一個程式當中的時候,onServiceConnected 返回 Binder 本地物件——即 UserManagerNative 物件給客戶端;當 Service 執行在不同程式中的時候,返回的是 BinderProxy 物件。
接著,在將這個 IBinder 物件傳給 UserManagerImpl 的 asInterface 方法並返回 IUserManager 介面,asInterface 方法實現如下:
/**
* 根據 Binder 本地物件或者代理物件返回 IUserManager 介面
*/
public static IUserManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//查詢本地物件
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IUserManager))) {
return ((IUserManager) iin);
}
return new UserManagerImpl.Proxy(obj);
}
複製程式碼
首先,會根據 DESCRIPTOR 呼叫 IBinder 物件的 queryLocalInterface 方法,那麼就得看 IBinder 的實現類怎麼處理這個方法了:
在 Binder 類中的實現:
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
//判斷 mDescriptor 跟引數 DESCRIPTOR 相同,返回 mOwner
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
複製程式碼
那麼這個 mOwner 和 mDescriptor 又是什麼時候被賦值的呢?答案在 Binder 的子類 UserManagerImpl 的構造方法裡面,:
public UserManagerImpl() {
//將 UserManagerImpl 和 DESCRIPTOR 注入到父類(Binder)
this.attachInterface(this, DESCRIPTOR);
}
複製程式碼
在 Binder$BinderProxy 類中的實現:
BinderProxy 並不是 Binder 本地物件,而是 Binder 的本地代理,因此 queryLocalInterface 返回的是 null:
public IInterface queryLocalInterface(String descriptor) {
return null;
}
複製程式碼
綜上兩點可以看出,如果 obj.queryLocalInterface(DESCRIPTOR) 方法存在返回值並且是 IUserManager 型別的物件,那麼它就是 Binder 本地物件,將它直接返回給 Client 呼叫;否則,使用 UserManagerImpl$Proxy 類將其進行包裝後再返回,Proxy 類也實現了 IUserManager 介面,因此,在 Client 眼中,它也具有 Server 承諾給 Client 的能力,那麼,經過包裝後的物件怎麼和 Server 進行互動呢?
首先,它會把 BinderProxy 物件儲存下來:
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
複製程式碼
然後,實現 IUserManager 的方法:
@Override
public void addUser(User user) 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 (user != null) {
_data.writeInt(1);
//將 user 物件的值寫入 _data
user.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
//通過 transact 跟 Server 互動
mRemote.transact(UserManagerImpl.TRANSACTION_addUser, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public List<User> getUserList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
List<User> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//通過 transact 跟 Server 互動
mRemote.transact(UserManagerImpl.TRANSACTION_getUserList, _data, _reply, 0);
_reply.readException();
//獲取 Server 的返回值並程式轉換
_result = _reply.createTypedArrayList(User.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
複製程式碼
可以看到,不管什麼方法,都是是將服務端的方法代號、處理過的引數和接收返回值的物件等通過 mRemote.transact 方法 Server 進行互動,mRemote 是 BinderProxy 型別,在 BinderProxy 類中,最終呼叫的是 transactNative 方法:
public native boolean transactNative(int code, Parcel data, Parcel reply, int flags) throws RemoteException;
複製程式碼
它的最終實現在 Native 層進行,Binder 驅動會通過 ioctl 系統呼叫喚醒 Server 程式,並呼叫 Server 本地物件的 onTransact 函式:
@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_addUser: {
data.enforceInterface(DESCRIPTOR);
User arg0;
if ((0 != data.readInt())) {
//取出客戶端傳遞過來的資料
arg0 = User.CREATOR.createFromParcel(data);
} else {
arg0 = null;
}
//呼叫 Binder 本地物件
this.addUser(arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getUserList: {
data.enforceInterface(DESCRIPTOR);
//呼叫 Binder 本地物件
List<User> result = this.getUserList();
reply.writeNoException();
//將結果返回給客戶端
reply.writeTypedList(result);
return true;
}
default:
break;
}
return super.onTransact(code, data, reply, flags);
}
複製程式碼
在 Server 程式中,onTransact 會根據 Client 傳過來的方法代號決定呼叫哪個方法,得到結果後又會通過 Binder 驅動返回給 Client。
總結
回溯到 onServiceConnected 回撥方法,待服務連線成功後,Client 就需要跟 Server 進行互動了,如果 Server 跟 Client 在同一個程式中,Client 可以直接呼叫 Server 的本地物件 ,當它們不在同一個程式中的時候,Binder 驅動會自動將 Server 的本地物件轉換成 BinderProxy 代理物件,經過一層包裝之後,返回一個新的代理物件給 Client。這樣,整個 IPC 的過程就完成了。
參考文章
文章中的程式碼已經上傳至我的 Github,如果你對文章內容有疑問或者有不同的意見,歡迎留言,我們一同探討。