深入解析Android-Binder机制

431 阅读10分钟

1,概述

首先,Binder是android平台下一种IPC机制,由于android基于linux,其linux本身就拥有一些ipc方式,如socket、通道等。但是,基于性能方面,android并未采用linux的ipc,而是自己实现了一套高效的ipc方式,Binder机制的特点,首先在于使用了内存映射技术,即linux下的mmap,使得client-service间通信只需要复制一次,而socket需要复制两次。Binder机制采用CS模式,为两个进程通信搭建桥梁。

说起mmap,不得不感叹Android对Binder的创新。

(1)如果基于共享内存进行IPC,效率虽然高,可是控制难啊。你就想一想Java内存模型吧,本身线程间共享一套内存,因此衍生处synchronize、volatile等等等等一系列锁或者锁升级、原子性啊、可见性啊一系列控制问题,如果Android还在进程间这么搞,有多复杂,你就想想吧!

(2)如果基于socket通信,那你也太慢了吧!网络传输是基于socket的tcp或者udp,没办法,网络传输的终端大概率不在同一终端,这个时候它们写入写出两次是理所当然的,可是Android呢?系统进程和用户进程就在同一手机嘛,你写入写出两次在应用层尚可,可是到Framework层就太慢了,毕竟系统服务慢,用户体验可不好。

那这这这?可这可咋办啊?

Google的大神们将方案1和2进行折中,想出了内存映射这个方案!你想想吧,如果共享内存不行,维护难度太高,那我们能不能让只有两个进程共享一块内存?话说到这,你就该明白了,就是将一大块Binder管理的内存,划分一部分给两个进程使用,这下就不存在多进程控制问题了是吧,这样的BpBinderh只需要写一次,BBinder不用去复制,直接到Binder给的一把钥匙,找到那块特定的内存区域去读取数据就可以了,这不就实现了两个进程间的通信嘛。当然哈,这也就产生一个弊端,分配给两个进程的共享内存不大,也就不能传输大数据了,当然在Framework层也没多大数据要传送,只是操作系统的一些参数,就很OK。

流程图:

image.png

简单时序图:

image.png

2,分析

日常开发中,如AIDL开发,需要继承Binder类,实现自己定义的服务接口,AIDL中有几个重点方法,如asInterface。 这在客户端上可以获取Proxy类,实现跨进程通信。当绑定一个服务后(另一个进程),返回的service本质是一个BinderProxy,对应native层BpBinder。通过特定描述符获取服务对象,实现跨进程调用。Proxy在客户端直接调用业务对应代理方法,其中mRemote可以理解为native层BpBinder,实际它们本身就建立了联系。创建代理需要服务端的IBinder在绑定服务时通过AMS的publishService交给客户端拿到(这本身又是个Binder通信),详细过程可查看bindService源码。

真正的通信过程,肯定要借助native层的BpBinder与Binder驱动进行通信,即通过内存映射的方式写入内存。前面说过,我们在AIDL中拿到的mRemote是服务端BinderProxy对象,然后调用asInterface方法生成代理类,其transact方法最终调用BinderProxy的transactNative方法,才能够将数据写进Binder驱动。

native层BpBinder直接与Binder驱动通信的类是IPCThreadState,在transact方法中代码如下,

image.png

来到IPCThreadState.transact方法,可以看到,Parcel数据报文已经传到这儿了,接下来将数据通过writeTransactionData方法写入,如何waitForResponse等待结果就行。

image.png

跟进writeTransactionData方法,通过mOut这个成员,就可以实现直接与Binder设备交互,

image.png

追根问底,mOut和对应的mIn是个啥呢?我们在IPCThreadState.h找到了答案,就是个Parcel。此时的mOut只是写入数据,还未与Binder驱动通信。

image.png

再看看waitForResponse,除了意料之中的mIn成员外,talkWithDriver!

image.png

