Android之Binder底层原理

作者: 黄俊彬 | 来源:发表于2017-04-19 08:40 被阅读908次

    什么是Binder

    Binder是Android中特有的一种跨进程通讯的方式。但我们在平时的开发过程中,可能很少用的。而Binder的整个体系结构又尤为复杂,一般很难通过网上的一两篇博客,就能把Binder吃透,我们需要通过源码及Binder的一些架构原理,来进行研究。后面的章节我们将主要通过3个部分来由浅至深来了解Binder。首先我们先看在实际的开发中怎么来实现Binder通讯,接着分析Binder框架的原理,最后结合源码进行分析。

    为什么感觉Binder很陌生?

    1. 项目业务简单,不涉及多进程通讯
    2. 涉及多进程通讯,只简单用AIDL,没深入了解

    为什么要学习Binder?

    Binder作为Android核心的跨进程通讯方式。如果我们要研究Android的源码,Binder是一道需要跨过去的坎。我们都知道系统的各种服务运行在SystemServer进程中,我们应用与系统的各种交互都涉及到跨进程通讯。

    例如最简单的启动一个Activity、启动一个Service。到例如使用系统的网络、硬件、等各种Service,其实都涉及到跨进程通讯。只是系统为我们做好了各种封装调用而已。

    所以如果你只希望一直停留在应用层的业务开发,其实你可能一直永远都不知道Binder,但是一旦你开始了解Android的源码,那么你总会与Binder相遇。

    Android为什么使用Binder作为主要进程间通讯机制?

    1. 安全性:Binder机制从协议本身就支持对通信双方做身份校检,安全性高。传统的进程通信方式对于通信双方的身份并没有做出严格的验证,只有在上层协议上进行架设;比如Socket通信ip地址是客户端手动填入的,都可以进行伪造
    2. 性能:socket作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信,Binder其实通过Binder驱动在内核区域进行了数据的传输,性能高

    如何实现一个Binder通讯?

    1. 在项目中新建一个aidl
    
    // IBookManager.aidl
    
    package com.jd.test.myapplication;
    import com.jd.test.myapplication.Book;
    // Declare any non-default types here with import statements
    
    interface IBookManager {
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
        void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
                double aDouble, String aString);
    
        List<Book> getBooks();
    
        void addBook(in Book book);
    }
    
    
    1. 创建一个在独立进程的Service
    <service android:name=".BookManagerService"
                android:process=":remote"/>
                
    
    
    public class BookManagerService extends Service {
        private CopyOnWriteArrayList<Book> mBookList=new CopyOnWriteArrayList<>();
    
        @Override
        public void onCreate() {
            super.onCreate();
            mBookList.add(new Book(1,"Android"));
            mBookList.add(new Book(2,"IOS"));
        }
    
        private Binder mBinder=new IBookManager.Stub(){
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
    
            }
    
            @Override
            public List<Book> getBooks() throws RemoteException {
                return mBookList;
            }
    
            @Override
            public void addBook(Book book) throws RemoteException {
                mBookList.add(book);
            }
        };
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
    }
    
    
    1. 另外一个进程,启用远程的Service,并调用接口方法,进行通讯
    public class MainActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Intent intent=new Intent(this,BookManagerService.class);
            bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    
        }
     private ServiceConnection mConnection=new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            IBookManager manager=IBookManager.Stub.asInterface(iBinder);
             try {
                 List<Book> books=manager.getBooks();
                 System.out.println("books:"+books);
             } catch (RemoteException e) {
                 e.printStackTrace();
             }
         }
    
         @Override
         public void onServiceDisconnected(ComponentName componentName) {
    
         }
     };
    }
    

    只能说so easy。Android提供了优秀的API,使得我们可以很方便的通过AIDL实现进程间的通讯。貌似我们实现了进程间通讯,但是连Binder的身影都没看到,这也就是上面的Binder对很多童鞋都很陌生的原因。

    Binder的原理

    我们定义了AIDI后,默认系统都会生成一个集成了IInterface的接口的类,eclipse默认是在gen目录下。


    /*
     * This file is auto-generated.  DO NOT MODIFY.
     * Original file: G:\\Source\\Demo\\MyApplication\\app\\src\\main\\aidl\\com\\jd\\test\\myapplication\\IBookManager.aidl
     */
    package com.jd.test.myapplication;
    // Declare any non-default types here with import statements
    
    public interface IBookManager extends android.os.IInterface
    {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.jd.test.myapplication.IBookManager
    {
    private static final java.lang.String DESCRIPTOR = "com.jd.test.myapplication.IBookManager";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
    this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.jd.test.myapplication.IBookManager interface,
     * generating a proxy if needed.
     */
    public static com.jd.test.myapplication.IBookManager asInterface(android.os.IBinder obj)
    {
    if ((obj==null)) {
    return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof com.jd.test.myapplication.IBookManager))) {
    return ((com.jd.test.myapplication.IBookManager)iin);
    }
    return new com.jd.test.myapplication.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
    {
    switch (code)
    {
    case INTERFACE_TRANSACTION:
    {
    reply.writeString(DESCRIPTOR);
    return true;
    }
    case TRANSACTION_basicTypes:
    {
    data.enforceInterface(DESCRIPTOR);
    int _arg0;
    _arg0 = data.readInt();
    long _arg1;
    _arg1 = data.readLong();
    boolean _arg2;
    _arg2 = (0!=data.readInt());
    float _arg3;
    _arg3 = data.readFloat();
    double _arg4;
    _arg4 = data.readDouble();
    java.lang.String _arg5;
    _arg5 = data.readString();
    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
    reply.writeNoException();
    return true;
    }
    case TRANSACTION_getBooks:
    {
    data.enforceInterface(DESCRIPTOR);
    java.util.List<com.jd.test.myapplication.Book> _result = this.getBooks();
    reply.writeNoException();
    reply.writeTypedList(_result);
    return true;
    }
    case TRANSACTION_addBook:
    {
    data.enforceInterface(DESCRIPTOR);
    com.jd.test.myapplication.Book _arg0;
    if ((0!=data.readInt())) {
    _arg0 = com.jd.test.myapplication.Book.CREATOR.createFromParcel(data);
    }
    else {
    _arg0 = null;
    }
    this.addBook(_arg0);
    reply.writeNoException();
    return true;
    }
    }
    return super.onTransact(code, data, reply, flags);
    }
    private static class Proxy implements com.jd.test.myapplication.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;
    }
    /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
    @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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.writeInt(anInt);
    _data.writeLong(aLong);
    _data.writeInt(((aBoolean)?(1):(0)));
    _data.writeFloat(aFloat);
    _data.writeDouble(aDouble);
    _data.writeString(aString);
    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
    _reply.readException();
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    }
    @Override public java.util.List<com.jd.test.myapplication.Book> getBooks() throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    java.util.List<com.jd.test.myapplication.Book> _result;
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    mRemote.transact(Stub.TRANSACTION_getBooks, _data, _reply, 0);
    _reply.readException();
    _result = _reply.createTypedArrayList(com.jd.test.myapplication.Book.CREATOR);
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    return _result;
    }
    @Override public void addBook(com.jd.test.myapplication.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();
    }
    }
    }
    static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    }
    /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
    public java.util.List<com.jd.test.myapplication.Book> getBooks() throws android.os.RemoteException;
    public void addBook(com.jd.test.myapplication.Book book) throws android.os.RemoteException;
    }
    
    

    这个类也就是我们在onServiceConnected中使用的接口。在这个IBookManager中我们有几个关键的类Stub、Proxy,也见到了久违的Binder。那么纠结Binder是怎么样来进行间通讯的呢?下面我们先通过一个示例图来简单描述一下该流程。

    IInterface结构分析

    首先变量DESCRIPTOR定义了接口和对应方法的唯一标示。因为Binder其实是一种底层的通讯方式,Google工程师将Binder包装成了一种对象的引用。所以这里的标识是告诉底层的Binder驱动,我的Binder引用标识,对应的方法标识。这样Binder驱动才能在进程间进行装换。

    Proxy:实现了IBookManager接口,这个代理类是往Binder驱动里面写数据,通过调用Binder的transact方法往Binder驱动写Parcel数据。可以理解为告诉Binder驱动我们要调用什么方法

    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
    

    Stub:继承了Binder,实现了IBookManager接口。Binder驱动调用远程方法成功后,要回调告诉执行的结果。通过回调onTransact方法。将Binder驱动中的Parcel数据转换为我们的回调数据。

    IBinder引用是什么时候注册到了Binder驱动中呢?

    我们知道Stub继承了Binder,那么当Stub实例化的时候,这个时候Binder无参构造被调用,执行了一个native 的init方法。这个时候向Binder驱动注册了IBinder的引用

    public Binder() {
            init();
    
            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());
                }
            }
        }
    
    private native final void init();
    
    

    transact方法(往Binder驱动写数据)最后调用为BinderProxy代理类的transact

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

    onTransact方法 (Binder驱动回调数据)

    根据定义的常量标识,解析Parcel数据,回调本地方法

     @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_basicTypes: {
                        data.enforceInterface(DESCRIPTOR);
                        int _arg0;
                        _arg0 = data.readInt();
                        long _arg1;
                        _arg1 = data.readLong();
                        boolean _arg2;
                        _arg2 = (0 != data.readInt());
                        float _arg3;
                        _arg3 = data.readFloat();
                        double _arg4;
                        _arg4 = data.readDouble();
                        java.lang.String _arg5;
                        _arg5 = data.readString();
                        this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                        reply.writeNoException();
                        return true;
                    }
                    case TRANSACTION_getBooks: {
                        data.enforceInterface(DESCRIPTOR);
                        java.util.List<com.jd.test.myapplication.Book> _result = this.getBooks();
                        reply.writeNoException();
                        reply.writeTypedList(_result);
                        return true;
                    }
                    case TRANSACTION_addBook: {
                        data.enforceInterface(DESCRIPTOR);
                        com.jd.test.myapplication.Book _arg0;
                        if ((0 != data.readInt())) {
                            _arg0 = com.jd.test.myapplication.Book.CREATOR.createFromParcel(data);
                        } else {
                            _arg0 = null;
                        }
                        this.addBook(_arg0);
                        reply.writeNoException();
                        return true;
                    }
                }
                return super.onTransact(code, data, reply, flags);
            }
    

    Binder应用层源码实现流程分析

    我们了解了Binder通讯的一些基础原理后,通过应用层的调用来追踪整个执行的流程。下面我们通过一个表格索引来描述Binder通讯的结构

    索引 调用的类间关系 说明
    1 Activity:bindService 执行服务的绑定
    2 Context:bindService 基类的的服务绑定抽象方法
    3 ContextImpl:bindService Context实现类的方法
    4 ContextImpl:bindServiceCommon 进行一些Intent校验等,调用ActivityManagerNative.getDefault().bindService
    5 ActivityManagerService:bindService 进行一些合法 非空的校验
    6 ActiveServices:bindServiceLocked 创建Service,对服务进行缓存记录,同时回调了connection方法
    7 ActiveServices:requestServiceBindingLocked 校验服务进程是否存在,调用ApplicationThread的scheduleBindService
    8 ApplicationThread:scheduleBindService ApplicationThread是ActivityThread的内部类,该方法发送了一个Message sendMessage(H.BIND_SERVICE, s);
    9 ActiviThread:handleBindService 校验Service是否存在,执行AMS的publishService方法
    10 ActivityManagerService:publishService 校验及调用ActiveServices的publishServiceLocked方法
    11 ActiveServices:publishServiceLocked 执行Binder的restoreCallingIdentity方法

    总结

    1. 本文对Bidner做了一些整体的介绍,主要是基于应用层的流程进行分析,如果要彻底搞清楚Bidner,可能还需阅读Binder驱动的源码及Bidner的协议等
    2. Binder在Android体系中,有着非常重要的地位,是核心的IPC方式。如果希望学习Android源码,Binder是一道需要越过去的坎。

    相关文章

      网友评论

        本文标题:Android之Binder底层原理

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