Android 中 AIDL 原理分析

Posted on Posted in Android

AIDL 的实现原理就是通过编写 .aidl 文件,让系统自动为我们生成跨进程通信所需的接口文件,然后调用这个接口进行实现就可以达到跨进程通信的目的。使用上篇博客中(Android中的 IPC 方式之使用 AIDL 跨进程通信)讲到的 IBookManager 为例,点进去这个类可以看到系统为我们自动生成的跨进程通讯所需的接口(这个类在 AndroidStudio的位置为 ProjectFolder/app/build/generated/source/aidl/debug/PackageName/IBookManager.java),只是格式比较乱:

跨进程通信的大部分奥秘都在这个文件里了,下面就来详细分析一下这里边的实现逻辑。首先使用 AndroidStudio 的格式化代码快捷键将代码进行格式化:

这样代码的结构就清晰多了。
然后在 Structure 菜单看一下这个类的结构

可以看到 IBookManager 是一个接口,它里面还有一个 Stub 的内部类,这是一个抽象类,并且 Stub 类里还有一个 Proxy 类,根据名字,可知这是一个代理类。
下面来详细的分析一下每一个类的具体实现

IBookManager

这是一个接口,并且它本身还继承了 android.os.IInterface 这个接口,在 IInterface 接口中只有一个方法 asBinder :

package android.os;

/**
 * Base class for Binder interfaces.  When defining a new interface,
 * you must derive it from IInterface.
 */
public interface IInterface {
    /**
     * Retrieve the Binder object associated with this interface.
     * You must use this instead of a plain cast, so that proxy objects
     * can return the correct result.
     */
    public IBinder asBinder();
}

IBookManager 中除了上面说到的 Stub 类,它还定义了几个方法,也就是我们在 aidl 文件中声明的那几个:

public java.util.List<com.chaoyang805.chaptertwo_ipc.Book> getBookList() throws android.os.RemoteException;

public void addBook(com.chaoyang805.chaptertwo_ipc.Book book) throws android.os.RemoteException;

public void registerListener(com.chaoyang805.chaptertwo_ipc.IOnNewBookArrivedListener listener) throws android.os.RemoteException;

public void unregisterListener(com.chaoyang805.chaptertwo_ipc.IOnNewBookArrivedListener listener) throws android.os.RemoteException;

IBookManager 声明了跨进程通信所需要的方法,具体的实现则在 Stub 这个内部类里了。

IBookManager.Stub

Stub 类是一个抽象类,它继承自 Binder 类,并且实现了 IBookManager 接口。跨进程通信的主要实现都在这个类里了。
然后来分析一下这个类的结构。
首先,它声明了几个整型的 id

static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_unregisterListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);

这几个 id 是用来在跨进程通信时用于标识之前声明的几个方法的。

DESCRIPTOR

这个是当前接口的唯一标识符,每个 Binder 都有唯一的一个标识符。一般用当前类的完整类名表示。

private static final java.lang.String DESCRIPTOR = 
"com.chaoyang805.chaptertwo_ipc.IBookManager";
Stub#asBinder

这个方法用于返回当前的 Binder 对象

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

这个方法用于将服务端的 Binder 对象转换成客户端所需的 AIDL 接口类型的对象,上篇博客中实现客户端逻辑时,在 onServiceConnected 方法中就用到了这个方法:

IBookManager manager = IBookManager.Stub.asInterface(iBinder);

然后来看这个方法的具体实现:

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

可以看出,这个转换是分情形的,首先查询本地是否存在这个接口本身,如果存在,就直接返回这个接口,这是服务端和客户端在同一进程的情形;不存在的话就返回一个 Stub.Proxy 对象,这是跨进程通信的情形。
既然跨进程通信会调用到 Proxy 类,那就先来看这个类的实现

这个类是 IBookManager 的具体实现类,它里面有一个 mRemoteIBinder 在构造的时候完成赋值,也就是 Stub.asInterface 方法调用时传入的 obj

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

并且在调用 asBinder 时返回的也是这个远程的 IBinder 对象:

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

然后来看一下我们在 AIDL 中声明的那几个方法的实现:

addBook
@Override
public void addBook(com.chaoyang805.chaptertwo_ipc.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();
    }
}
getBookList
@Override
public java.util.List<com.chaoyang805.chaptertwo_ipc.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.chaoyang805.chaptertwo_ipc.Book> _result;
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
        _reply.readException();
        _result = _reply.createTypedArrayList(com.chaoyang805.chaptertwo_ipc.Book.CREATOR);
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}