跟进talkWithDriver,发现底层使用ioctl方式与binder通信。ioctl是基于linux的一种io控制方式,第一个参数即Binder地址的文件描述法fd,第二个参数是控制协议,第三个参数与控制协议一同配合使用。

image.png

关于native层更深的实现暂时在此不展开分析,进程间通信已然可以实现,感兴趣的读者可以自学阅读android-s源码(写本文时google发布的最新版本)。

前面我们说过,BinderProxy对应native层BpBinder,如何对应?

这里,我们就来看看Binder构造方法,看,调用了一个Bnative方法,getNativeBBinderHolder,返回native层JavaBBinderHolder。

public Binder(@Nullable String descriptor) { mObject = getNativeBBinderHolder(); //将自身和返回的指针值指向的JavaBBinderHolder建立关系 NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);

    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Binder> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    mDescriptor = descriptor;
}

native层简单new了一个JavaBBinderHolder,它将native层Binder和Framework层Binder建立联系,虽然Framework层Binder是Native层镜像,但跨进程一定需要native层。JavaBBinderHolder所起的作用就是两层间传递者。

image.png

我们看下JavaBinderHolder.get方法,obj实际是Framework层Binder对象,new 了一个b,即JavaBBinder,保存在mBinder成员中,

image.png

那么,我们再看一看JavaBBinder的定义,

继承Binder,其中mObject成员指向Java层Binder对象,

image.png

那么到这,我们总结下,

Java层Binder.mObject ->(指向) native层JavaBBinderHolder

native层JavaBBinderHolder.mBinder -> native层JavaBBinder

native层JavaBBinder.-> Java层Binder

再看看JavaBBinder.onTransact方法,传入mObject对象即Java层Binder,反射调用execTransact方法,下一步不就来到服务端进程了嘛。所以,JavaBBinder起了一个传递作用。

image.png

在Java层Binder中,execTransact方法回调自身onTransact,进入服务端进程以实现对应业务啦,比如AMS的startActivityAsUser等,

private boolean execTransactInternal(int code, long dataObj, long replyObj, int flags, ... if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0) { AppOpsManager.startNotedAppOpsCollection(callingUid); try { res = onTransact(code, data, reply, flags); } finally { AppOpsManager.finishNotedAppOpsCollection(); } } else { // 回调服务端onTransact方法,进一步回调业务方法 res = onTransact(code, data, reply, flags); } } catch (RemoteException|RuntimeException e) { ... } ... return res; }

客户端如何做?前面说过,客户端拿到的是BinderProxy,保存在mRemote成员中,调用BinderProxy.transact方法,跟进transactNative方法,跟进native层BpBinder将数据写入Binder驱动。

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");

    ...

    if ((flags & FLAG_ONEWAY) == 0 && AppOpsManager.isListeningForOpNoted()) {
        flags |= FLAG_COLLECT_NOTED_APP_OPS;
    }

    try {
        // 进入底层实现,写入到BInder驱动,最后步骤是反射调用服务端execTransact方法
        return transactNative(code, data, reply, flags);
    } finally {
       ...
    }
}

可能读者要问了,Binder.java不是本身就有transact方法吗?拿来干啥?

因为asInterface如果在同一进程中调用,是不用跨进程通信的,这个时候就不会返回Proxy对象。asInterface方法内先调用queryLocalInterface查询本进程中是否存在该Binder,如果存在直接返回,这个时候mRemote保存的就是本进程Binder对象而非BinderProxy,transact就被本进程调用了。

public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) { if (mDescriptor != null && mDescriptor.equals(descriptor)) { return mOwner; } return null; } //调用这个方法的前提是同一进程 public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException { if (false) Log.v("Binder", "Transact: " + code + " to " + this); if (data != null) { data.setDataPosition(0); } boolean r = onTransact(code, data, reply, flags); if (reply != null) { reply.setDataPosition(0); } return r; } 3,应用层简单实例

