Android Binder学习笔记(一)

本文参考《Android开发艺术探索》,建议照着代码看文章。GitHub-完整代码链接

Binder的跨进程工作机制

Android Binder学习笔记(一)_第1张图片
Binder工作机制.png

通过Binder实现跨进程通信,提供服务的进程被称为服务端,请求服务的进程是客户端。

服务端:服务端首先要创建一个AIDL文件,将暴露给客户端的方法在这个AIDL文件中声明,接着创建一个Binder类继承AIDL接口中的Stub类,并实现Stub中的抽象方法。然后创建一个Service用来监听客户端的连接请求,在Service的onBind方法中返回这个Binder类的对象。

客户端:客户端首先要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。

不知道上面的文字在说什么,没关系,下面看个例子。这个例子实现跨进程获取书籍列表和添加书籍的功能。

获取书籍列表调用过程如下图所示,可以先大致浏览一下。

Android Binder学习笔记(一)_第2张图片
Android Binder学习笔记(一).jpg

我们先从服务端开始

定义一个Book类实现Parcelable接口,实现Parcelable接口是为了能够实现跨进程传输。

public class Book implements Parcelable {

    private int bookId;
    private String bookName;

    public Book() {
    }

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    public static final Creator CREATOR = new Creator() {
        @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);
    }

    /**
     * 参数是一个Parcel,用它来存储与传输数据
     * @param dest
     */
    public void readFromParcel(Parcel dest) {
        //注意,此处的读值顺序应当是和writeToParcel()方法中一致的
        bookId = dest.readInt();
        bookName = dest.readString();
    }

    @Override
    public String toString() {
        return "{bookId:" + bookId + ",bookName:" + bookName + "}";
    }
    //省略getter/setter
   
}

如果一个AIDL文件中用到了自定义的Parcelable对象类,那么必须新建一个和它同名的AIDL文件,并在其中声明它为parcelable类型。

为Book类声明对应的AIDL文件Book.aidl

// Book.aidl
package com.hm.aidlserver;

parcelable Book;

定义一个AIDL文件IBookManager.aidl,在其中声明获取书籍列表和添加书籍的方法。

// IBookManager.aidl
package com.hm.aidlserver;
//显式引入Book类
import com.hm.aidlserver.Book;

interface IBookManager {

   ListgetBookList();
   void addBook(in Book book);
}

注意:自定义的Parcelable对象类和AIDL文件必须要显式的import进来,不管它们是否和当前的AIDL文件位于同一个包内。

声明完了AIDL文件,重新build项目即可在build目录下看到系统为我们生成的用于进程间通信的类

../build/generated/source/aidl/debug/com/hm/aidlserver/IBookManager.java

我将该类调整了一下格式方便查看

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/dumingwei/AndroidStudioProjects/AIDLDemo/aidlserver/src/main/aidl/com/hm/aidlserver/IBookManager.aidl
 */
package com.hm.aidlserver;

public interface IBookManager extends android.os.IInterface {

    //抽象方法,获取书籍列表
    public java.util.List getBookList() throws android.os.RemoteException;