由于实现的规律都是相同的,因此就不贴出另外两个方法的实现了。根据前面的分析可以知道跨进程情况下客户端在调用 Stub.asInterface 方法返回的就是一个Proxy对象,因此在客户端调用 IBookManager 接口中定义的方法,其实最终会调用到上面的方法实现。
分析上面的代码,首先定义了两个 Parcel 对象,一个输入行的 _data, 一个输出型 _reply 如果有返回值的话,还会有一个 _result 的返回值对象;然后把方法的传入参数写入到 _data 中(如果有传入参数),接着通过 mRemote.transact 方法发起跨进程方法调用,通过前面的分析知道 mRemote 是服务端 onBind 方法返回的 IBinder 对象,这里的 transact 方法调用会调用到服务端的 onTransact 方法;接着远程方法调用返回后,会从 _reply 中读取返回的结果保存到 _result 中,如果有返回值的话,最后返回 _reply 中的数据。这就是客户端的调用流程。
然后再回来看 Stub 类中的 onTransact 实现,上面说到 调用 mRemote.transact 会执行到 onTransact 方法。从而完成客户端和服务端的通信。下面是 Stub.onTransact 的实现代码:

@Override
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_getBookList: {
            data.enforceInterface(DESCRIPTOR);
            java.util.List<com.chaoyang805.chaptertwo_ipc.Book> _result = this.getBookList();
            reply.writeNoException();
            reply.writeTypedList(_result);
            return true;
        }
        case TRANSACTION_addBook: {
            data.enforceInterface(DESCRIPTOR);
            com.chaoyang805.chaptertwo_ipc.Book _arg0;
            if ((0 != data.readInt())) {
                _arg0 = com.chaoyang805.chaptertwo_ipc.Book.CREATOR.createFromParcel(data);
            } else {
                _arg0 = null;
            }
            this.addBook(_arg0);
            reply.writeNoException();
            return true;
        }
        case TRANSACTION_registerListener: {
            data.enforceInterface(DESCRIPTOR);
            com.chaoyang805.chaptertwo_ipc.IOnNewBookArrivedListener _arg0;
            _arg0 = com.chaoyang805.chaptertwo_ipc.IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
            this.registerListener(_arg0);
            reply.writeNoException();
            return true;
        }
        case TRANSACTION_unregisterListener: {
            data.enforceInterface(DESCRIPTOR);
            com.chaoyang805.chaptertwo_ipc.IOnNewBookArrivedListener _arg0;
            _arg0 = com.chaoyang805.chaptertwo_ipc.IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
            this.unregisterListener(_arg0);
            reply.writeNoException();
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}

在这个方法里,首先根据传入的 code 来判断调用的是哪个方法,然后再从序列化的数据中反序列化调用方法所需的参数(如果有参数的话)。然后调用 Stub 类中具体的接口方法。如果是有返回值的方法,例如 Stub.getBookList 会返回一个 List 就需要将这个返回结果写入到 _reply 中去。这刚好和 Proxy 中的实现相对应。最后需要 return true 表示远程方法调用成功。
至此,整个 IPC 通信的流程就走通了。
分析完后我们可以发现,AIDL 只是为我们提供了实现 IPC 的简单语法,通过 .aidl 文件,AndroidStudio 可以为我们自动生成相关的代码。也就是说如果不用 AIDL,手动实现这个接口同样是可以的:
其中 IBookManager 就相当于跨进程通信的接口,只要我们手动继承自 IInterface 并添加相应的远程调用方法就可以实现;Stub 类相当于 IBookManager 接口的实现类,我们可以把它抽取出来,写成 IBookManagerImpl;然后 Proxy 类是 Stub 类在服务端的一个代理类。明白了这些关系,我们就可以手动写出跨进程通信的代码:

// IBookManager 接口
public interface IBookManager extends IInterface {

    static final String DESCRIPTOR = "com.chaoyang805.chaptertwo_ipc.aidl.IBookManager";
    static final int TRANSACTION_getBookList = IBinder.FIRST_CALL_TRANSACTION + 0;
    static final int TRANSACTION_addBook = IBinder.FIRST_CALL_TRANSACTION + 1;
    static final int TRANSACTION_registerListener = IBinder.FIRST_CALL_TRANSACTION + 2;
    static final int TRANSACTION_unregisterListener = IBinder.FIRST_CALL_TRANSACTION + 3;

    public List<Book> getBookList() throws RemoteException;

