Binder是Android提供的一种进程间通信机制,它是整个Android系统的核心,Android能进行如此丰富自由的多进程开发也多基于Binder机制。当前Android各路大神已经针对Binder机制写了诸多精彩的论述(话说写这篇文章我也是瑟瑟发抖啊),这些文章把Binder底层通信原理讲述的非常清楚了,本文我将从应用层面来剖析Binder机制,主要通过以下两个点来讲述:其一,如何手写AIDL实现跨进程通信;其二,通过Activity.bindService()过程来领略Android Framework层对Binder跨进程的灵活应用。
Binder通信原理
Binder 是一种进程间通信机制,基于开源的 OpenBinder 实现。理解Binder通信原理主要是要回答以下几个问题:
进程是什么?
什么是用户态、内核态,什么是用户空间、内核空间?
现有的进程IPC机制有哪些?共享内存、Socket、管道、消息队列、Binder,它们实现的基本原理和优劣有哪些?
Linux系统下的动态内核可加载模块&&内存映射 与 Binder驱动的关系
Binder的一次通信过程是怎样的
针对以上几个问题,现有的几篇文章讲述的非常清楚,读者可以带着这些问题去看这些文章:
Binder学习指南
写给 Android 应用工程师的 Binder 原理剖析
手写AIDL实现跨进程通信
Binder通信关键对象
在手写AIDL之前,先来了解Android Binder通信的关键对象, 类中的关键点我都写在了注释上面。
- 接口 IInterface
public interface IInterface{
public IBinder asBinder();
}
自定义的服务要跨进程通信,必须继承IInterface,从其定义来看表示该服务要可以被转化成IBinder
- 接口 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);
}
- 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;
}
}
- 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中的什么方法
具体的过程图示如下:
手写AIDL
以IBookService为例,我在这里详细描述一下手写AIDL的步骤
1、 定义接口IBookService,继承IInterface
public interface IBookService extends IInterface {
List 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 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 getBooks() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
List 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 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 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
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 mDispatcher;
InnerConnection(LoadedApk.ServiceDispatcher sd) {
mDispatcher = new WeakReference(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()出发一步一步跟进去。