×
广告

从应用层面剖析Android Binder机制

96
浪淘沙xud 595a1b60 08f6 4beb 998f 2bf55e230555
2018.11.06 16:05 字数 2295

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()出发一步一步跟进去。

技术修炼
Web note ad 1