    //抽象方法,添加书籍
    public void addBook(com.hm.aidlserver.Book book) throws android.os.RemoteException;

    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.hm.aidlserver.IBookManager {

        private static final java.lang.String DESCRIPTOR = "com.hm.aidlserver.IBookManager";

        //标志获取书籍列表的方法
        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        //标志添加书籍的方法
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

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

        /**
         * Cast an IBinder object into an com.hm.aidlserver.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.hm.aidlserver.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            //客户端调用queryLocalInterface(String descriptor)方法,返回的肯定是null
            //服务端调用queryLocalInterface(String descriptor)方法,返回的不是null
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.hm.aidlserver.IBookManager))) {
                return ((com.hm.aidlserver.IBookManager) iin);
            }
            //客户端最终返回的是一个代理对象
            return new com.hm.aidlserver.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 _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(descriptor);
                    com.hm.aidlserver.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.hm.aidlserver.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.hm.aidlserver.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 getBookList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.hm.aidlserver.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

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

各个类的职责

  • IBinder :IBinder 是一个接口,代表了一种跨进程通信的能力。只要实现了这个接口,这个对象就能跨进程传输。IBinder的直接子类是BinderBinderProxy。这两个类都继承自 IBinder, 因而都具有跨进程传输的能力;Binder代表的是服务端Binder本地对象。BinderProxy类,它是服务端的Binder对象的代理对象;实际上,在跨越进程的时候,Binder驱动会自动完成这两个对象的转换,客户端真正拿到的是服务端的Binder对象的代理对象(即一个BinderProxy类型的对象)。

  • IInterface :IInterface 代表的就是 Server 进程对象具备什么样的能力(能提供哪些方法,其实对应的就是 AIDL 文件中定义的接口)。

  • Stub :编译工具会给我们定义的AIDL 文件生成一个名为 Stub 的静态内部类;这个类继承了Binder, 说明它是一个Binder本地对象,它实现了IInterface接口,表明它具有 Server承诺给Client 的能力。Stub是一个抽象类,具体的IInterface的相关实现需要开发者自己实现。

  • Stub.Proxy:服务端Binder对象的代理类,客户端是通过BinderProxy对象获取到服务端Binder对象的代理类Proxy,然后通过这个代理类调用服务端的方法。

自动生成的IBookManager.java中,声明了我们在AIDL文件中定义的方法

//抽象方法,获取书籍列表
public java.util.List getBookList() throws android.os.RemoteException;

//抽象方法添加书籍
public void addBook(com.hm.aidlserver.Book book) throws android.os.RemoteException;

IBookManager.java文件中,还为我们生成了一个抽象的内部类Stub

public static abstract class Stub extends android.os.Binder implements com.hm.aidlserver.IBookManager {

    //用来标志Binder对象的名字
    private static final java.lang.String DESCRIPTOR = "com.hm.aidlserver.IBookManager";

    //标志获取书籍列表的方法
    static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    //标志添加书籍的方法
    static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        
    //... 
        
        
}

注意:Stub抽象类继承了android.os.Binder类并实现了com.hm.aidlserver.IBookManager接口,但是并没有实现com.hm.aidlserver.IBookManager接口中的抽象方法。我们最后在服务端返回的Binder对象要继承Stub类并实现com.hm.aidlserver.IBookManager接口中的抽象方法。

在Stub类中,还为我们生成了一个私有的静态内部类Proxy,客户端就是使用这个代理类来实现和服务端进行通信的,后面再细说。

private static class Proxy implements com.hm.aidlserver.IBookManager {
    //...
}

服务端Service的实现

/**
 * 服务端的Service
 */
public class BookManagerService extends Service {

    private static final String TAG = BookManagerService.class.getSimpleName();

    //使用CopyOnWriteArrayList,因为可能会有多个客户端同时操作书籍列表造成线程同步的问题
    private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList<>();
    
    public BookManagerService() {
    }
    
    /**
     * 实现IBookManager接口中的方法
     */
    private Binder mBinder = new IBookManager.Stub() {

        @Override
        public List getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }
    }


    @Override
    public void onCreate() {
        super.onCreate();
        //创建Service的时候先主动添加两个书籍
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "ios"));
    }

    @Override
    public IBinder onBind(Intent intent) {
        //返回binder对象
        return mBinder;
    }
}

在BookManagerService中我们创建了一个Binder对象mBinder,mBinder实现IBookManager.aidl中定义的方法。然后我们在onBind方法中返回mBinder对象即可。

我们的书籍列表类型使用了CopyOnWriteArrayList,因为可能会有多个客户端同时操作书籍列表造成线程同步的问题。

注意:不要忘了在AndroidManifest.xml文件中注册BookManagerService



到这,一个简单的服务端就实现了,接下来我们实现客户端。

客户端的实现