通过AIDL自动生成以下代码,

/*

  • This file is auto-generated. DO NOT MODIFY. */ package com.zjw.source.aidl;

public interface IBookManager extends android.os.IInterface { // 方法接口 public java.util.List getBookList() throws android.os.RemoteException; public void addBook(Book book) throws android.os.RemoteException;

/**
 * Default implementation for IBookManager.
 * 这个接口默认实现,没啥用,当进程间无法获取Proxy时,返回一个空实现
 */
public static class Default implements IBookManager {

    @Override
    public java.util.List<Book> getBookList() throws android.os.RemoteException {
        return null;
    }

    @Override
    public void addBook(Book book) throws android.os.RemoteException {

    }

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

/**
 * Local-side IPC implementation stub class.
 * 这是具体实现的接口,本身是个Binder
 */

public static abstract class Stub extends android.os.Binder implements IBookManager {
    //Binder唯一标识,一般用当前类名表示
    private static final String DESCRIPTOR = "com.zjw.source.aidl.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.zjw.source.aidl.IBookManager interface,
     * generating a proxy if needed.
     */
    //用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程的,
    //如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.Proxy对象。
    public static IBookManager asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        //这里先在本地进程查询,如果查询到说明在同一进程下
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof IBookManager))) {
            return ((IBookManager) iin);
        }
        // 查询不到,返回代理对象
        return new Proxy(obj);
    }
    //此方法返回Binder对象本身
    @Override
    public android.os.IBinder asBinder() {
        return this;
    }

    //这个方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装好后交由次方法来处理,
    //服务端通过code可以确认客户端所请求的目标方法是什么,接着从data中取出目标方法所需要的参数,然后执行目标方法,
    //当目标方法执行完毕后,就向reply中写入返回值(如果有返回值),
    //如果此方法返回false,那么客户端的请求会失败。
    @Override
    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {

        String descriptor = DESCRIPTOR;
        switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(descriptor);
                return true;
            }
            //getBookList方法
            case TRANSACTION_getBookList: {
                data.enforceInterface(descriptor);
                java.util.List<Book> _result = this.getBookList();
                reply.writeNoException();
                reply.writeTypedList(_result);
                return true;
            }
            //addBook方法
            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;
            }

            default: {
                return super.onTransact(code, data, reply, flags);
            }
        }
    }

    //下面即是返回给客户端的Proxy对象
    private static class Proxy implements IBookManager {
        //服务端Binder
        //服务端返回时: return new Proxy(obj);
        private android.os.IBinder mRemote;
        //接口默认实现
        public static IBookManager sDefaultImpl;
        Proxy(android.os.IBinder remote) {
            //mRemote是个BinderProxy,可以去看一看Framework层源码
            mRemote = remote;
        }

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

        public String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }

        // 以下方法均是客户端调用
        @Override
        public java.util.List<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<Book> _result;

            try {
                //这里先写入Binder Token 和 方法参数(如果有)
                _data.writeInterfaceToken(DESCRIPTOR);
                //调用transact发起远程请求,同时线程挂起
                boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                if (!_status && getDefaultImpl() != null) {
                    return getDefaultImpl().getBookList();
                }
                _reply.readException();
                _result = _reply.createTypedArrayList(Book.CREATOR);

            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }

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

                boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                if (!_status && getDefaultImpl() != null) {
                    getDefaultImpl().addBook(book);
                    return;
                }
                _reply.readException();

            } finally {
                _reply.recycle();
                _data.recycle();
            }
        }
    }

    public static boolean setDefaultImpl(IBookManager impl) {
        // Only one user of this interface can use this function
        // at a time. This is a heuristic to detect if two different
        // users in the same process use this function.
        if (Proxy.sDefaultImpl != null) {
            throw new IllegalStateException("setDefaultImpl() called twice");
        }
        if (impl != null) {
            Proxy.sDefaultImpl = impl;
            return true;
        }
        return false;
    }

    public static IBookManager getDefaultImpl() {
        return Proxy.sDefaultImpl;
    }
}

}

