从应用层面剖析Android Binder机制

作者: 浪淘沙xud | 来源:发表于2018-11-06 16:05 被阅读10次

    Binder是Android提供的一种进程间通信机制,它是整个Android系统的核心,Android能进行如此丰富自由的多进程开发也多基于Binder机制。当前Android各路大神已经针对Binder机制写了诸多精彩的论述(话说写这篇文章我也是瑟瑟发抖啊),这些文章把Binder底层通信原理讲述的非常清楚了,本文我将从应用层面来剖析Binder机制,主要通过以下两个点来讲述:其一,如何手写AIDL实现跨进程通信;其二,通过Activity.bindService()过程来领略Android Framework层对Binder跨进程的灵活应用。

    Binder通信原理

    Binder 是一种进程间通信机制,基于开源的 OpenBinder 实现。理解Binder通信原理主要是要回答以下几个问题:

    1. 进程是什么?

    2. 什么是用户态、内核态,什么是用户空间、内核空间?

    3. 现有的进程IPC机制有哪些?共享内存、Socket、管道、消息队列、Binder,它们实现的基本原理和优劣有哪些?

    4. Linux系统下的动态内核可加载模块&&内存映射 与 Binder驱动的关系

    5. Binder的一次通信过程是怎样的

    针对以上几个问题,现有的几篇文章讲述的非常清楚,读者可以带着这些问题去看这些文章:

    手写AIDL实现跨进程通信

    Binder通信关键对象

    在手写AIDL之前,先来了解Android Binder通信的关键对象, 类中的关键点我都写在了注释上面。

    1. 接口 IInterface
    
    public interface IInterface{
    
      public IBinder asBinder();
    
    }
    
    

    自定义的服务要跨进程通信,必须继承IInterface,从其定义来看表示该服务要可以被转化成IBinder

    1. 接口 IBinder
    public interface IBinder {
        
        // 1. IBinder自定义的transaction code
        int INTERFACE_TRANSACTION   = ('_'<<24)|('N'<<16)|('T'<<8)|'F';
        int DUMP_TRANSACTION        = ('_'<<24)|('D'<<16)|('M'<<8)|'P';
        int SHELL_COMMAND_TRANSACTION = ('_'<<24)|('C'<<16)|('M'<<8)|'D';
        ...
    
        // 2. 核心的transact方法,参数data为参数序列化,参数reply为调用结果序列化,Binder底层驱动负责跨进程调用,参数中有个code,这个code很关键,用以server端区别不同的方法调用的
        public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException;
    
        ...
    
        // 3. Binder连接与否的状态监听,比如Remote进程被杀掉了,通过这个监听可以获取到这一变化
        public void linkToDeath(@NonNull DeathRecipient recipient, int flags)
            throws RemoteException;
    
        public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags);
    }
    
    1. IBinder实现类Binder
    public class Binder implements IBinder {
        
        // 1. 该方法的核心就是调用onTransact()方法,onTransact() 根据不同的code来具体执行相应的方法调用
        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;
        }
    
        // 这是Binder自带的onTransact()实现,处理了自定义的code,当前code处理完要return true 中断执行。自定义服务关键也就是要重载这个函数,去handle自定义服务的code
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
                int flags) throws RemoteException {
            if (code == INTERFACE_TRANSACTION) {
                reply.writeString(getInterfaceDescriptor());
                return true;
            } else if (code == DUMP_TRANSACTION) {
                ...
                return true;
            } else if (code == SHELL_COMMAND_TRANSACTION) {
                ...
                return true;
            }
            return false;
        }
    }
    
    1. IBinder的代理实现类BinderProxy
    final class BinderProxy implements IBinder {
    
        // transact方法做了很多校验,然后调用transactNative进行跨进程调用
        public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
    
            if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) {
                // For now, avoid spamming the log by disabling after we've logged
                // about this interface at least once
                mWarnOnBlocking = false;
                Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY",
                        new Throwable());
            }
    
            final boolean tracingEnabled = Binder.isTracingEnabled();
            if (tracingEnabled) {
                final Throwable tr = new Throwable();
                Binder.getTransactionTracker().addTrace(tr);
                StackTraceElement stackTraceElement = tr.getStackTrace()[1];
                Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
                        stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
            }
            try {
                return transactNative(code, data, reply, flags);
            } finally {
                if (tracingEnabled) {
                    Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
                }
            }
        }
    
        // 这是跨进程调用参数传递的关键
        public native boolean transactNative(int code, Parcel data, Parcel reply,
                int flags) throws RemoteException;
    }
    

    现在,对于Binder跨进程通信过程,可以这么讲:

    1. 定义一个服务IService,使之继承IInterface,表示可跨进程调用;

    2. Server进程端实现IService,并继承Binder,使之可以处理Client进程端的调用请求,具体的说,就是重载onTransact(), 根据code来处理IService中的不同方法;

    3. Client进程端持有BinderProxy,当要调用IService中的方法时,通过BinderProxy.transact()方法调用,经过Binder驱动跨进程传递之后,最终找到Server端onTransact()执行;

    4. Client端和Server协定好transaction code分别代表IService中的什么方法

    具体的过程图示如下:


    Binder通信过程.png

    手写AIDL

    以IBookService为例,我在这里详细描述一下手写AIDL的步骤

    1、 定义接口IBookService,继承IInterface

    
    public interface IBookService extends IInterface {
    
     List<Book> getBooks() throws RemoteException;
    
     void addBook(Book book) throws RemoteException;
    
    }
    
    

    2、 实现IBookService的本地实现类Stub

    这个类有两个方法很关键:

    其一,IBookService asInterface(IBinder binder),这个方法被Client端用来获取具体的Binder实例,如果是同一进程,返回Binder实例,如果是不同进程,返回BinderProxy;

    其二:boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags),这个方法负责具体的执行过程,如果是跨进程调用,最终就会调用这个方法,该方法根据code来区分不同的调用

    public abstract class Stub extends Binder implements IBookService {
    
        private static final String DESCRIPTOR = "com.xud.ipc.server.IBookService";
    
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
    
        public static IBookService asInterface(IBinder binder) {
            if (binder == null)
                return null;
            IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
            if (iin != null && iin instanceof IBookService)
                return (IBookService) iin;
            return new Proxy(binder);
        }
    
        @Override
        public IBinder asBinder() {
            return this;
        }
    
        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
            switch (code) {
    
                case INTERFACE_TRANSACTION:
                    reply.writeString(DESCRIPTOR);
                    return true;
    
                case TRANSAVTION_getBooks:
                    data.enforceInterface(DESCRIPTOR);
                    List<Book> result = this.getBooks();
                    reply.writeNoException();
                    reply.writeTypedList(result);
                    return true;
    
                case TRANSAVTION_addBook:
                    data.enforceInterface(DESCRIPTOR);
                    Book arg0 = null;
                    if (data.readInt() != 0) {
                        arg0 = Book.CREATOR.createFromParcel(data);
                    }
                    this.addBook(arg0);
                    reply.writeNoException();
                    return true;
    
            }
            return super.onTransact(code, data, reply, flags);
        }
    
        public static final int TRANSAVTION_getBooks = IBinder.FIRST_CALL_TRANSACTION;
        public static final int TRANSAVTION_addBook = IBinder.FIRST_CALL_TRANSACTION + 1;
    }
    

    3、实现IBookService的代理类Proxy

    Proxy是一个典型的静态代理模式,(关于代理模式读者可以细读相关文章),Proxy并没有实现IBookService中的方法,而是通过remote将方法请求传递到Server进程,也即是上面的Stub类处理,而remote是一个BinderProxy

    public class Proxy implements IBookService {
    
        private static final String DESCRIPTOR = "com.xud.ipc.server.IBookService";
    
        private IBinder remote;
    
        public Proxy(IBinder remote) {
    
            this.remote = remote;
        }
    
        public String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }
    
        @Override
        public IBinder asBinder() {
            return remote;
        }
    
        @Override
        public List<Book> getBooks() throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel replay = Parcel.obtain();
            List<Book> result;
    
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                remote.transact(Stub.TRANSAVTION_getBooks, data, replay, 0);
                replay.readException();
                result = replay.createTypedArrayList(Book.CREATOR);
            } finally {
                replay.recycle();
                data.recycle();
            }
            return result;
        }
    
        @Override
        public void addBook(Book book) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel replay = Parcel.obtain();
    
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                if (book != null) {
                    data.writeInt(1);
                    book.writeToParcel(data, 0);
                } else {
                    data.writeInt(0);
                }
                remote.transact(Stub.TRANSAVTION_addBook, data, replay, 0);
                replay.readException();
            } finally {
                replay.recycle();
                data.recycle();
            }
        }
    }
    

    4. 在Server进程端创建BookService,提供IBookService的Binder实例

    public class BookService extends Service {
    
        ...
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return bookManager;
        }
    
        private final Stub bookManager = new Stub() {
            @Override
            public List<Book> getBooks() throws RemoteException {
                synchronized (this) {
                    //todo
                }
            }
    
            @Override
            public void addBook(Book book) throws RemoteException {
                synchronized (this) {
                    // todo
                }
            }
        };
    }
    

    调用的步骤就非常简单,就是我们熟悉的bindService()

    Intent intent = new Intent(IpcActivity.this, BookService.class);
            bindService(intent, new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    // 若是同一个进程,service就是Binder实例;若是不同进程,service就是BinderProxy
                    IBookService bookService = Stub.asInterface(service);
                    if (bookService != null) {
                        try {
                            List<Book> books = bookService.getBooks();
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                }
    
                @Override
                public void onServiceDisconnected(ComponentName name) {
    
                }
            }, Context.BIND_AUTO_CREATE);
    

    Activity.bindService()执行过程

    从上面的过程中可以发现,针对一个跨进程接口IInterface,它具备两个实现,一个是本地实现Stub,一个是代理实现Proxy。如果是在同一进程,你就是通过Stub实例来进行方法调用;如果是跨进程,则是通过Proxy来方法调用。

    明白这点之后,再看Android Framework层源码,就会清晰很多了。在Framework层,大量的使用到了Binder IPC机制,那些核心的类,如: ActivityManagerService、PackageManagerService、WindowManagerService、ServiceManager、ApplicationThread等都是Binder实现的,它们都具备 形如IActivityManager、ActivityManagerNative 或者 IActivityManager.Stub (这两个实质都是Stub, Framework层不同的命名而已)、ActivityManagerProxy这样的三种类。

    所以解决了跨进程通信的问题之后,Framework的核心处理就变成了如何持有和保证Binder?如果去管理各种组件?如果去实现组件之间的通信?等等这样的问题了。在阅读源码的时候我们也要跳出繁琐的Binder切换,去把握真正的核心。

    写到这里,我也有个疑问:相比于iOS,Android的卡顿和响应速度一直是被诟病的,iOS只有独立进程,而Android则支持多进程,为了提供这样的灵活性,Framework层花了很大的力气去处理维护和支持跨进程通信,是否这些处理导致了Android底层处理过于繁琐并影响了速度呢?欢迎有相关研究的读者在评论中留言讨论

    接下来要说到本节的重点,Activity.bindService()执行过程。针对这个问题,老罗在文章Android应用程序绑定服务(bindService)的过程源代码分析写的非常详细,总共有30个步骤,非常之详细。我在这里是主要是跳出这个繁琐的过程,把更实质的内容呈现出来。下面我们一步一步来看:

    1. Activity.bindService(Intent service, ServiceConnection conn, int flags), ServiceConnection是一个回调,在Service bind成功之后返回Binder对象,该Binder实例由Service中的 IBinder onBind(Intent intent) 方法创建

    2. ContextImpl.boolean bindService(Intent service, ServiceConnection conn, int flags), 该方法进一步调用bindServiceCommon(), 在该方法中,首先从ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices 内部缓存对象中获取IServiceConnection(这是我们遇到的第一个Binder对象)

    public interface IServiceConnection extends android.os.IInterface {
        public void connected(android.content.ComponentName name, android.os.IBinder service) throws android.os.RemoteException;
    }
    

    接着调用:

    ActivityManagerNative.getDefault().bindService(
        mMainThread.getApplicationThread(), getActivityToken(), service,
        service.resolveTypeIfNeeded(getContentResolver()),
        sd, flags, getOpPackageName(), user.getIdentifier());
    

    3. 这里我们遇到了第二个Binder——IActivityManager, 如果不是同一个进程,会由ActivityManagerProxy处理,如果是同一个进程,则由ActivityManagerNative,代码如下,

    public abstract class ActivityManagerNative extends Binder implements IActivityManager{
    
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)throws RemoteException {
    
            ...
            case BIND_SERVICE_TRANSACTION: {
                data.enforceInterface(IActivityManager.descriptor);
                IBinder b = data.readStrongBinder();
                IApplicationThread app = ApplicationThreadNative.asInterface(b);
                IBinder token = data.readStrongBinder();
                Intent service = Intent.CREATOR.createFromParcel(data);
                String resolvedType = data.readString();
                b = data.readStrongBinder();
                int fl = data.readInt();
                String callingPackage = data.readString();
                int userId = data.readInt();
                IServiceConnection conn = IServiceConnection.Stub.asInterface(b);
                int res = bindService(app, token, service, resolvedType, conn, fl,
                        callingPackage, userId);
                reply.writeNoException();
                reply.writeInt(res);
                return true;
            }
            ...
        }
    }
    

    bindService(app, token, service, resolvedType, conn, fl,

    callingPackage, userId); 实现在ActivityManagerService中

    4. 这里出现了第三个Binder——IApplicationThread,对于这个类的作用,可以总结为: ActivityThread之间跨进程通信的Binder接口。从它提供的方法就可以看出来, 都是形如:scheduleLaunchActivity、scheduleDestroyActivity、scheduleBindService、scheduleUnbindService等。这里调用了ActivityManagerService.bringUpServiceLocked,这个方法进程是否启动做了相关的处理,没有启动会启动进程

    5. 接下来调用:ActivityManagerService.realStartServiceLocked,这个方法里面调用了, 这里我们也可以看到IApplicationThread的作用了

    app.thread.scheduleCreateService(r, r.serviceInfo,
    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),app.repProcState);
    

    6. 下面我们可以直接跳到ApplicationThread中的scheduleCreateService方法,在它下面我们看到了scheduleBindService方法,这两个方法最终是触发Service中的onCreate() onBind()方法。需要注意的是ApplicationThread只是Schedule,具体的执行是交给ActivityThread来做的

    7. ActivityThread: Android运行的核心,Android MainThread执行的地方,也是Android四大组件实际管理和执行的地方。这个方法中有一个Handler类,它是四大组件生命周期的总调度

    private class H extends Handler {
    
        public void handleMessage(Message msg) {
    
            ...
            case CREATE_SERVICE:
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
                        handleCreateService((CreateServiceData)msg.obj);
                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                        break;
                    case BIND_SERVICE:
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
                        handleBindService((BindServiceData)msg.obj);
                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                        break;
                    case UNBIND_SERVICE:
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind");
                        handleUnbindService((BindServiceData)msg.obj);
                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                        break;
            ...
        }
    }
    

    在上述的handle方法中,可以看到Service.onCreate()、Service.onBind()方法被最终调用。

    8. 这里直接跳到handleBindService((BindServiceData)msg.obj)方法,其详细过程如下,ActivityManagerNative.getDefault().publishService(data.token, data.intent, binder); 这个地方很好理解,调用Service.onBind()方法获取Binder之后,通过Binder IPC机制通知调用方。其中ActivityManagerNative.getDefault()是获取到相应的Binder,同理,若是同进程取得Binder实例,若是不同进程取得BinderProxy。

    private void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        if (DEBUG_SERVICE)
            Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                try {
                    if (!data.rebind) {
                        IBinder binder = s.onBind(data.intent);
                        ActivityManagerNative.getDefault().publishService(
                                data.token, data.intent, binder);
                    } else {
                        s.onRebind(data.intent);
                        ActivityManagerNative.getDefault().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            } catch (Exception e) {
                if (!mInstrumentation.onException(s, e)) {
                    throw new RuntimeException(
                            "Unable to bind to service " + s
                            + " with " + data.intent + ": " + e.toString(), e);
                }
            }
        }
    }
    

    9. 下面我们再看具体的publishService()方法,可以追踪到ActiveService.publishServiceLocked(ServiceRecord r, Intent intent, IBinder service)方法,这个方法通过c.conn.connected(r.name, service);发出最终的回调,c.conn就是第二步取到的IServiceConnection

    10. 在第二步我们知道IServiceConnection是LoadedApk.ServiceDispatcher.InnerConnection, c.conn.connected(r.name, service)最终回调的connected方法如下:

    private static class InnerConnection extends IServiceConnection.Stub {
        final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
        InnerConnection(LoadedApk.ServiceDispatcher sd) {
            mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
        }
    
        public void connected(ComponentName name, IBinder service) throws RemoteException {
            LoadedApk.ServiceDispatcher sd = mDispatcher.get();
            if (sd != null) {
                sd.connected(name, service);
            }
        }
    }
    
    public void connected(ComponentName name, IBinder service) {
        if (mActivityThread != null) {
            mActivityThread.post(new RunConnection(name, service, 0));
        } else {
            doConnected(name, service);
        }
    }
    
    public void doConnected(ComponentName name, IBinder service) {
        ...
        if (old != null) {
            mConnection.onServiceDisconnected(name);
        }
        // If there is a new service, it is now connected.
        if (service != null) {
            mConnection.onServiceConnected(name, service);
        }
        ...
    }
    

    这里我们就看到了Activity.bindService(Intent service, ServiceConnection conn, int flags)中的ServiceConnection是什么时候发出回调的了

    至此,整个Activity.bindService()执行过程就追踪完毕了,这里面对Binder IPC 的调用还望读者细细体会。

    在读源码的过程中,有兴趣的同学还可以去研究一下Framework层如何cache binder的,可以从ActivityManagerNative.getDefault()出发一步一步跟进去。

    相关文章

      网友评论

        本文标题:从应用层面剖析Android Binder机制

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