首先把在服务端定义的类和AIDL文件都拷贝到客户端(具体的项目结构可以下载源码看一看),重新build项目即可在build目录下看到系统为我们生成的用于进程间通信的类。

绑定远程服务

    //定义IBookManager对象
    private IBookManager bookManager;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //注释1处,注意这个的service对象是一个BinderProxy对象
            Log.e(TAG, "onServiceConnected:" + service.getClass().getCanonicalName());
            //注释2处,获取bookManager对象,注意这个传入的service对象是一个BinderProxy对象
            bookManager = IBookManager.Stub.asInterface(service);
            Log.e(TAG, "onServiceConnected:" + bookManager.getClass().getCanonicalName());

            try {
                getBookList();
                bookManager.registerListener(mOnNewBookArriveListener);
            } catch (RemoteException e) {
                e.printStackTrace();
                Log.e(TAG, "onServiceConnected: error" + e.getMessage());
            }
        }
        //...
    };

绑定服务

private void bind() {
    binded = true;
    Intent intent = new Intent();
    intent.setComponent(new ComponentName("com.hm.aidlserver", "com.hm.aidlserver.BookManagerService"));
    bindService(intent, connection, Context.BIND_AUTO_CREATE);
}

绑定远程服务以后,我们就可以调用服务端的方法了

获取书籍列表

public void getBookList() {
    if (bookManager != null) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //获取书籍列表
                    bookList = bookManager.getBookList();
                    Log.e(TAG, "query book list,list type:" + bookList.getClass().getCanonicalName());
                    Log.e(TAG, "query book list,list:" + bookList.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    } 
}

添加书籍

public void addBook() {
    if (bookManager != null) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //添加书籍
                    bookManager.addBook(new Book(bookId, "Android 进阶" + bookId));
                    bookId++;
                    //添加完毕后,重新获取一下书籍列表
                    bookList = bookManager.getBookList();
                    Log.e(TAG, "query book list,list type:" + bookList.getClass().getCanonicalName());
                    Log.e(TAG, "query book list,list:" + bookList.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    } 
}

注意:当客户端调用服务端的方法时,如果是耗时方法的话,我们不能在主线程调用,否则可能会导致ANR,所以我们开启新线程来调用服务端的方法。

现在我们已经可以愉快的调用服务端的方法了,即实现了进程间通信。

接下来我们研究一下其中的一些细节。先从客户端我们定义的IBookManager对象和ServiceConnection对象。

深入细节

    //定义IBookManager对象
    private IBookManager bookManager;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //注释1处,
            Log.e(TAG, "onServiceConnected:" + service.getClass().getCanonicalName());
            //注释2处,获取bookManager对象,注意这个传入的service对象是一个BinderProxy对象
            bookManager = IBookManager.Stub.asInterface(service);
            Log.e(TAG, "onServiceConnected:" + bookManager.getClass().getCanonicalName());

            try {
                getBookList();
                bookManager.registerListener(mOnNewBookArriveListener);
            } catch (RemoteException e) {
                e.printStackTrace();
                Log.e(TAG, "onServiceConnected: error" + e.getMessage());
            }
        }
        //...
    };

我们打印了service的类名bookManager的类型,如下所示。

onServiceConnected:android.os.BinderProxy
onServiceConnected:com.hm.aidlserver.IBookManager.Stub.Proxy

说明当绑定到服务端的Service的时候,服务端返回的service对象是一个BinderProxy类型的对象而我们在客户端定义的bookManager最终指向了一个IBookManager.Stub.Proxy类型的对象。这点要注意,后面还要再说。

在注释2处,调用了IBookManager.Stub的asInterface(android.os.IBinder obj)方法。

public static com.hm.aidlserver.IBookManager asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    //注释1处,客户端调用IBinder类的queryLocalInterface(String descriptor)方法,返回的是null
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.hm.aidlserver.IBookManager))) {
        return ((com.hm.aidlserver.IBookManager) iin);
    }
    //注释2处,客户端最终返回的是一个代理对象
    return new com.hm.aidlserver.IBookManager.Stub.Proxy(obj);
}

