天天看点

通过 AIDL 分析 Binder 的工作机制

前言

简单来讲,Binder 是 Android 中的一个类,它实现了 IBinder 接口。从 IPC 角度来讲,Binder 是 Android 中的一种跨进程通信方式,可以将 Binder 理解为一种虚拟的物理设备,它的设备驱动是 /dev/binder,该通信方式在 Linux 中并没有。

Binder作用初见

  1. 从 Android Framework 角度来说:

    Binder 是 ServiceManager 连接各种 Manager (ActivityManager、WindowManager等)和相应 ManagerService 的桥梁。

  2. 从 Android 应用层来说:

    Binder 是客户端和服务端进行通信的媒介, 当 bindService 的时候,服务端会返回一个包含了服务端业务调用的 Binder 对象,通过这个 Binder 对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于 AIDL 的服务。

Binder 工作机制

Android 开发中,Binder 主要用在 Service 中,包括 AIDL 和 Messenger,其中普通 Service 中的 Binder 不涉及进程间通信,较为简单,无法触及 Binder 的核心。而 Messenger 的底层其实是 AIDL,所以这里选择 AIDL 来分析 Binder 的工作机制。

1、新建 java 和 aidl 文件

文件结构如下:

通过 AIDL 分析 Binder 的工作机制

Book.java

package com.glp.binder.aidl;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by Guolipeng on 2017/9/8.
 * 通过 AIDL 来分析 Binder 的工作过程
 */

public class Book implements Parcelable {
    public int bookId;
    public String bookName;

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

    @Override
    public int describeContents() {
        return ;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.bookId);
        dest.writeString(this.bookName);
    }

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

    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };
}
           

Book.aidl

package com.glp.binder.aidl;

parcelable Book;
           

IBookManager.aidl

package com.glp.binder.aidl;

import com.glp.binder.aidl.Book;
// 【注意】尽管 Book 类已经和 IBookManager 位于相同的包中,但是在 IBookManager 中仍然需要导入 Book 类,这就是 AIDL 的特殊之处。

interface IBookManager {
    List<Book> getBookList(); // 用于从远程服务端获取图书列表
    void addBook(in Book book); // 用于往图书列表中添加一本书
}
           

上面三个文件中:

  • Book.java 表示图书信息的类,实现了 Parcelable 接口
  • Book.aidl 是 Book 类在 AIDL 中的声明
  • IBookManager.aidl 是我们定义的一个接口,定义我们将来需要的方法

2、构建自动生成

然后我们构建一下项目,就会在

app\build\generated\source\aidl\debug\

目录下找到系统为我们自动生成的

com.glp.binder.aidl.IBookManager.java

文件。下面通过在自动生成的 java 类中进行代码注释的方式,分析一下 Binder 的工作原理:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\Wiwide\\WindowStudy\\app\\src\\main\\aidl\\com\\glp\\binder\\aidl\\IBookManager.aidl
 */
package com.glp.binder.aidl;

