IPC机制之AIDL的学习笔记(二)

作者: vison123 | 来源:发表于2017-08-22 16:28 被阅读38次

    前言

    上篇文章已经写了aidl的使用步骤,记录了在编写过程中踩过的坑。使用还是比较简单的,虽然步骤中的细节比较多。但是有没有想过这个aidl到底是怎么工作的呢。我们可不可以脱离aidl文件来实现我们的跨进程通信功能呢?答案当然是可以的。这篇文章我们主要去分析一下aidl的大体实现步骤,最后我们自己脱离aidl文件,写一个java文件来代替它。实现我们上一篇实现的功能。

    正文

    • 效果图展示
      gif
      通过上篇的编写,我们可以看到,aidl的工作流程图这样的。
      1.在服务端的aidl文件中定义提供给客户端调用的接口方法
      2.在服务端的service定义一个binder,binder里面实现这个接口方法。然后在onbind方法中返回这个binder
      3.在客户端的ServiceConnection中拿到这个binder,然后调用binder的方法,这样就实现了客户端调用服务端的方法了。
      我们从使用的地方开始看一下,首先看一下这个在客户端拿到的这个IMyAidlInterface
    binder = IMyAidlInterface.Stub.asInterface(service);
    

    点击去看

    public static com.example.newserver.IMyAidlInterface asInterface(android.os.IBinder obj)
    {
    if ((obj==null)) {
    return null;
    }
    //这里是从本地查看是否有这个IMyAidlInterface ,有的话直接返回
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof com.example.newserver.IMyAidlInterface))) {
    return ((com.example.newserver.IMyAidlInterface)iin);
    }
    //没有的话返回这个Proxy,一开始都是没有的,我们去看看这个Proxy
    return new com.example.newserver.IMyAidlInterface.Stub.Proxy(obj);
    }
    

    接着我们来看看这个Proxy

    private static class Proxy implements com.example.newserver.IMyAidlInterface{
    
    private android.os.IBinder mRemote;
    Proxy(android.os.IBinder remote)
    {
    mRemote = remote;
    }
    
    @Override
     public android.os.IBinder asBinder()//这个方法就是返回这个Binder
    {
    return mRemote;
    }
    public java.lang.String getInterfaceDescriptor()
    {
    return DESCRIPTOR;
    }
    /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
    @Override public void addPerson(com.example.newserver.Person person) 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 ((person!=null)) {
    _data.writeInt(1);
    person.writeToParcel(_data, 0);
    }
    else {
    _data.writeInt(0);
    }
    mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
    _reply.readException();
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    }
    
    //可以看到,客户端调用的getPersonList就是这个方法
    @Override 
    public java.util.List<com.example.newserver.Person> getPersonList() throws android.os.RemoteException
    {
    //_data是客户端写进去的参数,_reply是通过计算,服务返回来的值
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    java.util.List<com.example.newserver.Person> _result;
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    // 参数说明:
    // code:Client进程请求方法标识符。即Server进程根据该标识确定所请求的目标方法
    // data:目标方法的参数。(Client进程传进来的)
    // reply:目标方法执行后的结果(返回给Client进程)
    //第四个参数是一个 int 值,它的作用是设置进行 IPC 的模式,为 0 表示数据可以双向流通,即 _reply 流可以正常的携带数据回来,如果为 1 的话那么数据将只能单向流通,从服务端回来的 _reply 流将不携带任何数据。
    mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);
    //读取_reply值
    _reply.readException();
    //将reply值进行解序列化得到person的list,然后返回给客户端
    _result = _reply.createTypedArrayList(com.example.newserver.Person.CREATOR);
    }
    finally {
    //data和reply的回收
    _reply.recycle();
    _data.recycle();
    }
    return _result;
    }
    }
    

    上面我就拿getPersonList这个方法做了注释,addPerson的流程也是一样的。主要的方法就是调用了服务端传过来的Binder的transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0),那为什么执行了这个方法这个方法就执行了服务的对应的接口方法了呢?其实是调用了IMyAidlInterface这个类里面的onTransact(code, data, reply, flags)方法,方法的参数值和Transact方法是一样的。至于为什么是调用了这个onTrasact方法,这个就是系统底层的东西,这个并不得而知。这里就不去分析它了。
    我们来看看onTransact(code, data, reply, flags)这个方法

    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{
    switch (code)
    {
    case INTERFACE_TRANSACTION:
    {
    reply.writeString(DESCRIPTOR);
    return true;
    }
    case TRANSACTION_addPerson:
    {
    data.enforceInterface(DESCRIPTOR);
    com.example.newserver.Person _arg0;
    if ((0!=data.readInt())) {
    _arg0 = com.example.newserver.Person.CREATOR.createFromParcel(data);
    }
    else {
    _arg0 = null;
    }
    this.addPerson(_arg0);
    reply.writeNoException();
    return true;
    }
    //这个TRANSACTION_getPersonList就是transact方法的code参数,标志识别哪个方法
    case TRANSACTION_getPersonList:
    {
    data.enforceInterface(DESCRIPTOR);
    //这里就是关键了,是调用了真正实现的方法,也就是Service里面的getPersonLisst
    java.util.List<com.example.newserver.Person> _result = this.getPersonList();
    reply.writeNoException();
    reply.writeTypedList(_result);
    return true;
    }
    }
    return super.onTransact(code, data, reply, flags);
    }
    

    这里同样分析getPersonList这个方法,由于这个IMyAidlInterface是一个接口

    public interface IMyAidlInterface extends android.os.IInterface
    

    当执行getPersonList的时候,执行了this.getPersonList()这个方法,这个方法是IMyAidlInterface这个接口定义的没有实现的方法。

    public void addPerson(com.example.newserver.Person person) throws android.os.RemoteException;
    public java.util.List<com.example.newserver.Person> getPersonList() throws android.os.RemoteException;
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
    }
    

    那问题来了,到底哪里实现它呢?答案就是SerVice里面的IMyAidlInterface.Stub这个实现类,这个实现类是继承IMyAidlInterface这个接口的。

    public static abstract class Stub extends android.os.Binder implements com.example.newserver.IMyAidlInterface
    

    来看看Service里面的这个实现类

       IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
            @Override
            public void addPerson(Person person) throws RemoteException {
                if (null == mPersonArrayList) {
                    mPersonArrayList = new ArrayList<>();
                }
                mPersonArrayList.add(person);
            }
    
            @Override
            public List<Person> getPersonList() throws RemoteException {
                if (null != mPersonArrayList) {
                  return mPersonArrayList;
                }
                return null;
            }
    
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
    
            }
        };
    

    看到这里,终于就明白了吧,就是调用了Service里面的方法。
    来写一个总结流程:
    1.首先客户端先通过ServiceConnection获取了服务端的定义的一个binder
    2.拿到binder,然后通过一个代理类Proxy调用服务端定义的方法。服务端定义的方法就会执行Transact方法
    3.Transact方法又会执行Binder类的的方法onTransact方法
    4.onTrsact方法调用对应服务端的实现类IMyAidlInterface.Stub里面的方法。
    我们看到,这其实就是binder在中间起到了连接的作用,实际上实现了binder就实现了跨进程通信的能力,中间就是Transact方法和onTransact的执行。我们也完全可以通过这个步骤来脱离aidl文件来实现这个功能。
    来看一下怎么实现。

    1.首先客户端先通过ServiceConnection获取了服务端的定义的一个binder

     ServiceConnection mConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                binder = MyProXy.getInstance(service);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
        };
    

    2.拿到binder,然后通过一个代理类Proxy调用服务端定义的方法。服务端定义的方法就会执行Transact方法
    这个代理类是在客户端调用的。

    public class MyProXy {
    
        public static final int ADD = 0;
        public static final int GET = 1;
    
        private static IBinder mRemote;
        private static MyProXy myProXy;
    
        public MyProXy() {
        }
    
        public static MyProXy getInstance(IBinder binder) {
            myProXy = new MyProXy();
            mRemote = binder;
            return myProXy;
        }
    
        /**
         * 添加一个英雄
         * @param person
         */
        public void addPerson(Person person) {
    
            Parcel _write = Parcel.obtain();
            Parcel _reply = Parcel.obtain();
            if (null != person) {
                _write.writeInt(1);
                person.writeToParcel(_write, 0);
            } else {
                _write.writeInt(0);
            }
            try {
                mRemote.transact(ADD, _write, _reply, 0);
                _reply.readException();
            } catch (RemoteException e) {
                e.printStackTrace();
            }finally {
                _write.recycle();
                _reply.recycle();
            }
        }
    
        /**
         * 获取英雄列表
         * @return
         */
        public List<Person> getPersonList(){
            Parcel _data = Parcel.obtain();
            Parcel _reply = Parcel.obtain();
            List<Person> mList = new ArrayList<>();
    
            try {
                mRemote.transact(GET,_data,_reply,0);
                _reply.readException();
                mList = _reply.createTypedArrayList(Person.CREATOR);
            } catch (RemoteException e) {
                e.printStackTrace();
            }finally {
                _data.recycle();
                _reply.recycle();
            }
            return mList;
        }
    }
    

    3.Transact方法又会执行Binder类的的方法onTransact方法
    这个类是写在服务端的,里面有抽象方法,供Service的实现类实现

    public abstract class Stub extends Binder {
    
        public static final int ADD = 0;
        public static final int GET = 1;
    
        public abstract void addPerson(Person person);
        public abstract List<Person> getPerson();
    
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            switch (code){
                case ADD:
                    Person person;
                    if (0 != data.readInt()) {
                        person = Person.CREATOR.createFromParcel(data);
                    } else {
                        person = null;
                    }
                    this.addPerson(person);
                    reply.writeInt(1);
                    reply.writeNoException();
                    return true;
                case GET:
                    List<Person> mList;
                    mList = this.getPerson();
                    reply.writeNoException();
                    reply.writeTypedList(mList);
                    return true;
                default:
                    return super.onTransact(code, data, reply, flags);
            }
        }
    }
    

    4.onTrsact方法调用对应服务端的实现类IMyAidlInterface.Stub里面的方法

    public class MyService extends Service {
    
        private List<Person> mPersonList = new ArrayList<>();
    
        Stub mStub = new Stub() {
            @Override
            public void addPerson(Person person) {
                if (null == mPersonList) {
                    mPersonList = new ArrayList<>();
                }
                if (null != person) {
                    mPersonList.add(person);
                }
            }
    
            @Override
            public List<Person> getPerson() {
                if (null != mPersonList) {
                    return mPersonList;
                }
                return null;
            }
        };
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return mStub;
        }
    }
    

    到这里就结束了,可以看到实现还是挺简单的。

    结语

    Android中跨进程通信的方法还是挺多的,这两篇只是讲了其中aidl方式。

    image.png

    附上Demo传送门~

    相关文章

      网友评论

        本文标题:IPC机制之AIDL的学习笔记(二)

        本文链接:https://www.haomeiwen.com/subject/ppakdxtx.html