    public void addBook(Book book) throws RemoteException;
    public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException;
    public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException;
}


// IBookManager 接口的实现类 BookManagerImpl
public class BookManagerImpl extends Binder implements IBookManager {
    private static final String TAG = "BookManagerImpl";
    private List<Book> mBooks;
    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList;

    public BookManagerImpl(List<Book> books, RemoteCallbackList<IOnNewBookArrivedListener> listeners) {
        this();
        mBooks = books;
        mListenerList = listeners;

    }

    public BookManagerImpl() {
        this.attachInterface(this, DESCRIPTOR);
    }

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

    public static IBookManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (iin != null && iin instanceof IBookManager) {
            return (IBookManager) iin;
        }
        return new BookManagerImpl.Proxy(obj);
    }

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_getBookList: {

                data.enforceInterface(DESCRIPTOR);
                List<Book> bookList = this.getBookList();
                reply.writeNoException();
                reply.writeTypedList(bookList);
                return true;
            }
            case TRANSACTION_addBook: {

                data.enforceInterface(DESCRIPTOR);
                Book arg0;
                if (0 != data.readInt()) {
                    arg0 = Book.CREATOR.createFromParcel(data);
                } else {
                    arg0 = null;
                }
                this.addBook(arg0);
                reply.writeNoException();
                return true;
            }
            case TRANSACTION_registerListener: {

                data.enforceInterface(DESCRIPTOR);
                IOnNewBookArrivedListener arg0;
                arg0 = IOnNewBookArrivedListenerImpl.asInterface(data.readStrongBinder());
                this.registerListener(arg0);
                reply.writeNoException();
                return true;
            }
            case TRANSACTION_unregisterListener: {
                IOnNewBookArrivedListener arg0;
                data.enforceInterface(DESCRIPTOR);
                arg0 = IOnNewBookArrivedListenerImpl.asInterface(data.readStrongBinder());
                this.unregisterListener(arg0);
                reply.writeNoException();
                return true;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }

    @Override
    public List<Book> getBookList() {
        return mBooks;
    }

    @Override
    public void addBook(Book book) {
        if (mBooks != null) {
            mBooks.add(book);
        }
    }

    @Override
    public void registerListener(IOnNewBookArrivedListener listener) {
        mListenerList.register(listener);
    }

    @Override
    public void unregisterListener(IOnNewBookArrivedListener listener) {
        mListenerList.unregister(listener);
    }
     // BookManagerImpl 类的代理类 Proxy
    private static class Proxy implements IBookManager {

        private IBinder mRemote;

        public Proxy(IBinder remote) {
            mRemote = remote;
        }

        public String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }

        @Override
        public List<Book> getBookList() throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            List<Book> result;
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                mRemote.transact(TRANSACTION_getBookList, data, reply, 0);
                reply.readException();
                result = reply.createTypedArrayList(Book.CREATOR);
            } finally {
                data.recycle();
                reply.recycle();
            }
            return result;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                if (book != null) {
                    data.writeInt(1);
                    book.writeToParcel(data, 0);
                } else {
                    data.writeInt(0);
                }
                mRemote.transact(TRANSACTION_addBook, data, reply, 0);
                reply.readException();

            } finally {
                data.recycle();
                reply.recycle();
            }
        }

        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                data.writeStrongBinder(listener != null ? listener.asBinder() : null);
                mRemote.transact(TRANSACTION_registerListener, data, reply, 0);
                reply.readException();
            } finally {
                data.recycle();
                reply.recycle();
            }


        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                data.writeStrongBinder(listener != null ? listener.asBinder() : null);
                mRemote.transact(TRANSACTION_unregisterListener, data, reply, 0);
                reply.readException();
            } finally {
                data.recycle();
                reply.recycle();
            }
        }

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

}

然后在 Service 中,直接返回 BookManagerImpl 类的对象即可:

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        mBinder = new BookManagerImpl(mBooks, mListenerList);
        return mBinder;
    }

同样在客户端 使用 BookManagerImpl 来进行 IBinder 对象的转换:


private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            IBookManager manager = BookManagerImpl.asInterface(iBinder);
            mBookManager = manager;
            try {
                mBookManager.registerListener(mListener);

                List<Book> books = manager.getBookList();
                Log.d(TAG, "query book list, list type:" + books.getClass().getCanonicalName());
                Log.d(TAG, "query book list:" + books.get(0).bookName);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

当然,手动实现只是为了了解其中的原理,平时的话还是主要以效率为主,用 AIDL 实现就行了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注