跨进程通信之AIDL

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/su_1106941640/article/details/53008075
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则客户端将请求失败。
        
        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();
        }

    最后在onDestroy方法中解除绑定的监听和服务。
 
@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



展开阅读全文

没有更多推荐了,返回首页