AIDL的Proxy-Stub理解:(以实际媒体播放器服务单独运行在一个进程中为例)

来源:互联网 发布:淘宝官方活动报名 编辑:程序博客网 时间:2024/06/05 07:33

AIDL的Proxy-Stub理解:(以实际媒体播放器服务单独运行在一个进程中为例)

一、从接口定义入手
1、定义AIDL接口  AudioPlayerAidl.aidl文件

interface AudioPlayerAidl { // 注意,这里不要使用public关键字/* * 打开媒体,但不播放 * @param path 文件路径 */int openMedia(String path);// 方便起见,仅定义一个接口函数}


二、分析由aidl文件生成的AudioPlayerAidl.java文件(生成在gen目录下)

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);}@Overridepublic IBinder asBinder() {return this;}// 该函数由父类transact调用 (根据code码确定调用哪个函数),接收来自客户端的请求@Overridepublic 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();// 注意,从这里调用到了在服务端实现的openMediaint _result = this.openMedia(path);reply.writeNoException();reply.writeInt(result);return true;}}// 父类Binder处理return super.onTransact(code, data, reply, flags);}// Stub的内部类Proxyprivate static class Proxy implements AudioPlayerAidl {// 服务端IBinder接口private IBinder mRemote;// 代理对象被创建的时候被传入远程服务端IBinderProxy(IBinder remote) {mRemote = remote;}public IBinder asIBinder() {return mRemote;}public String getInterfaceDescriptor() {return DESCRIPTOR;}// Proxy端口实现openMedia (会远程调用的服务端相应接口)@Overridepublic 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函数)

@Overridepublic IBinder onBind(Intent intent) {return new LocalBinder();}public LocalBinder extends AudioPlayerAidl.Stub {@Overridepublic 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() {@Overridepublic void onServiceDisconnected(ComponentName arg0) {mAudioPlayerAidl = null;}@Overridepublic 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);









0 0
原创粉丝点击