AIDL的Proxy-Stub理解:(以实际媒体播放器服务单独运行在一个进程中为例)
一、从接口定义入手1、定义AIDL接口 AudioPlayerAidl.aidl文件
interface AudioPlayerAidl { // 注意,这里不要使用public关键字
/*
* 打开媒体,但不播放
* @param path 文件路径
*/
int openMedia(String path);// 方便起见,仅定义一个接口函数
}
1、生成文件如下
public interface AudioPlayerAidl extends android.os.IInterface {
// 内部Stub抽象类(实现AudioPlayerAidl接口)
// 注意:该类是需要我们在服务端去继承并实现AudioPlayerAidl接口
public static abstract class Stub extends android.os.Binder implements AudioPlayerAidl {
// Binder描述符
public static final java.lang.String DESCRIPTOR = "com.zhonghong.zhmedia.service.AudioPlayerAidl";
// 构造器
public Stub() {
this.attachInterface(this, DESCRIPTOR);//Binder类中的一个函数
}
// 供客户端调用的重要函数(获取远程调用接口)
public static AudioPlayerAidl asInterface(IBinder ib) {
if (ib == null) {
return null;
}
// 在同一个进程时不会生成代理类
IInterface iin = ib.queryLocalInterface(DESCRIPTOR);//根据attachInterface中返回相应对象
if (iin != null && iin instanceof AudioPlayerAidl) {
return ((AudioPlayerAidl) iin);
}
// 必要时生成代理类(跨进程通信时)
return new AudioPlayerAidl.Stub.Proxy(ib);
}
@Override
public IBinder asBinder() {
return this;
}
// 该函数由父类transact调用 (根据code码确定调用哪个函数),接收来自客户端的请求
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {//IBinder的一个通信协议常量
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_openMedia: {
data.enforceInterface(DESCRIPTOR);
String path;
path = data.readString();
// 注意,从这里调用到了在服务端实现的openMedia
int _result = this.openMedia(path);
reply.writeNoException();
reply.writeInt(result);
return true;
}
}
// 父类Binder处理
return super.onTransact(code, data, reply, flags);
}
// Stub的内部类Proxy
private static class Proxy implements AudioPlayerAidl {
// 服务端IBinder接口
private IBinder mRemote;
// 代理对象被创建的时候被传入远程服务端IBinder
Proxy(IBinder remote) {
mRemote = remote;
}
public IBinder asIBinder() {
return mRemote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
// Proxy端口实现openMedia (会远程调用的服务端相应接口)
@Override
public int openMedia(String path) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(path);
// 调用到服务端openMedia函数
mRemote.transact(Stub.TRANSACTION_openMedia, _data, _reply, 0);
_reply.readException();
// 得到服务端返回码
_reply = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_openMedia = (IBinder.FIRST_CALL_TRANSACTION + 0);
}
// ======================================接口函数声明=================================
/**
* 打开媒体,但不播放
* @param path
* @return 状态码
*/
public int openMedia(java.lang.String path) throws android.os.RemoteException;
// ======================================接口函数声明=================================
}
三、服务端AudioPlayerService返回的IBinder对象继承抽象类Stub(需要实现 AudioPlayerAidl接口中的openMedia函数)
@Override
public IBinder onBind(Intent intent) {
return new LocalBinder();
}
public LocalBinder extends AudioPlayerAidl.Stub {
@Override
public int openMedia(String path) throws RemoteException {
return AudioPlayerService.this.openMedia(path);
}
}
四、在AudioPlayerService服务端真正的openMedia方法实现 (注意: 该服务是在单独的进程中运行,才会有这一系列的繁琐过程)
public int openMedia(String path) {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setDataSource(path);
mMediaPlayer.prepare();
//.....
}
五、在应用进程中(和服务端不在同一个进程)绑定服务时:
context.bindService(intent, mServiceConn, Service.BIND_AUTO_CREATE);
private ServiceConnection mServiceConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName arg0) {
mAudioPlayerAidl = null;
}
@Override
public void onServiceConnected(ComponentName cn, IBinder ib) {
// 这个ib就是服务端的LocalBinder, 在这里用Proxy包装
mAudioPlayerAidl = AudioPlayerAidl.Stub.asInterface(ib);
}
};
服务端与客户端交互:(跨进程通信)
一、关键在于IBinder接口的transact函数
以及其实现类Binder的onTransact函数
1、服务端(提供服务的一方, 如本例中提供打开媒体的真正实现(AudioPlayerService所在进程))
其实就是 LocalBinder,它继承了 AudioPlayerAidl.Stub,而Stub不过是服务端的代理,继承了Binder,
因此也就实现了IBinder接口。
2、客户端
当客户端需要调用服务时, 使用服务端Stub.asInterface(IBinder ib)函数获取客户端代理Proxy,
Proxy中含有一个服务端的IBinder接口mRemote(其实就是LocalBinder), 这样调用IBinder的transact函数
就间接调用到了Stub(继承了Binder)中的onTransact函数
3、根据transact和onTransact的控制反转关系(code协议控制)
// Proxy中调用
// 传入的code值用来判断调用哪个函数
transact(int code, Parcel data, Parcel reply, int flags);
// Stub中调用
// 根据code值来具体调用服务端的函数
onTransact(int code, Parcel data, Parcel reply, int flags);
Binder的死亡代理:
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mAudioPlayerAidl == null) return;
mAudioPlayerAidl.asBinder().unlinkToDeath(mDeathRecipient, 0);
mAudioPlayerAidl = null;
// TODO: 在这里重新绑定远程Service
}
};
mAudioPlayerAidl = AudioPlayerAidl.Stub.asInterface(ib);
ib.linkToDeath(mDeathRecipient, 0);