然后,服务端可以重写Stub方法了,如下所示:

public class MyService extends Service {

public static final String TAG = "MyService";
private final List<Book> mBookList = new ArrayList<>();

//服务端实现的方法
private final IBookManager.Stub mBookManager = new IBookManager.Stub() {
    @Override
    public List<Book> getBookList() throws RemoteException {
        synchronized (this) {
            Log.d(TAG, "getBookList");
            return mBookList;
        }
    }

    @Override
    public void addBook(Book book) throws RemoteException {
        synchronized (this) {
            Log.d(TAG, "addBook: " + book.toString());
            mBookList.add(book);
        }
    }

    @Override
    public void removeLastBook() throws RemoteException {
        synchronized (this) {
            Log.d(TAG, "removeLastBook");
            if (!mBookList.isEmpty()) mBookList.remove(mBookList.size() - 1);                                                         
        }
    }
};


@Override
public IBinder onBind(Intent intent) {
    return mBookManager;
}

@Override
public void onCreate() {
    super.onCreate();
    Log.d(TAG, "onCreate: myPid->" + Process.myPid());
    mBookList.add(new Book(0, "Android开发艺术探索"));
}

}

客户端调用

//自定义Service

private inner class MyServiceConnection : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
mPing = true mBookManager = IBookManager.Stub.asInterface(service) service?.linkToDeath(MyDeathRecipient(),0) Log.d(TAG, "onServiceConnected: ") }

override fun onServiceDisconnected(name: ComponentName?) {
    Log.d(TAG, "onServiceDisconnected: 连接断开,尝试重写连接")
    mPing = false
}

}

private inner class MyDeathRecipient:IBinder.DeathRecipient{ override fun binderDied() { Log.d(TAG, "binderDied: ") mPing = false mBookManager.asBinder().unlinkToDeath(this,0) attemptConnectService() } } // 某Activity button3.setOnClickListener { if (mBookManager.asBinder().pingBinder()) { mBookManager.removeLastBook() Log.d(TAG, mBookManager.bookList.toString()) } else { Log.d(TAG, "onServiceDisconnected: 连接断开,尝试重写连接") attemptConnectService() } } button4.setOnClickListener { if (mBookManager.asBinder().pingBinder()) { mBookManager.addBook(Book(mBookManager.bookList.size, "add book from client")) Log.d(TAG, mBookManager.bookList.toString()) } else { Log.d(TAG, "onServiceDisconnected: 连接断开,尝试重写连接") attemptConnectService() } }

/*** AndroidManifest.xml ***/

日志

2021-10-13 15:22:42.853 9139-9139/com.zjw.source D/MainActivity: removeLastBook: []

2021-10-13 15:22:45.166 9139-9139/com.zjw.source D/MainActivity: add book: [Book{id=0, name='add book from client'}]

2021-10-13 15:22:45.616 9139-9139/com.zjw.source D/MainActivity: add book: [Book{id=0, name='add book from client'}, Book{id=1, name='add book from client'}]

2021-10-13 15:22:46.311 9139-9139/com.zjw.source D/MainActivity: add book: [Book{id=0, name='add book from client'}, Book{id=1, name='add book from client'}, Book{id=2, name='add book from client'}]

2021-10-13 15:22:47.290 9139-9183/com.zjw.source I/com.zjw.source: ProcessProfilingInfo new_methods=1100 is saved saved_to_disk=1 resolve_classes_delay=8000

2021-10-13 15:22:47.316 9139-9139/com.zjw.source D/MainActivity: removeLastBook: [Book{id=0, name='add book from client'}, Book{id=1, name='add book from client'}]

2021-10-13 15:22:47.834 9139-9139/com.zjw.source D/MainActivity: removeLastBook: [Book{id=0, name='add book from client'}]