相關概念
序列化
Serialization(序列化)是一種將對象以一連串的字節描述的過程;反序列化deserialization是一種將這些字節重建成一個對象的過程。從用途角度來說,如果對象需要持久化或者在不同進程間傳輸,就需要序列化。
Java原生提供了Serializable接口支持序列化,Serializable比較強大,持久化和數據傳輸都可以支持,但是在序列化開銷比較大,故Android為Binder通信等只在內存中傳輸的場景設計了另一套序列化機制--Parcelable。
Parcelable的性能比Serializable好,在內存開銷方面較小,所以在內存間數據傳輸時推薦使用Parcelable,如activity間傳輸數據,而Serializable可將數據持久化方便保存,所以在需要保存或網絡傳輸數據時選擇Serializable,因為android不同版本Parcelable可能不同,所以不推薦使用Parcelable進行數據持久化。
一個簡單的示例:
1 public class MyParcelable implements Parcelable {
2 private int mData;
3 private String mStr;
4
5 public int describeContents() {
6 return 0;
7 }
8
9 // 寫數據進行保存
10 public void writeToParcel(Parcel out, int flags) {
11 out.writeInt(mData);
12 out.writeString(mStr);
13 }
14
15 // 用來創建自定義的Parcelable的對象
16 public static final Parcelable.Creator<MyParcelable> CREATOR
17 = new Parcelable.Creator<MyParcelable>() {
18 public MyParcelable createFromParcel(Parcel in) {
19 return new MyParcelable(in);
20 }
21
22 public MyParcelable[] newArray(int size) {
23 return new MyParcelable[size];
24 }
25 };
26
27 // 讀數據進行恢復
28 private MyParcelable(Parcel in) {
29 mData = in.readInt();
30 mStr = in.readString();
31 }
32 }
android studio 可以通過android parcelable code generator插件快速生成一個JavaBean對象的序列化相關代碼
AIDL的使用
由上一講我們知道android IPC是通過Binder實現的,但是Binder相關的概念非常復雜,為了方便開發者google就推出了AIDL(安卓接口定義語言)。通過編寫AIDL文件,eclipse或者android studio 就可以幫我們生成Binder通信的相關代碼。開發者即使不了解Binder機制也可以實現IPC了。
AIDl的關鍵字
- oneway
正常情況下Client調用AIDL接口方法時會阻塞,直到Server進程中該方法被執行完。oneway可以修飾AIDL文件里的方法,oneway修飾的方法在用戶請求相應功能時不需要等待響應可直接調用返回,非阻塞效果,該關鍵字可以用來聲明接口或者聲明方法,如果接口聲明中用到了oneway關鍵字,則該接口聲明的所有方法都采用oneway方式。(注意,如果client和Server在同一進程中,oneway修飾的方法還是會阻塞) - in
非基本數據類型和string的參數類型必須加參數修飾符,in的意思是只輸入,既最終server端執行完后不會影響到參數對象 - out
與in相反,out修飾的參數只能由server寫入并傳遞到client,而client傳入的值并不會傳遞到server - inout
被inout修飾的參數,既可以從client傳遞到server,也可以server傳遞到client
AIDL自動生成文件詳解
不多說,直接上代碼
aidl 文件
//方法參數選擇了Intent類型,其他任何實現了Parceable的類型都可以作為方法參數
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void meth1(int args);
void meth2(in Intent args);
void meth3(out Intent args);
void meth4(inout Intent args);
oneway void meth5(inout Intent args);
int meth6(in Intent args);
int meth7(out Intent args);
int meth8(inout Intent args);
oneway int meth9(in Intent args);
Intent meth10(in Intent args);
}
自動生成代碼
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.xns.aidldemo.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.xns.aidldemo.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.xns.aidldemo.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.xns.aidldemo.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//先調用queryLocalInterface,這個方法是IBinder定義的,默認實現是返回NULL,而在BBinder的子類BnInterface中,重載了該方法,返回this,而
//BpInterface并沒有重載,使用IBinder的默認實現,返回NULL。
//簡單的說就是如果server調用binder對象在一個進程就直接返回其本身,如果不在一個進程就返回代理對象
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.xns.aidldemo.IMyAidlInterface))) {
return ((com.xns.aidldemo.IMyAidlInterface) iin);
}
return new com.xns.aidldemo.IMyAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
/**
* 代理類中的每個方法都會通過onTransact來調用server端的接口的方法,此方法運行在server端binder線程池
* @param code
* @param data
* @param reply
* @param flags
* @return
* @throws android.os.RemoteException
*/
@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_meth1: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
this.meth1(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_meth2: {
data.enforceInterface(DESCRIPTOR);
android.content.Intent _arg0;
if ((0 != data.readInt())) {
_arg0 = android.content.Intent.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.meth2(_arg0);
return true;
}
...
...
...
case TRANSACTION_meth10: {
data.enforceInterface(DESCRIPTOR);
android.content.Intent _arg0;
if ((0 != data.readInt())) {
_arg0 = android.content.Intent.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
android.content.Intent _result = this.meth10(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
/**
* server調用接口在client的代理類
*/
private static class Proxy implements com.xns.aidldemo.IMyAidlInterface {
private android.os.IBinder mRemote;
/**
*
* @param remote server與client通信的中介,通過binder驅動交互
*/
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public void meth1(int args) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(args);
//向binder驅動傳輸數據,server端對應的onTransact會被執行
mRemote.transact(Stub.TRANSACTION_meth1, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void meth2(android.content.Intent args) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((args != null)) {
_data.writeInt(1);
//args為in 修飾,此處會將args寫入到需要傳輸的_data中
args.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
//oneway方法,不會阻塞,也就沒有_reply
mRemote.transact(Stub.TRANSACTION_meth2, _data, null, android.os.IBinder.FLAG_ONEWAY);
} finally {
_data.recycle();
}
}
@Override
public void meth3(android.content.Intent args) 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_meth3, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
//args為out類型,所以方法執行完了會回寫args,但是由于沒有in,所以相當于args只是在方法調用完了接受結果,卻不能傳遞參數
args.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void meth4(android.content.Intent args) 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 ((args != null)) {
_data.writeInt(1);
args.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_meth4, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
args.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void meth5(android.content.Intent args) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((args != null)) {
_data.writeInt(1);
args.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_meth5, _data, null, android.os.IBinder.FLAG_ONEWAY);
//雖然args定義為inout,但是由于該方法被oneway修飾,所以不會回寫args,out屬性無效
} finally {
_data.recycle();
}
}
...
...
...
@Override
public int meth9(android.content.Intent args) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((args != null)) {
_data.writeInt(1);
args.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_meth9, _data, null, android.os.IBinder.FLAG_ONEWAY);
} finally {
_data.recycle();
}
//該方法是有返回值的,但是被oneway修飾后該方法不會接受_reply,所以不會返回值.
return _result;
}
@Override
public android.content.Intent meth10(android.content.Intent args) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.content.Intent _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((args != null)) {
_data.writeInt(1);
args.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_meth10, _data, _reply, 0);
_reply.readException();
if ((0 != _reply.readInt())) {
_result = android.content.Intent.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
//所有方法的id,onTransact中switch通過這些id判斷調用哪個方法
static final int TRANSACTION_meth1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_meth2 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_meth3 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_meth4 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_meth5 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_meth6 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
static final int TRANSACTION_meth7 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
static final int TRANSACTION_meth8 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
static final int TRANSACTION_meth9 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 8);
static final int TRANSACTION_meth10 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 9);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void meth1(int args) throws android.os.RemoteException;
public void meth2(android.content.Intent args) throws android.os.RemoteException;
public void meth3(android.content.Intent args) throws android.os.RemoteException;
public void meth4(android.content.Intent args) throws android.os.RemoteException;
public void meth5(android.content.Intent args) throws android.os.RemoteException;
public int meth6(android.content.Intent args) throws android.os.RemoteException;
public int meth7(android.content.Intent args) throws android.os.RemoteException;
public int meth8(android.content.Intent args) throws android.os.RemoteException;
public int meth9(android.content.Intent args) throws android.os.RemoteException;
public android.content.Intent meth10(android.content.Intent args) throws android.os.RemoteException;
}
總結下,IDE其實用我們編寫的AIDL文件幫我們做了這些事:
- 創建了IMyAidlInterface的實現類Stub和Stub的子類Proxy
- Stub類中實現了有IBinder對象轉換為IMyAidlInterface類型的asInterface,asInterface中通過queryLocalInterface(DESCRIPTOR)方法查看本進程是否有IMyAidlInterface在server端的實現類(既判斷Server與Client是否在同一進程),如果是同一進程就直接返回Server端的IMyAidlInterface實現者,如果不在同一進程就返回代理對象
- Proxy類中實現了aidl中定義的方法,根據oneway、in、out、inout修飾符來生成不同的代碼,決定是否向binder驅動寫入數據或者執行完后向方法參數回寫數據。注意:oneway修飾一個方法后,該方法不阻塞client調用線程,但是方法沒有返回值,方法參數在執行方法執行完后也不會回寫。
- Proxy類中實現的方法最終通過transact()方法向Binder驅動寫入數據(運行再client進程),最終Stub類中的onTransact()方法會被調用到(運行在server進程),就這樣完成一次跨進程方法調用。
異常處理
Binder死亡處理
在進程間通信過程中,很可能出現一個進程死亡的情況。如果這時活著的一方不知道另一方已經死了就會出現問題。那我們如何在A進程中獲取B進程的存活狀態呢?
android肯定給我們提供了解決方式,那就是Binder
的linkToDeath
和unlinkToDeath
方法,linkToDeath
方法需要傳入一個DeathRecipient
對象,DeathRecipient
類里面有個binderDied
方法,當binder對象的所在進程死亡,binderDied
方法就會被執行,我們就可以在binderDied
方法里面做一些異常處理,釋放資源等操作了。示例如下:
...
mClientCallBack = IRemoteCallBack.Stub.asInterface(callback);
if (mClientDeathHandler == null) {
mClientDeathHandler = new ClientDeathRecipient();
}
mClientCallBack.asBinder().linkToDeath(new ClientDeathRecipient(), 0);
...
private class ClientDeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
mCallbackList.unregister(mClientCallBack);
mClientCallBack = null;
Logger.d(TAG,"client is died");
}
}
上面是我在server端對client的回調接口的binder對象設置的DeathRecipient。在client死亡時,解注冊client的回調,并且置空。
client注冊回調接口
之前一直說的都是client向server的通信,那如果server要調用client呢?
一個比較容易想到的辦法就是通過AIDL在server端設置一個client的回調。這樣的話就相當于client端是server端的server了。
有注冊回調就肯定有解注冊,但是client端與server不在一個進程,server是無法得知client解注冊時傳入的回調接口是哪一個(client調用解注冊時,是通過binder傳輸到server端,所以解注冊時的回調接口是新創建的,而不是注冊時的回調接口)。為了解決這個問題,android提供了RemoteCallbackList這個類來專門管理remote回調的注冊與解注冊。
用法如下:
//TaskCallback.aidl 用于存放要回調client端的方法
package com.xns.demo.server;
interface ITaskCallback {
void actionPerformed(int actionId);
}
//ITaskBinder.aidl 用于存放供給client端調用的方法
package com.xns.demo.server;
import com.xns.demo.server.ITaskCallback;
interface ITaskBinder {
boolean isTaskRunning();
void stopRunningTask();
void registerCallback(ITaskCallback cb);
void unregisterCallback(ITaskCallback cb);
}
//myservice
package com.xns.demo.server;
import com.xns.demo.server.ITaskBinder;
import com.xns.demo.server.ITaskCallback;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
public class MyService extends Service {
private static final String TAG = "aidltest";
...
@Override
public IBinder onBind(Intent t) {
printf("service on bind");
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
printf("service on unbind");
return super.onUnbind(intent);
}
void callback(int val) {
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
try {
mCallbacks.getBroadcastItem(i).actionPerformed(val);
}
catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast();
}
private final ITaskBinder.Stub mBinder = new ITaskBinder.Stub() {
public void stopRunningTask() {
}
public boolean isTaskRunning() {
return false;
}
public void registerCallback(ITaskCallback cb) {
if (cb != null) {
mCallbacks.register(cb);
}
}
public void unregisterCallback(ITaskCallback cb) {
if(cb != null) {
mCallbacks.unregister(cb);
}
}
};
final RemoteCallbackList <ITaskCallback>mCallbacks = new RemoteCallbackList <ITaskCallback>();
}
//client端
package com.xns.demo;
...
import com.xns.demo.server.*;
public class MyActivity extends Activity {
private static final String TAG = "aidltest";
private Button btnOk;
private Button btnCancel;
...
ITaskBinder mService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = ITaskBinder.Stub.asInterface(service);
try {
mService.registerCallback(mCallback);
} catch (RemoteException e) {
}
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
private ITaskCallback mCallback = new ITaskCallback.Stub() {
public void actionPerformed(int id) {
printf("callback id=" + id);
}
};
}
RemoteCallbackList可以實現正常注冊于解注冊的原因在于注冊與解注冊時雖然對應的回調接口不是同一個,但是其對應的Binder對象卻是同一個。
Messenger與AIDL的異同
其實Messenger的底層也是用AIDL實現的,但用起來還是有些不同的,這里總結了幾點區別:
1. Messenger本質也是AIDL,只是進行了封裝,開發的時候不用再寫.aidl文件。
結合我自身的使用,因為不用去寫.aidl文件,相比起來,Messenger使用起來十分簡單。但前面也說了,Messenger本質上也是AIDL,故在底層進程間通信這一塊,兩者的效率應該是一樣的。
2. 在service端,Messenger處理client端的請求是單線程的,而AIDL是多線程的。
使用AIDL的時候,service端每收到一個client端的請求時,就在BInder線程池中取一個線程去執行相應的操作。而Messenger,service收到的請求是放在Handler的MessageQueue里面,Handler大家都用過,它需要綁定一個Thread,然后不斷poll message執行相關操作,這個過程是同步執行的。
3. client的方法,使用AIDL獲取返回值是同步的,而Messenger是異步的。
Messenger只提供了一個方法進行進程間通信,就是send(Message msg)方法,發送的是一個Message,沒有返回值,要拿到返回值,需要把client的Messenger作為msg.replyTo參數傳遞過去,service端處理完之后,在調用客戶端的Messenger的send(Message msg)方法把返回值傳遞回client,這個過程是異步的,而AIDL你可以自己指定方法,指定返回值,它獲取返回值是同步的(如果沒有用oneway修飾方法的話)。
總的來說,AIDL靈活性更高,如果需要IPC通信的地方比較多,還是更推薦自定義AIDL一點。