public interface IBookManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    // 声明了内部类 Stub 就是一个 Binder 类
    // 当客户端和服务端都位于同一个进程时,方法调用不会走 transact 过程   (transact  [træn'zækt] 交易;谈判;处理)
    // 当两者位于不同进程时,方法调用需要走跨进程的 transact 过程,这个逻辑由 Stub 的内部代理类 Proxy 来完成  (proxy  ['prɒksɪ] n. 代理人;委托书)
    public static abstract class Stub extends android.os.Binder implements com.glp.binder.aidl.IBookManager {

        // Binder 的唯一标识,一般用当前 Binder 的类名表示
        private static final java.lang.String DESCRIPTOR = "com.glp.binder.aidl.IBookManager";

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

        /**
         * Cast an IBinder object into an com.glp.binder.aidl.IBookManager interface,
         * generating a proxy if needed.
         */
        // 用于将服务端的 Binder 对象转换成客户端所需要的 AIDL 接口类型的对象,该转换过程是区分进程的
        // 客户端和服务端位于同一进程,此方法返回的就是服务端的 Stub 本身,否则返回的是系统封装后的 Stub.Proxy 对象
        public static com.glp.binder.aidl.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.glp.binder.aidl.IBookManager))) {
                return ((com.glp.binder.aidl.IBookManager) iin);
            }
            return new com.glp.binder.aidl.IBookManager.Stub.Proxy(obj);
        }

        // 返回当前的 Binder 对象
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        // 该方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            // 服务端通过code来确定客户端请求的目标方法是什么
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getBookList: {
                    data.enforceInterface(DESCRIPTOR);
                    // 目标方法无参数
                    java.util.List<com.glp.binder.aidl.Book> _result = this.getBookList();
                    reply.writeNoException();
                    // 目标方法执行完成之后,向reply中写入返回值(如果目标方法有返回值的话)
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    // 从data中取出目标方法所需要的参数(如果目标方法有参数的话),然后去执行目标方法
                    com.glp.binder.aidl.Book _arg0;
                    if (( != data.readInt())) {
                        _arg0 = com.glp.binder.aidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    // 目标方法无返回值
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
            // 如果 onTransact 方法返回false,那么客户端请求就会失败,可以利用这个特性来做权限验证
            //(也不希望随便一个进程都能远程调用我们的服务)
        }

        // Stub 的内部代理类 Proxy
        private static class Proxy implements com.glp.binder.aidl.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;
            }

            // 在 IBookManager.aidl 中声明的方法(运行在客户端)
            @Override
            public java.util.List<com.glp.binder.aidl.Book> getBookList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();   // 创建方法所需要的输入型Parcel对象 _data
                android.os.Parcel _reply = android.os.Parcel.obtain();  // 创建方法所需要的输出型Parcel对象 _reply
                java.util.List<com.glp.binder.aidl.Book> _result;       // 创建方法返回值对象 _result
                try {
                    // 方法无参数
                    _data.writeInterfaceToken(DESCRIPTOR);
                    // 调用 transact 方法来发起 RPC(远程过程调用)请求,同时当前线程挂起
                    // 然后服务端的 onTransact 方法会被调用,直到 RPC 过程返回后,当前线程继续执行
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, );
                    _reply.readException();
                    // 从 _reply 中取出 RPC 过程的返回结果
                    _result = _reply.createTypedArrayList(com.glp.binder.aidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                // 最后返回 结果
                return _result;
            }

            // 在 IBookManager.aidl 中声明的方法(运行在客户端)
            @Override
            public void addBook(com.glp.binder.aidl.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);
                    // 将方法参数写进 _data(如果有参数的话)
                    if ((book != null)) {
                        _data.writeInt();
                        book.writeToParcel(_data, );
                    } else {
                        _data.writeInt();
                    }
                    // 调用 transact 方法来发起 RPC(远程过程调用)请求,同时当前线程挂起
                    // 然后服务端的 onTransact 方法会被调用,直到 RPC 过程返回后,当前线程继续执行
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, );
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        // 声明两个整型的id分别用于标识两个方法,这两个标识用于区分在跨进程的 transact 过程中客户端请求的到底是哪个方法
        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + );
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + );
    }

    public java.util.List<com.glp.binder.aidl.Book> getBookList() throws android.os.RemoteException;

    public void addBook(com.glp.binder.aidl.Book book) throws android.os.RemoteException;
}
           

通过上述代码我们可以发现,系统根据 IBookManager.aidl 为我们自动生成的 IBookManager.java 类继承了 IInterface 接口,同时它自己还是个接口。【注意】所有可以在 Binder 中传输的接口都需要继承 IInterface 接口。

3、额外说明

  • 当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回数据,所以如果一个远程方法是很耗时的,那么不能在 UI 线程中发起远程请求。
  • 由于服务端的 Binder 方法运行在 Binder 的线程池中,所以 Binder 方法不管是否耗时,都应该采用同步的方式去实现,因为它已经运行在一个线程中了。

4、Binder 工作机制图

通过 AIDL 分析 Binder 的工作机制

补充

我们知道,Binder 运行在服务端进程中,如果服务端进程由于某种原因异常终止,这个时候我们到服务端的 Binder 连接断裂(称为 Binder 死亡),会导致我们的远程调用失败。更为关键的是,如果我们不知道 Binder 连接已经断裂,那么客户端的功能就会受到影响。为了解决这个问题,Binder 中提供了两个配对的方法 linkToDeath 和 unlinkToDeath。通过 linkToDeath 可以给 Binder 设置一个死亡代理,当 Binder 死亡时,我们会收到通知,这个时候我们就可以重新发起连接请求从而恢复连接。

设置死亡代理示例:

private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.d(TAG, "binder died. tname:" + Thread.currentThread().getName());
            if (mRemoteBookManager == null)
                return;
            mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, );
            mRemoteBookManager = null;
            // TODO:这里重新绑定远程Service
        }
    };
           

首先,声明一个 DeathRecipient 对象。DeathRecipient 是一个接口,其内部只有一个方法 binderDied,我们需要实现这个方法,当 Binder 死亡的时候,系统会回调 binderDied 方法,然后我们就可以移除之前绑定的 binder 代理并重新绑定远程服务。

其次,在客户端绑定远程服务成功后,给 binder 设置死亡代理:

private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            mRemoteBookManager = bookManager;
            try {
                mRemoteBookManager.asBinder().linkToDeath(mDeathRecipient, );
            ... ...
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            mRemoteBookManager = null;
            Log.d(TAG, "onServiceDisconnected. tname:" + Thread.currentThread().getName());
        }
    };
           

其中 linkToDeath 的第二个参数是个标记位,我们直接设为 0 即可。经过上面两个步骤,就给我们的 Binder 设置了死亡代理,当 Binder 死亡的时候我们就可以收到通知了。另外,通过 Binder 的方法 isBinderAlive 也可以判断 Binder 是否死亡。