美文网首页
aidl基础用法和原理解析

aidl基础用法和原理解析

作者: 坤_RTFSC | 来源:发表于2020-11-10 16:18 被阅读0次

aidl是android中非常重要的进程间通信的方式,底层还是用的binder来实现的。今天就重点讲解一下aidl的使用和原理;

基础使用

aidl本质上也是采用的c/s架构,首先创建一个server端:

server端

  • 1.右键生成aidl文件
interface IMyAidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
    String getName();
    void setName(String name);
}

basicTypes这个可以删掉的。这个方法是介绍了基础的用法,可以用哪些变量;后面就可以自定义方法了。

  • 2.创建service
@Override
    public IBinder onBind(Intent intent) {
        return new Like();
    }

    class Like extends IMyAidlInterface.Stub{
        @Override
        public String getName() throws RemoteException {
            return "i'm yours";
        }

        @Override
        public void setName(String name) throws RemoteException {
        }
    }

创建一个内部类,继承自aidl.stub。然后重写方法;
然后在onBind方法中return 这个内部类的对象。

Client端

client端的写法就很简单了。

Intent intent= new Intent(action);
        intent.setPackage(packagename);
        bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        }, Context.BIND_AUTO_CREATE);

就是一个bindservice的过程;这样两个app就能通信啦。怎么样,是不是很简单,接下来讲下实现的原理;

原理分析

要知道原理首先肯定得知道在编译aidl文件时,发生了什么。观察看目录知道其实是生成了一个同名的java文件,下面我们来研究这个java文件里都有啥。

/**
    public static class Default implements com.qikun.demo.IMyAidlInterface {
        @Override
        public void setName(java.lang.String name) throws android.os.RemoteException {
        }

        @Override
        public java.lang.String getName() throws android.os.RemoteException {
            return null;
        }

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

首先生成了一个默认的实现了此接口的类,叫default;
重点看stub类,此类实现了IMyAidlInterface的接口并继承了binder。

public static com.qikun.demo.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.qikun.demo.IMyAidlInterface))) {
                return ((com.qikun.demo.IMyAidlInterface) iin);
            }
            return new com.qikun.demo.IMyAidlInterface.Stub.Proxy(obj);
        }

将服务端的ibinder对象转化为客户端所需要的接口对象。

如果有必要的话,产生一个代理对象Proxy。否则直接查询本地的对象,就解决了同一个进程里使用aidl的困境)
来分析一下这个proxy代理对象是怎么回事:
首先,proxy构造需要传入一个ibinder对象;

@Override
            public java.lang.String getName() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        return getDefaultImpl().getName();
                    }
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

一个data,一个reply,都是parcel对象。注意最核心的代码:

mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);

mRemote其实就是proxy初始化的时候传入进来的binder对象。transact是什么方法呢?

/**
     * Perform a generic operation with the object.
     * 
     * @param code The action to perform.  This should
     * be a number between {@link #FIRST_CALL_TRANSACTION} and
     * {@link #LAST_CALL_TRANSACTION}.
     * @param data Marshalled data to send to the target.  Must not be null.
     * If you are not sending any data, you must create an empty Parcel
     * that is given here.
     * @param reply Marshalled data to be received from the target.  May be
     * null if you are not interested in the return value.
     * @param flags Additional operation flags.  Either 0 for a normal
     * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
     *
     * @return Returns the result from {@link Binder#onTransact}.  A successful call
     * generally returns true; false generally means the transaction code was not
     * understood.
     */
    public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
        throws RemoteException;

这里就从官方文档注释大概知道意思:

  • 1.code表示要执行什么动作;
  • 2.data和reply就是一个传入值一个返回值;
  • 3.flag只有0和flag_oneway,0表示client需要等待server返回,而flag_oneway表示这是一个单项的活动,无需等待。所以参数为0时表示可能会有阻塞,这里要多注意.

okay,让我们再回到stub里面来,关注一下onTransact方法:

 @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_setName: {
                    data.enforceInterface(descriptor);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    this.setName(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getName: {
                    data.enforceInterface(descriptor);
                    java.lang.String _result = this.getName();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

可以看到stub根据不同的code去执行不同的方法;

小结

通过以上的源码分析能够得出一次aidl的IPC(inter-process communication)大致流程:

  • a.客户端通过bindService绑定服务端的service
  • b.拿到service之后拿到ibinder对象,再通过接口的Stub.asInterface将ibinder对象转化为接口对象。
  • c.拿到接口对象之后调用接口的api,api里通过binder.transact方法调起远程通信,如果flag是0,等待结果回来。
  • d.远程接收到后,通过写入参数到reply参数中去,然后再调用binder.transact方法返回。

这里再稍微提一下binder机制。因为这个实在是太重要了对于android系统来说。
首先android系统不同进程是不共享空间的,但是内核空间是共享的,因此binder 驱动层肯定是在内核空间的,相当于一个底层中转站的概念。后续将花大篇幅介绍binder。

相关文章

网友评论

      本文标题:aidl基础用法和原理解析

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