vlambda博客
学习文章列表

Android binder机制源码解析

IBinder

首先我们来看一下Binder的声明:

public class Binder implements IBinder {...}

哟,那么IBinder又是什么呢?

public interface IBinder {  int FIRST_CALL_TRANSACTION = 0x00000001; int LAST_CALL_TRANSACTION = 0x00ffffff; int PING_TRANSACTION = ('_'<<24)|('P'<<16)|('N'<<8)|'G'; int DUMP_TRANSACTION = ('_'<<24)|('D'<<16)|('M'<<8)|'P'; int SHELL_COMMAND_TRANSACTION = ('_'<<24)|('C'<<16)|('M'<<8)|'D'; int INTERFACE_TRANSACTION = ('_'<<24)|('N'<<16)|('T'<<8)|'F'; int TWEET_TRANSACTION = ('_'<<24)|('T'<<16)|('W'<<8)|'T'; int LIKE_TRANSACTION = ('_'<<24)|('L'<<16)|('I'<<8)|'K'; int SYSPROPS_TRANSACTION = ('_'<<24)|('S'<<16)|('P'<<8)|'R'; int FLAG_ONEWAY = 0x00000001;  public static final int MAX_IPC_SIZE = 64 * 1024; public @Nullable String getInterfaceDescriptor() throws RemoteException; public boolean pingBinder(); public boolean isBinderAlive(); public @Nullable IInterface queryLocalInterface(@NonNull String descriptor); public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException; public void dumpAsync(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException; public void shellCommand(@Nullable FileDescriptor in,  @Nullable FileDescriptor out, @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback shellCallback, @NonNull ResultReceiver resultReceiver) throws RemoteException; public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException; public interface DeathRecipient { public void binderDied(); } public void linkToDeath(@NonNull DeathRecipient recipient, int flags) throws RemoteException; public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags);}

哦,原来IBinder 是一个定义了跨进程传输能力的接口。只要实现了这个接口,就能将这个对象进行跨进程传递。


这些方法中重点关注 transact(),Binder里面与它对应的是 Binder.onTransact()


Binder

Binder是官方提供的实现了IBinder接口的操作,它是 Android IPC 的基础,平常接触到的各种 Manager(ActivityManager, ServiceManager 等),以及绑定 Service 时都在使用它进行跨进程操作。


下面介绍几个关键方法:

transact()

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驱动,发起IPC请求。

onTransact()

protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException { if (code == INTERFACE_TRANSACTION) { ...... } else if (code == DUMP_TRANSACTION) { ...... } else if (code == SHELL_COMMAND_TRANSACTION) { ...... } return false;}

根据 code 对传入的参数 data 做相应的处理,然后写入 reply,这样就能返回操作后的数据。

attachInterface()

public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) { mOwner = owner; mDescriptor = descriptor;}

这个方法的作用是将一个描述符、特定的 IInterface 与当前 Binder 绑定起来,这样后续调用 queryLocalInterface 就可以拿到这个 IInterface,那 IInterface 又是什么呢?


public interface IInterface{ public IBinder asBinder();}

IInterface 里只定义了一个 asBinder() 方法,这个方法可以返回当前接口关联的 Binder 对象。

 

Binder通信机制

上面只是简单介绍了Binder类相关的信息,具体的详细信息可以去查看源码,但这只是Binder通信机制的基础。


要理解Binder通信机制,首先我们要先来了解一下Android的内存空间:

用户空间:用户空间的进程是相互独立的,彼此之间不能共享内核空间:内核空间的内存是可以共享的,其大小可以通过参数来配置Android进程间通信其实质就是利用内核空间共享来完成的。

下面我们来看Binder进程间通信的四个重要角色:

Android binder机制源码解析

Client:用户端进程


Server:服务端进程


Service Manager:运行在用户空间,它负责管理 Service 注册与查询等操作


Binder驱动:负责进程之间Binder通信的建立,进程间通信的核心



下面我们来总结一下Binder机制的整个运行过程

1.ServiceManager 初始化


2.Server 向 ServiceManager 注册自己


3.Client 获取远程服务

    1、Client 向 Server 发起 IPC 请求


    2、Binder 驱动将该请求转发给 ServiceManager 进程,


    3、ServiceManager 查找到 对应的 Server 的 Binder 引用后通过 Binder 驱动反馈给 Client(并不是实际真实的远程Binder对象,而是Binder驱动里的一个映射)


    4、Client 收到 Server 对应的 Binder 引用后,会创建一个 Server 对应的远程服务(即 Server 在当前进程的代理)