注意:asInterface方法的注释1处,客户端调用IBinder类的queryLocalInterface(String descriptor)方法,返回的是null。为什么呢?IBinder接口有两个实现类,一个是Binder,一个是BinderProxy。我们在onServiceConnected(ComponentName name, IBinder service)方法中得到的service是一个BinderProxy类型的对象。

BinderProxy的queryLocalInterface(String descriptor)方法直接返回的是null。

public IInterface queryLocalInterface(String descriptor) {
    return null;
}

asInterface方法的注释2处,客户端最终返回的是一个代理对象。也就是说我们客户端的bookManager对象是一个IBookManager.Stub.Proxy对象。我们获取书籍列表和添加书籍都是调用IBookManager.Stub.Proxy类中的方法,接下来看一下。

    private static class Proxy implements com.hm.aidlserver.IBookManager {
        //注释1处
        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 getBookList() throws android.os.RemoteException {
            //构建发送到服务端的数据
            android.os.Parcel _data = android.os.Parcel.obtain();
           //构建服务端返回的数据
            android.os.Parcel _reply = android.os.Parcel.obtain();
            //要获取的书籍列表
            java.util.List _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                //注释1处,标记调用服务端的getBookList方法
                mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                _reply.readException();
               //获取查询到的书籍列表
                _result = _reply.createTypedArrayList(com.hm.aidlserver.Book.CREATOR);
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            //返回结果
            return _result;
        }

        @Override
        public void addBook(com.hm.aidlserver.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对象写入到data中
                    book.writeToParcel(_data, 0);
                } else {
                    _data.writeInt(0);
                }
                //注释1处,标记调用服务端的addBook方法
                mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                _reply.readException();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
        }

    }

注释1处,Proxy类中的mRemote就是一个BinderProxy对象。

BinderProxy的transact(int code, Parcel data, Parcel reply, int flags)方法。

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    //检查数据大小,不能大于800K
    Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
    //...
    try {
        //注释1处,
        return transactNative(code, data, reply, flags);
    } finally {
        //...
    }
}

注释1处,调用了BinderProxy的transactNative(int code, Parcel data, Parcel reply, int flags)方法,这是一个本地方法,最终会调用服务端Binder对象的transact方法。这个Binder对象就是我们在服务端Service中返回的对象(IBookManager.Stub类型)。

     /**
     * 实现IBookManager接口中的方法
     */
    private Binder mBinder = new IBookManager.Stub() {

        @Override
        public List getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }
    }

我们看一下IBookManager.Stub继承了Binder类,并没有重写transact方法

Binder类transact方法

public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
            int flags) throws RemoteException {
    
    if (data != null) {
        //读取客户端传递的数据
        data.setDataPosition(0);
    }
    //注释1处,调用onTransact方法
    boolean r = onTransact(code, data, reply, flags);
    if (reply != null) {
        //设置返回给客户端的数据
        reply.setDataPosition(0);
    }
    return r;
}

注释1处,调用了onTransact方法,IBookManager.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_getBookList: {
            data.enforceInterface(descriptor);
            java.util.List _result = this.getBookList();
            reply.writeNoException();
            //返回数据
            reply.writeTypedList(_result);
            //返回true表示调用成功
            return true;
        }
        case TRANSACTION_addBook: {
            data.enforceInterface(descriptor);
            com.hm.aidlserver.Book _arg0;
            if ((0 != data.readInt())) {
                _arg0 = com.hm.aidlserver.Book.CREATOR.createFromParcel(data);
            } else {
                _arg0 = null;
            }
            //添加书籍
            this.addBook(_arg0);
            reply.writeNoException();
            return true;
        }
        default: {
            return super.onTransact(code, data, reply, flags);
        }
    }
}

参考链接

  • 《Android开发艺术探索》
  • 写给 Android 应用工程师的 Binder 原理剖析

你可能感兴趣的:(Android Binder学习笔记(一))