1.基本介绍。
当我们在项目中新建一个.aidl文件时,编译器一般会自动帮我们生成一个与前面.aidl文件同名的java文件,该文件是一个继承自
IInterface的接口。比如我们创建了一个A.aidl文件,此时对应的就会有一个A.java文件生成(下面我们用接口A替代)。接口A主要由下面几部分组成:
a.我们在A.aidl文件中声明方法;在接口A中会用不同的id标记这些方法,以便客户端发起请求时区分请求的是那个方法。
b.静态抽象内部类Stub,Stub类是继承Binder类并且实现了接口A;该类运行在服务端,主要负责处理客户端发起的请求。在该类中有几个重要的方法:
·asBinder():返回当前Binder对象。
·asInterface(IBinder):将服务端的Binder对象转换为客户端所需的AIDL接口类型对象,如果客户端和服务端处于同一进程,则返回服务端的Stub对象本身,否则返回系统封装后的Stub.Proxy对象。
·onTransact(int,Parcel,Parcel,int):该方法运行在服务端的Binder线程池中,客户端发起的请求都会经过系统底层封装之后再交给这个方法处理,当这些封装之后的请求达到该方法时,先根据code判断客户端请求的是哪个方法,然后执行对应的方法,执行方式时如果目标方法有参数则从data中取出,执行完方法后如果目标方法有返回值,则将返回值写入reply,最后返回true,如果返回false则客户端将请求失败。
·onTransact(int,Parcel,Parcel,int):该方法运行在服务端的Binder线程池中,客户端发起的请求都会经过系统底层封装之后再交给这个方法处理,当这些封装之后的请求达到该方法时,先根据code判断客户端请求的是哪个方法,然后执行对应的方法,执行方式时如果目标方法有参数则从data中取出,执行完方法后如果目标方法有返回值,则将返回值写入reply,最后返回true,如果返回false则客户端将请求失败。
c.Stub类的静态内部类Proxy:这个类也是实现我们在.aidl文件中声明的接口,并实现接口中的方法,且这些被实现的方法都运行在客户端。
上面我们说了由.aidl文件中生成的java文件是一个是继承
IInterfac的接口,而
IInterfac是Binder接口的基类,也就是说实现了该接口的类就是一个Binder类,
IInterfac只声明了一个方法,那就是asBinder()。
虽然说编译器会自动帮我们生成一个与我们创建的.aidl同名的java文件,但是这个不是固定的步骤,换句话说,我们完全可以自己写一个与.aild对应的java文件,可能这能加深我们对AIDL的理解和方便查看,具体用什么方法生成相应的java文件,看各人喜欢咯。
2.使用AIDL跨进程通信实践。
使用AIDL进行跨进程通信主要有三个步骤,第一,创建一个.aidl文件,在这个文件中声明我们需要暴露给客户端的接口;第二,创建service,负责监听客户端的请求,然后实现在.aidl文件中接口声明的方法;第三,创建客户端,绑定服务端的Service,绑定成功后将服务端返回的Binder对象转换成AIDL接口所需的类型,然后调用aidl中的方法。
为了方便,我还是和上一篇文章
《跨进程通信之Messenger
》一样,在同一个应用中实现跨进程通信。接下来将根据上面所说的三步,一步一步理解如何使用AIDL实现跨进程通信。
·创建.aidl文件:
interface IBook {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book newBook);
}
第一个接口IBook中生命的方法是要暴露给客户端的;第二个接口IOnNewBookArrivedListener是为了在第一个接口中使用而声明的,因为.aidl文件中不能使用普通接口。
当上面的.aidl文件创建完成后,Make Project之后在工程中就能找到对应的.java文件。
·创建service:该类主要负责监听客户端的请求和实现来自.aidl中接口的方法,而要实现该接口需要继承Binder接口,所以我们使用了下面的方法取得Binder对象并实现接口中的方法。(
关于Stub类在第一部分有说明,忘了的话可以到第一部分看看。
)
private Binder mBinder = new IBook.Stub(){
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.unregister(listener);
}
@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.register(listener);
}
};
取得Binder对象之后,我们要将接口暴露给客户端,以便客户端调用接口中的方法,所以要实现onBind方法,并且在该方法中我们要返回取得的Bind对象。
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
·创建客户端:在客户端第一步是绑定服务,如下:
Intent intent = new Intent(this,BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
然后在onServiceConnected方法中接收服务端返回的Binder对象,并将其转换成客户端所需的AIDL接口类型,实现这步之后就可以调用在.aidl文件中声明的接口中的方法,
private ServiceConnection mConnection = new ServiceConnection() {
/**
* 与服务建立连接时调用
* @param name 建立连接的组件名
* @param service 已连接组件中的IBinder
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IBook book = IBook.Stub.asInterface(service);
try{
mRemoteManager = book;
List<Book> list = book.getBookList();
Book newBook = new Book(3, "Android 进阶");
book.addBook(newBook);
List<Book> newList = book.getBookList();
book.registerListener(mOnNewBookArrivedListener);
}catch (RemoteException e){
e.printStackTrace();
}
@Override
protected void onDestroy() {
if (mRemoteManager != null && mRemoteManager.asBinder().isBinderAlive()){
try{
Log.e(TAG, "unregister listener:" + mOnNewBookArrivedListener);
mRemoteManager.unregisterListener(mOnNewBookArrivedListener);
}catch (RemoteException e){
e.printStackTrace();
}
}
unbindService(mConnection);
super.onDestroy();
}
3.总结:在理解了Messenger的使用之后,理解AIDL使用方法会容易很多,不过在上一篇讲Messenger的时候也说到了,客户端请求可能会有几个同时到达服务端,所以使用AIDL还要考虑线程同步的问题,而且当客户端请求服务端的方法是耗时的时候,还要注意应该另开线程去执行而不是在UI线程执行请求。上面的例子只是基本的使用方法而已,要用好AIDL还得更多的实践,如果上面所说有误,恳请指正。
4.相关书籍:《Android开发艺术》
http://www.android-doc.com/guide/components/aidl.html