4.Client 通过代理调用 Server

  • Client 会先将请求数据从用户空间拷贝到内核空间,然后调用transact去连接Binder驱动

  • 接着Binder驱动会去连接远程进程,并通知远程进程执行onTransact()函数

5.Server 返回结果

    1.执行完成后将结果写入内核空间


    2.Binder驱动再唤醒客户端线程获取数据


注意:这个过程中客户端当前线程会被挂起!因此,如果远程进程是执行长时间的运算,请不要使用主线程去调用远程函数,以防止ANR。


示例分析

上面讲了一些理论上的Binder进程间通信的知识,可以清晰的看出Binder采用的是C/S架构的模式来进行进程间通信的,而其核心点也就是内核空间的Binder驱动。下面呢,我们会通过一个具体的实例从源码的角度来解析Binder的进程间通信。

首先我们这里给出一副价值200万的图(一点也不夸张哈!)

Android binder机制源码解析

1.AIDL(Android Interface Definition Language),即Android接口定义语言。通俗来讲就是定义一个大家都懂的语言,这样进程间交流才不会有障碍。


2.Stub:接收底层C/C++Binder引用的回调。


     function:就是我们定义的一些接口方法。

      

     Proxy:用来访问底层的Binder驱动。


3.本地服务:就是在我们当前进程里的服务(后面会有具体的涉及)。


4.Binder驱动:这里看图就行了,不做过多解释。


OK!价值200万的图大家赶紧收好。接下来我们就具体来分析源码。

 

一、AIDL创建

1.新建com.wf.testaidl.bean.Person类,并实现Parcelable

public class Person implements Parcelable {...}


2.在main下面新建aidl文件夹并创建aidl文件(注意:包名和类名必须相同

3.编写aidl接口

// Person.aidlpackage com.wf.testaidl.bean; // Declare any non-default types here with import statementsparcelable Person;


Preson.aidl做一下映射避免下面文件编译的时候报错。

// PersonAidl.aidlpackage com.wf.testaidl; // Declare any non-default types here with import statementsimport com.wf.testaidl.bean.Person; interface PersonAidl { /** * 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);*/  void addPerson(in Person person); List<Person> getPersonList();}

这个文件就具体定义了我们需要的接口。

4.Make Project

编译成功后会生成

app\build\generated\source\aidl\debug\com\wf\testaidl\PersonAidl.java

文件。

 

二、解析PersonAidl.java源码

package com.wf.testaidl;public interface PersonAidl extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.wf.testaidl.PersonAidl { ... private static class Proxy implements com.wf.testaidl.PersonAidl {...} ... }  /** * 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);*/ public void addPerson(com.wf.testaidl.bean.Person person) throws android.os.RemoteException; public java.util.List<com.wf.testaidl.bean.Person> getPersonList() throws android.os.RemoteException;}


是不是很熟悉,没错就是我们上面那幅200万的图。这里可以很清晰的看出PersonAidl里面包含了我们定义的接口方法和一个静态内部抽象类Stub,这个Stub继承了Binder并且实现了PersonAidl接口,这个Stub里面又定义了一个Proxy同样实现了PersonAidl接口。


Stub

ok!接下来我们来看看这个神奇Stub。

 /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.wf.testaidl.PersonAidl { private static final java.lang.String DESCRIPTOR = "com.wf.testaidl.PersonAidl";  public Stub() {...}  /** * Cast an IBinder object into an com.wf.testaidl.PersonAidl interface, * generating a proxy if needed. */ public static com.wf.testaidl.PersonAidl asInterface(android.os.IBinder obj) {...}  @Override public android.os.IBinder asBinder() {...}  @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {...}  private static class Proxy implements com.wf.testaidl.PersonAidl {...}  static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); }


上面就是这个Stub的整个的一个结构,这里有两个方法名映射的int常量,这个后面我们会用到。

下面我们就来一步一步的分析它里面的代码。

1.DESCRIPTOR

private static final java.lang.String DESCRIPTOR = "com.wf.testaidl.PersonAidl";


这个很简单吧,就是用一个静态常量来存储了我们AIDL的类名,有什么用呢?后面你就知道了。

2.Stub() 构造函数

public Stub() { this.attachInterface(this, DESCRIPTOR);}


构造函数里面就调了一个方法,而这个attachInterface自己没有,那么是谁的呢?没错,是它爹Binder的。

private IInterface mOwner;private String mDescriptor; public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) { mOwner = owner; mDescriptor = descriptor;}


那么,这个方法干了一个什么事儿呢?它就是把自己和这个DESCRIPTOR保存在了当前这个类里,做了一个关联关系,那么有什么用呢?我们继续往后面看。


3.asInterface()

