美文网首页
Android多进程机制(二)Binder工作机制

Android多进程机制(二)Binder工作机制

作者: Utte | 来源:发表于2018-06-13 18:47 被阅读23次

    Binder工作机制

    我们可以先从SDK自动为我们生成的AIDL对应的Binder类来分析Binder工作机制。

    建立AIDL

    AIDL中使用了Binder,所以我们先通过AIDL来分析一下Binder工作过程。先创建一个Book类,实现Parcelable接口。

    public class Book implements Parcelable {
    
        public int bookId;
        public String bookName;
    
        public Book(int bookId, String bookName) {
            this.bookId = bookId;
            this.bookName = bookName;
        }
    
        private Book(Parcel in) {
            bookId = in.readInt();
            bookName = in.readString();
        }
    
        public static final Creator<Book> CREATOR = new Creator<Book>() {
            @Override
            public Book createFromParcel(Parcel in) {
                return new Book(in);
            }
    
            @Override
            public Book[] newArray(int size) {
                return new Book[size];
            }
        };
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(bookId);
            dest.writeString(bookName);
        }
    
    }
    

    创建两个AIDL文件,Book.aidl用来声明Book类,IBookManager.aidl为定义的接口,写两个方法。

    Book.aidl

    package com.utte.aidltest;
    
    parcelable Book;
    

    IBookManager.aidl

    package com.utte.aidltest;
    
    import com.utte.aidltest.Book;
    
    interface IBookManager {
        List<Book> getBookList();
        void addBook(in Book book);
    }
    

    目录像这样

    可以发现虽然Book.java在同一个包中,但是在IBookManager.aidl中用Book时还是需要import。

    生成Binder类

    此时如果build就会在generated目录下找到一个IBookManager.java,这是系统自动为IBookManager.aidl生成的Binder类。

    虽然生成了IBookManager.java,但是可能会报错找不到Book类。

    在Module:app的Gradle的android{}中加如下代码,就能解决了。

    sourceSets {
        main {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            java.srcDirs = ['src/main/java', 'src/main/aidl']
            resources.srcDirs = ['src/main/java', 'src/main/aidl']
            aidl.srcDirs = ['src/main/aidl']
            res.srcDirs = ['src/main/res']
            assets.srcDirs = ['src/main/assets']
        }
    }
    

    分析IBookManager.aidl

    先看一下整体结构:

    package com.utte.aidltest;
    public interface IBookManager extends android.os.IInterface {
        private static final java.lang.String DESCRIPTOR = "com.utte.aidltest.IBookManager";
        public static abstract class Stub extends android.os.Binder implements com.utte.aidltest.IBookManager {
            
            public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
    
            public static com.utte.aidltest.IBookManager asInterface(android.os.IBinder 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 {
                //...
            }
    
            private static class Proxy implements com.utte.aidltest.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.utte.aidltest.Book> getBookList() throws android.os.RemoteException {
                    //...
                }
    
                @Override
                public void addBook(com.utte.aidltest.Book book) throws android.os.RemoteException {
                    //...
                }
            }
    
            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.utte.aidltest.Book> getBookList() throws android.os.RemoteException;
    
        public void addBook(com.utte.aidltest.Book book) throws android.os.RemoteException;
    }
    
    

    来看我画了好久的图:

    IBinderManager.png

    IBookManager:

    • IBookManager继承了IInterface接口。

    所有需要在Binder中运输的接口都需要继承IInterface。

    • 声明了两个抽象方法getBookList()和addBook()。
    • 还定义了一个静态内部抽象类Stub。

    Stub:

    • Stub继承Binder、实现IBookManager,是个抽象类。

    Stub其实就是Binder类。

    • Stub定义了一个静态方法asInterface。
    • 实现了IInterface的asBinder()。
    • 实现了Binder的onTransact()。
    • 定义了一个静态内部类Proxy。
    • 声明了两个整型id。

    这两个id用于标识IBookManager中的两个抽象方法,用于区分transact过程中客户端具体请求的是哪个方法。

    Proxy:

    • Proxy真正实现了最外层的IBookManager。

    Stub的内部代理,主要控制是否需要跨进程transact的逻辑。

    • 定义了getInterfaceDescriptor()。
    • 实现了IInterface的asBinder()。
    • 实现了IBookManager定义的抽象方法getBookList()和addBook()。

    下面来看这里两个类中成员的具体含义和细节。

    DESCRIPTOR

    Binder的唯一标识,一般用Binder的类名表示。

    private static final java.lang.String DESCRIPTOR = "com.utte.aidltest.IBookManager";
    
    asInterface()

    用于将服务端中的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程的,如果客户端服务端位于同一进程,那么此方法返回的是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy对象。

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

    返回当前Binder对象。

    • Stub asBinder():返回当前对象。
    @Override
    public android.os.IBinder asBinder() {
        return this;
    }
    
    • Proxy asBinder():返回构造器传入的Binder对象。
    @Override
    public android.os.IBinder asBinder() {
        return mRemote;
    }
    
    onTransact()

    这个方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。具体流程:

    1. 服务端通过code来判断客户端请求的目标方法是什么。
    2. data装着目标方法的参数,将参数取出。
    3. 传入参数,调用执行code所代表的方法。
    4. 执行对应方法完毕后,向reply中写入目标方法的返回值。
    5. 如果onTransact()返回false,那么客户端会请求失败。
    @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.utte.aidltest.Book> _result = this.getBookList();
                reply.writeNoException();
                reply.writeTypedList(_result);
                return true;
            }
            case TRANSACTION_addBook: {
                data.enforceInterface(DESCRIPTOR);
                com.utte.aidltest.Book _arg0;
                if ((0 != data.readInt())) {
                    _arg0 = com.utte.aidltest.Book.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }
                this.addBook(_arg0);
                reply.writeNoException();
                return true;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }
    
    Proxy#getBookList()

    这个方法运行在客户端。客户端远程调用此方法时,具体流程如下:

    1. 创建方法所需要的Parcel型的_data和_reply,再创建真正的返回值对象_result。
    2. 把方法参数写入_data(如果有参数)。
    3. 调用transact()来发起远程过程调用(RPC)请求,当前线程挂起。
    4. 服务端onTransact()被调用。
    5. RPC返回,当前线程继续执行,从_reply中取出返回结果构造_result。
    6. 回收_data和_reply,返回_result。
    @Override
    public java.util.List<com.utte.aidltest.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.utte.aidltest.Book> _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
            _reply.readException();
            _result = _reply.createTypedArrayList(com.utte.aidltest.Book.CREATOR);
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }
    
    Proxy#addBook()

    和Proxy#getBookList()基本一致,只不过没有返回值,不需要从_reply中取出返回值。

    @Override
    public void addBook(com.utte.aidltest.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();
        }
    }
    

    注意点与总结

    • 远程请求是耗时的。客户端发起请求时,当前线程会被挂起直到服务端返回数据,所以不能再UI线程进行远程请求。
    • 服务端的onTransact()运行在Binder线程池中,所以此Binder方法是否耗时,都应该采用同步方式去实现,因为它已经运行在一个单独线程中了。
    • 如果因为某些原因,服务端进程异常终止,如果我们不知道连接已经断裂,那么客户端就会受到影响。应该通过Binder的linkToDeath和unlinkToDeath设置死亡代理。
    • Binder工作机制图:

    自己实现Binder

    不依赖AIDL,自己可以根据上面的分析来实现一个Binder。

    相关文章

      网友评论

          本文标题:Android多进程机制(二)Binder工作机制

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