Android学习之旅Android开发经验谈Android技术知识

Android学习之旅-使用 AIDL 实现进程间通信02[艺术

2018-12-08  本文已影响10人  TengFeiGo

本篇文章将着重于分析 AIDL 是如何通过 Binder 来实现进程间通信的,当然只是分析它的上层应用,不然 Binder 让我现在的能力去看绝对是云里雾里,没多大价值,倒不如退而求其次,毕竟学习需要一步一步的来,而且前面的两篇文章也是这周学习完 IPC 机制后利用空余时间来一次实践总结,我计划在每周学习完一个专题的内容后都去总结一遍,一步一步的提示自身的开发能力。
好了,通过上篇文章我们知道 AIDL 其实就是帮助我们来实现进程间通信的一个工具,新建完 AIDL 文件重新 rebuild 之后在 AS 的 build 文件夹下会自动生成 java 代码,其实就是协助我们进行跨进程通信的,你可以理解为 AIDL 是在 binder 的基础上进行了一次封装,方便了我们的开发者,👌下面看代码:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/tengfei/AndroidStudy/Android-HotYong/01_AndroidCrossProcess/client/src/main/aidl/com/example/tengfei/aidl/IBookManager.aidl
 */
package com.example.tengfei.aidl;

public interface IBookManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.example.tengfei.aidl.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.example.tengfei.aidl.IBookManager";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.example.tengfei.aidl.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.example.tengfei.aidl.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.tengfei.aidl.IBookManager))) {
                return ((com.example.tengfei.aidl.IBookManager) iin);
            }
            return new com.example.tengfei.aidl.IBookManager.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_getBookList: {
                    data.enforceInterface(descriptor);
                    java.util.List<com.example.tengfei.aidl.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(descriptor);
                    com.example.tengfei.aidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.example.tengfei.aidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.example.tengfei.aidl.IBookManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public java.util.List<com.example.tengfei.aidl.Book> getBookList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.example.tengfei.aidl.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.example.tengfei.aidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addBook(com.example.tengfei.aidl.Book book) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public java.util.List<com.example.tengfei.aidl.Book> getBookList() throws android.os.RemoteException;

    public void addBook(com.example.tengfei.aidl.Book book) throws android.os.RemoteException;
}


代码很多,但其实整体结构上还是很清晰的,首先是一个接口 IBookManager 继承了 IInterface ,在接口内部定义了两个方法 getBookList() 和 addBook(com.example.tengfei.aidl.Book book) 只有继承了 IInterface 的接口才可以通过 binder 传递,在其内部定义了一个静态抽象类 Stub 继承自 Binder 并且实现了 IBookManager 接口,所以说 IBookManager.java
这个类的核心就是 Stub 类,下面让我们来具体看一下 Stub 的内部结构。

  1. DESCRIPTOR : 给继承自 binder 的 stub 类定义一个唯一的标记,一般用当前的类名来表示,在 Stub 的构造方法中 调用 binder 的 attachInterface 并将当前定义的 DESCRIPTOR 传递进参数中,其作用是为了将特定接口与 binder 关联
  2. TRANSACTION_getBookList : 一个 int 整型值用来标记 getBookList 方法
  3. TRANSACTION_addBook : 一个 int 整型值用来标记 addBook 方法
  4. asInterface(android.os.IBinder obj) : 将服务端的 binder 对象转换为客户端所需要的 AIDL 接口类型的对象,如果是客户端与服务端处于同一个进程,那么返回的就是服务端的 Stub 对象,否则返回的就是代理类 Proxy 对象
  5. asBinder : 返回当前的 binder 对象
  6. onTransact : 运行在服务端 binder 线程池中,调用远程服务的 transact 过程其实最终就是回调 onTransact 方法,通过它参数中的 code 可以确定调用的是哪一个方法,从 data 中取出调用的目标方法所需要的参数,当方法执行完毕后向reply中写入返回值,值得注意的是如果 onTransact 方法的返回值是 false 那么客户端的请求会失败
  7. Proxy : Proxy 是一个代理类,它实现了 IBookManager 接口,在其内部重写了 getBookList 和 addBook 方法,这两方法运行在客户端中,由我们开发者自己去调用,让我们简单分析下这个类的具体实现,看代码:
 private static class Proxy implements com.example.tengfei.aidl.IBookManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public java.util.List<com.example.tengfei.aidl.Book> getBookList() throws android.os.RemoteException {
                //创建输入型Parcel对象
                android.os.Parcel _data = android.os.Parcel.obtain();
                //创建输出型Parcel对象
                android.os.Parcel _reply = android.os.Parcel.obtain();
                //返回值对象
                java.util.List<com.example.tengfei.aidl.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.example.tengfei.aidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addBook(com.example.tengfei.aidl.Book book) throws android.os.RemoteException {
                 //创建输入型Parcel对象
                android.os.Parcel _data = android.os.Parcel.obtain();
                //创建输出型Parcel对象
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

首先它是一个静态内部类,我们不管这些,其实都可以分开来写的,将我们的注意力重点放在 getBookList 和 addBook 方法上,首先是 getBookList 方法,分别通过 Parcel.obtain() 构造出 两个Parcel 对象 _data 和 _reply 一个用于输入数据,一个用于输出数据,Parcel是一个容器,它可以包含数据或者是对象引用,并且能够用于Binder的传输。调用 Parcel 的 writeInterfaceToken 用于说明给当前的数据加上中介数据用来标识data是给含有DESCRIPTOR标志的Binder接口的参数,这句话的意思是说我 data 的数据是给定义了 DESCRIPTOR 标记的接口来用的,这本示例中对应着 Stub 类,紧接着调用 IBinder 的 transact 方法来执行远程RPC过程,调用 transact 方法最终回调服务端 binder线程池终的 onTransact 方法,至于 addBook 与 getBookList 差不多,就不讲啦。。。

总结:

1.定义一个接口继承自 IInterface,在接口中定义客户端与服务端通信的方法,只有继承了 IInterface 的接口才能在binder中传递
2.定义一个抽象类继承自 Binder 并且实现了 IInterface 类型的接口,在它的构造方法中为此类定义唯一的标记,在 asInterface 方法中将服务端的 IBinder 对象返回为客户端所需要的 AIDL 接口类型的对象,但是是有区别的,如果客户端与服务端处于同一个进程,那么返回的是服务端的 Stub 对象,否则返回的就是 Stub.Proxy 代理类对象
3.代理类 Proxy 实现了 IInterface类型的接口,并重写相关方法,这些方法运行在客户端,由我们开发人员自己调用,并最终调用 IBinder 的 transact 方法执行远程 RPC 过程,在 transact 方法中传入指定执行方法的标记以及输入输出型Parcel对象和 flag ,flag 一般定义为 0
4.执行 transact 方法最终会回调服务端binder线程池中的 onTransact 方法,在 onTransact 方法中通过传递过来的 code 可以判断出具体要执行的方法,在其内部执行写入和读取数据的操作的相关逻辑
5.客户端在发起远程请求时,客户端会被挂起知道服务端返回数据给客户端才会被唤醒

最后用一张流程图来描述下使用 AIDL 是如何实现进程间通信的,如下:


AIDL执行进程间通信的方式

我上面写的不一定都是对的,仅仅是我个人学习过程中的思考以及摘录,在以后的学习过程中如果我发现了错误我会及时改正,也请开发前辈们如果发现了本文的错误及时指出,大家一起进步😄😄😄😄

参考资料

1、Android开发艺术探索
2、对Binder的浅显分析及AIDL的使用
3、Android Parcel对象详解

上一篇下一篇

猜你喜欢

热点阅读