public static com.wf.testaidl.PersonAidl asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.wf.testaidl.PersonAidl))) { return ((com.wf.testaidl.PersonAidl) iin); } return new com.wf.testaidl.PersonAidl.Stub.Proxy(obj);}


这个方法主要就干了一个事儿,那就是当发起IPC请求时把我们从Binder驱动拿到的这个IBinder描述转换成一个具体的AIDL接口对象,后面才能调用相应的接口方法。这里面首先调用了一个queryLocalInterface,我们来看看为什么要先调用这个方法呢?

public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) { if (mDescriptor.equals(descriptor)) { return mOwner; } return null;}


这里可以看出它是拿这个descriptor去判断是不是调用的自己,是就直接返回自己(明白了上面那个attachInterface的作用了吧)。这又是为什么呢?这里就有一个要注意的点了,上面我们给出的那个200万的图是不是有一个本地服务,那么为什么会有一个本地服务呢?这里我就要反问一个问题了:是不是我们需要访问的服务一定是在不同的进程呢?答案是不是的,我们也有可能会访问自己的服务,对吧!好了,那么我想你应该明白了这里为什么会有一个queryLocalInterface了吧。


好了,我们接着往下看,如果我们访问的不是自己就需要拿这个IBinder描述去创建一个远程的AIDL接口对象。


4.onTransact()

@Overridepublic 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_addPerson: { data.enforceInterface(DESCRIPTOR); com.wf.testaidl.bean.Person _arg0; if ((0 != data.readInt())) { _arg0 = com.wf.testaidl.bean.Person.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addPerson(_arg0); reply.writeNoException(); return true; } case TRANSACTION_getPersonList: { data.enforceInterface(DESCRIPTOR); java.util.List<com.wf.testaidl.bean.Person> _result = this.getPersonList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } } return super.onTransact(code, data, reply, flags); }

这个方法就是来处理我们的IPC请求的,根据不同的code值最终会调到它的实现类的对应的接口方法,然后将处理后的结果写入replay返回给客户端。这里面的代码都很简单,就不一一分析了,相信大家自己也能看明白。


5.class Proxy这个我们单独放到下一节来讲。


6.此刻我想你一定有一个问题:服务不是要注册到ServiceManager嘛,上面没有哪里讲到注册了啊?其实它在我们上面讲的构造函数里面就已经完成了注册,无参构造函数的初始化首先会调用父类的无参构造函数,那我们就来看看它的父类Binder的无参构造函数:

public Binder() { init(); ...} private native final void init();


从上面我们可以看出它调用了一个native方法,就是在这里进行注册的。那么你又会问了:这里没有传任何数据啊?其实这里native方法它会隐式的把this传过去,那我们之定义的DESCRIPTOR就注册过去了。


到这里我们的Stub服务端就基本讲完了。


Proxy
上面我们剖析了一下Stub的源码,理解到了Stub其实是作为一个服务端注册到ServiceManager来响应我们的请求的。接下来我们就来看我么是怎么样发起请求的呢?首先我们来看一下这个Proxy是什么。


private static class Proxy implements com.wf.testaidl.PersonAidl { 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; }  @Override public void addPerson(com.wf.testaidl.bean.Person person) throws android.os.RemoteException {...}  @Override public java.util.List<com.wf.testaidl.bean.Person> getPersonList() throws android.os.RemoteException { ...}}


Proxy作为一个Stub的静态内部类主要用来发起跨进程的请求,那一个进程为什么既有Stub又有Proxy呢?因为一个进程它既可以是客户端也可以是服务端。好了,接下来我们一步步分析Proxy的源码,看看它是怎么工作的。


1.构造函数

Proxy(android.os.IBinder remote) { mRemote = remote;}


初始化的时候传入一需要访问的进程的IBinder描述,也就是我们发起IPC请求的时候从ServiceManager里面查询寻到的IBinder,这样我们才知道是要去访问哪个进程。


2.实现接口方法addPerson()和getPersonList()

@Overridepublic void addPerson(com.wf.testaidl.bean.Person person) 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 ((person != null)) { _data.writeInt(1); person.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); }} @Overridepublic java.util.List<com.wf.testaidl.bean.Person> getPersonList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.wf.testaidl.bean.Person> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.wf.testaidl.bean.Person.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result;}


首先初始化两个Parcel对象,都是通过Parcel.obtain()去Parcel池(队列)里面拿的(节约内存)。然后将我们要访问的进程(DESCRIPTOR)和传递的参数写入Parcel。接下来就调用IBinder引用的transact()去连接 Binder驱动。

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


这里有一个很有意思也是最简单的做法,它把要访问的方法定义成了一个int类型的常量TRANSACTION_addPerson和TRANSACTION_getPersonList,这两个常量是定义在它外部类stub里面的,这样IBinder引用拿到后就知道是要去调用哪个方法了。


