【Android】Binder机制及其源码解读

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。

流程图:

简单时序图:

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方法中代码如下,

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

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

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

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

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

关于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所起的作用就是两层间传递者。

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

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

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

 那么到这,我们总结下,

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

native层JavaBBinderHolder.mBinder -> native层JavaBBinder

native层JavaBBinder.-> Java层Binder

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

 在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<Book> 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

<service
    android:name=".service.MyService"
    android:exported="true"
    android:process=":remote" />
***/

日志

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'}]     

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值