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

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);
				}
				
				@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);









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值