接下来我们看看这个transact()做了些什么事情?

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()方法。到这里我们的整个请求就算完成了。


小结

1.一个进程既可以是服务端Stub,也可以是客户端Proxy。


2.一个IPC请求发起时,首先会调用Proxy去连接Binder驱动,然后Binder驱动再去连接Stub


3.要实现跨进程通信,两个进程必须要有相同的AIDL接口

 

三、调用流程分析


1.ServiceManager的注册和获取


上面我们讲到了Binder进程间通信时所有的Server都时由ServiceManager来管理的,那么你可能会问了ServiceManager也是一个独立进程,那么它是什么时候启动?又是什么时候注册的呢?


系统启动的时候会去执行\system\core\rootdir\init.rc,在这里面就启动了ServiceManager,启动之后会去执行service_manager.c,那这里面干了什么呢?

int main(int argc, char **argv){ struct binder_state *bs; void *svcmgr = BINDER_SERVICE_MANAGER;  bs = binder_open(128*1024);  if (binder_become_context_manager(bs)) { ALOGE("cannot become context manager (%s)\n", strerror(errno)); return -1; }  svcmgr_handle = svcmgr; binder_loop(bs, svcmgr_handler); return 0;}

打开bind驱动,并且分配128K大小,紧接着把自己注册为Service 大管家,然后就开启loop来读取Binder驱动的消息处理各种服务请求。

2.客户端发起IPC请求

private PersonAidl mPersonAidl;//绑定服务Intent intent = new Intent(this, AidlService.class);bindService(intent, new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mPersonAidl = PersonAidl.Stub.asInterface(service); }  @Override public void onServiceDisconnected(ComponentName name) { mPersonAidl = null; }}, BIND_AUTO_CREATE);//请求接口mPersonAidl.addPerson(person);List<Person> personList = mPersonAidl.getPersonList();


上面我们是通过显示意图来绑定的,因为我们是写在一起然后配置在不同进程的,当然你也可以用隐式意图来绑定服务,那么接下来我们就来看看bindService做了什么?


@Overridepublic boolean bindService(Intent service, ServiceConnection conn, int flags) { return mBase.bindService(service, conn, flags);}


那我们就来看看mBase.bindService又是干了什么?

public abstract boolean bindService(@RequiresPermission Intent service, @NonNull ServiceConnection conn, @BindServiceFlags int flags);


我们可以看到它调用的是一个抽象方法,那么我们就自然要去找到它的实现类,最终我们找到了ContextImpl.java里面。

@Overridepublic boolean bindService(Intent service, ServiceConnection conn, int flags){ warnIfCallingFromSystemProcess(); return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), getUser());}


那么我再来看看 bindServiceCommon又干了些啥?

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user){ ... int res = ActivityManager.getService().bindService(...) ...}


好,到这里就开始跨进程访问了,通过ActivityManagerService去获取远程服务,最终会在ActivityThread.java的handleBindService()方法里回调远程服务的onBind方法返回一个IBinder引用,这样我们就能通过这个IBinder引用去调用相应的接口来进程跨进程访问。后面具体的底层源码这里就不贴出来了,感兴趣的话可以自己去翻阅源码。


3.自定义服务端注册

public class AidlService extends Service {  private List<Person> mPersons = new ArrayList<>(); private IBinder mIBinder = new PersonAidl.Stub() { @Override public void addPerson(Person person) throws RemoteException { mPersons.add(person); }  @Override public List<Person> getPersonList() throws RemoteException { return mPersons; } };  @Nullable @Override public IBinder onBind(Intent intent) { return mIBinder; }}


上面的代码就是我们自定义的一个服务,它里面实现了我们的PersonAidl.Stub,上一节我们分析的Stub源码里的onTransact最终会回调到这里我们具体的实现。并且这里可以看到它里面实现了一个onBind方法来返回这个IBinder引用。

<service android:name=".service.AidlService" android:exported="true" android:enabled="true" android:process=":aidl"/>


上面这段代码就是把我们的服务声明到aidl进程,这里没有什么要说的,都是很标准的写法。


总结

好了,到这里呢,我们这次的Binder进程间通信机制的探索就告一段落了。主要掌握以下几点:


1.Linux内存空间分为用户空间和内核空间,用户空间不能共享,内核空间可以共享;


2.Binder机制的四个重要角色:Client、Server、ServiceManager、Binder驱动;


3.Binder机制采用的是C/S架构,Binder驱动是他们之间的桥梁亦是整个架构的核心。


4.Android中Binder主要的表现形式就是AIDL,重点理解Stub和Proxy模式。