一. 前言
Binder是个很深入的话题,上到应用层,下到linux kernel底层,都能见到Binder的身影。要想用一篇文章对整个Binder的原理叙述清楚,是不可能的。本文从应用层的角度出发,为大家剖析Binder的工作过程。为了避免深入代码细节无法自拔,本文的源码解析也是点到为止,从一个整体上,让大家对Binder有更进一步的理解。
二. 什么是Binder?
直观来说,Binder是Android中的一个类,它实现了IBinder接口。从IPC角度来说,Binder是Android中的一种跨进程通信的方式,是Linux IPC中没有的;从Android Framework角度来说,Binder是ServiceManager连接各种Manager(AcitivtyManager、WindowManager等)和相应的ManagerService的桥梁;从Android应用层来说,Binder是客户端和服务端进行通信的媒介。
在Android开发中,Binder主要被应用在Service中。我们常常通过定义AIDL文件让系统自动生成相应的Binder类,来实现不同进程间的跨进程通信。
这里我们也通过AIDL文件的形式创建Binder,通过分析系统为我们生成的代码,更好的理解Binder的工作过程。
三. 正式开始
首先,我们需要先创建IPeopleManager.aidl,Person.aidl,以及相应的Person.java文件,Person类需要实现parcelable接口才能在Binder中传输,具体的代码如下:
// IPeopleManager.aidl
import com.example.essay.binder.Person;
interface IPeopleManager {
// 给binder定义的需要实现的方法
void addPerson(in Person person);
List<Person> getPeople();
}
// Person.aidl
parcelable Person;
// Person.java
public class Person implements Parcelable {
public int gender;
public String name;
// ...省略实现parcelable接口的方法,以及相应的构造函数
}
一些小伙伴可能之前没创建过aidl类型的文件,看到现在可能有点迷糊。其实不用担心,不晓得aidl文件的创建并不影响我们理解Binder的工作原理,这里只是借助aidl,让系统帮助我们创建一个Binder类而已。接着往下看:
我们已经有了aidl文件,点击编译后,AS就会在app/build/generated/aidl_source_output_dir/debug/out目录下的com/example/essay/binder/,生成IPeopleManager.java文件。这文件里的代码就是我们要着重分析的了,先来整体的看看IPeopleManager.java的代码结构吧,先有个整体的认识,才能更好理解具体方法内部的实现。
public interface IPeopleManager extends android.os.IInterface
{
public static class Default implements com.example.essay.binder.IPeopleManager
{
// ...IPeopleManager的默认实现类,其实都是空实现,里面啥也没干
}
// IPeopleManager 的内部实现类,同时继承了Binder
public static abstract class Stub extends android.os.Binder implements com.example.essay.binder.IPeopleManager
{
// Stub的内部类,同样实现了IPeopleManager接口
private static class Proxy implements com.example.essay.binder.IPeopleManager
{
// ...
}
// ...
}
// 我们在aidl文件中定义的方法,都是以接口的形式,空实现
public void addPerson(com.example.essay.binder.Person person) throws android.os.RemoteException;
public java.util.List<com.example.essay.binder.Person> getPeople() throws android.os.RemoteException;
}
我先把具体的代码细节省略了,是不希望太多的细节影响我们对IPeopleManager整体的分析;可以看到IPeopleManger中有好几个类,分别是Default、Stub、Proxy类。其中Default和Stub是IpeopleManager的直接子类,Proxy是Stub的直接子类,是IPeopleManager的间接子类。
这些类,除了Default,都在Binder通信中起着重要作用,后面我们会逐个分析。系统给我们把这些类都生成在了同一个文件夹里,事实上把它们各个类都独立分离出来也是可以的。
接下来我们再逐个单独提取一个类进行分析。
1. IPeopleManager
public interface IPeopleManager extends android.os.IInterface
{
// 默认的空实现类
public static class Default implements com.example.essay.binder.IPeopleManager
{
@Override public void addPerson(com.example.essay.binder.Person person) throws android.os.RemoteException
{
}
@Override public java.util.List<com.example.essay.binder.Person> getPeople() throws android.os.RemoteException
{
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
public void addPerson(com.example.essay.binder.Person person) throws android.os.RemoteException;
public java.util.List<com.example.essay.binder.Person> getPeople() throws android.os.RemoteException;
}
可以看到,IPeopleManager是个接口,并继承了android.os.IInterface接口。事实上,所有能在Binder上面传输的接口都必须继承IIterface接口。同时它还定义了两个接口方法getPeople()和addPerson(),这两个方法其实就是我们在IPeopleManager.aidl文件中定义的,系统帮我们自动生成了。IpeopleManager只是定义了服务端要提供的方法。
2. Stub
接下来我们看的Stub类,它是个抽象类,继承了Binder,并实现了IPeopleManager接口
public static abstract class Stub extends android.os.Binder implements com.example.essay.binder.IPeopleManager
{
private static final java.lang.String DESCRIPTOR = "com.example.essay.binder.IPeopleManager";
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.essay.binder.IPeopleManager interface,
* generating a proxy if needed.
*/
public static com.example.essay.binder.IPeopleManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.essay.binder.IPeopleManager))) {
return ((com.example.essay.binder.IPeopleManager)iin);
}
return new com.example.essay.binder.IPeopleManager.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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_addPerson:
{
data.enforceInterface(descriptor);
com.example.essay.binder.Person _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.essay.binder.Person.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addPerson(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getPeople:
{
data.enforceInterface(descriptor);
java.util.List<com.example.essay.binder.Person> _result = this.getPeople();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getPeople = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public static boolean setDefaultImpl(com.example.essay.binder.IPeopleManager impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.essay.binder.IPeopleManager getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
private static class Proxy implements com.example.essay.binder.IPeopleManager
{
// ...
public static com.example.essay.binder.IPeopleManager sDefaultImpl;
}
}
哇,好长一串代码。是的,Stub类虽然是个抽象类,本身也并没有具体实现IPeopleManager中定义的接口方法,但它因为继承了Binder,本身是一个Binder类。
当客户端和服务端位于同一个进程时,方法调用不会走跨进程的transact过程,而当两者位于不同进程时,方法调用需要先进入transact()方法,而这个逻辑由Stub的内部代理类Proxy完成,上面代码中,这个类被暂时省略了,后面会仔细解析。可以先说的是,Proxy这个类是专门用于当客户端与服务端不在同一进程时,客户端能够远程调用服务端方法的入口。下面先介绍Stub类中每个方法的含义。
2.1 Stub()
这个方法内部只有一句话
this.attachInterface(this, DESCRIPTOR)
首先,我们要知道DESCRIPTOR的含义,每一个Binder类都有一个DESCRIPTOR用来唯一标识这个Binder,一般来说都用当前的Binder类名表示。这个标识是为了后来,当客户端需要调用服务端方法时,在系统底层的Binder驱动会通过这个标识,找到当前Binder实例的引用并返回给客户端。总结一句话就是说,有了这个标识,客户端就可以通过这个标识获取服务端Binder的引用了。
DESCRIPTOR我们是说清楚了,那上面这行代码到底有什么用呢?为啥要在创建Binder实例的时候调用呢?我们进入到方法里面看看:
/**
* Convenience method for associating a specific interface with the Binder.
* After calling, queryLocalInterface() will be implemented for you
* to return the given owner IInterface when the corresponding
* descriptor is requested.
*/
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
这方法里面好像也没有做什么啊?无疑就是对一些属性进行初始化操作,具体有什么用呢?我们看看这个方法的注释,或许能给我们些提示。噔噔瞪,一串英文,我来给大家翻译下:
这是一个将特定的接口和Binder联系起来的便捷方法。调用后,
queryLocalInterface()就能够返回descriptor对应的IInterface。
现在应该很清楚了,其实这个方法就是将IInterface和descriptor绑定起来,当调用queryLocalInterface()方法时,就返回当前的IInterface对象,通过这个对象,就可以调用我们的addPerson()和getPeople()方法。
2.2 asInterface(android.os.IBinder obj)
这个方法,一般由客户端调用,用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象。这种转换时区分进程的,如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.Proxy对象。核心代码如下:
public static com.example.essay.binder.IPeopleManager asInterface(android.os.IBinder obj)
{
...
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.essay.binder.IPeopleManager))) {
return ((com.example.essay.binder.IPeopleManager)iin);
}
return new com.example.essay.binder.IPeopleManager.Stub.Proxy(obj);
}
2.3 asBinder()
asBinder()是IInterface接口中唯一定义的方法,用于返回当前的Binder对象。
2.4 onTransact
这个方法一般运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法处理。该方法参数列表为(int code, android.os.Parcel data, android.os.Parcel reply, int flags),服务端通过code客户端请求的目标方法是什么,这些code也已经在Stub中定义好,分别对应两个方法:
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getPeople = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
接着从data中取出目标方法所需的参数(如果目标方法中有的话),然后执行目标方法。当目标方法执行完毕后,就向reply中写入返回值(如果目标方法有返回值的话),onTransact返回值标志着客户端请求是否成功,返回true则成功,false则失败。无论返回什么结果,Binder线程池都已经完成了客户端所要求的的任务,并将结果存储在了reply中,这个在上面代码中也能看出来。关于客户端请求成功或者失败,其实是由Proxy类控制的,具体如何控制,其实很简单,我们稍后解析。
2.5 setDefaultImpl(IPeopleManager impl) & getDefaultImpl()
这个方法是sDefaultImpl属性的get和set方法。它是个静态属性,但值得一提的是,这个属性是被定义在Proxy类内部类中的,而它的get和set方法却在外部类中。虽然静态属性被定义在外部类和内部类效果是相同的,但为啥系统不把它生成在外部类中呢?莫非sDefaultImpl这个属性这么表示是在暗示什么?接着往下看。
**3 Proxy **
按照惯例,我们需要将Proxy整个类的结构梳理一遍,下面是Proxy类的完整代码:
private static class Proxy implements com.example.essay.binder.IPeopleManager
{
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.example.essay.binder.Person person) throws android.os.RemoteException
{
...
}
@Override public java.util.List<com.example.essay.binder.Person> getPeople() throws android.os.RemoteException
{
...
}
public static com.example.essay.binder.IPeopleManager sDefaultImpl;
}
Proxy类就没有Stub类复杂了,它是一个Binder代理类,可以看到,它只继承了IPeopleManager接口,并实现了接口的asBinder()方法,asBinder()返回一个mRemote,这个mRemote是在构造方法中被初始化的,而这个构造方法是被Stub类的asInterface()方法中调用,并把客户端传入的IBinder对象作为参数传入,最终赋值给mRemote。
前文说了,Proxy类是客户端远程调用服务端方法的入口类,相关的判断客户端业务请求成功与否,也是由Proxy类共同完成。这些都是怎么完成的?带着问题,我们一起来看Proxy类具体的内部方法:
3.1 Proxy#getPeople()
@Override public java.util.List<com.example.essay.binder.Person> getPeople() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.essay.binder.Person> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getPeople, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getPeople();
}
_reply.readException();
_result = _reply.createTypedArrayList(com.example.essay.binder.Person.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.example.essay.binder.IPeopleManager sDefaultImpl;
}
这个方法,由客户端调用,运行在客户端。当客户端远程调用此方法时:首先,方法内部创建了三个对象_data和_reply,_result,接着将方法的参数写入_data中,然后执行mRemote.transact()进入远程过程调用(RPC),客户端当前的执行线程被挂起,同时服务端的onTransact()方法会被调用,执行完Stub.TRANSACTION_getPeople指定的方法后,服务端将结果写入_reply,并把onTransact()执行结果返回给_status,此时客户端当前的执行进程继续执行。
然后判断_status的状态:如果为false(服务端判定客户端的请求失败)且getDefaultImpl()不为空时,就会调用sDefaultImpl对象的getPeople()方法,而不去取_reply中的结果,直接结束。反之,_status状态为true,从_reply中取出结果,并创建List对象,将结果返回。
而对于**Proxy#addPerson(Person person)**方法,其内部处理逻辑和getPeople()类似,这里就不在详细介绍了,小伙伴们可以自己创建个adil文件,生成个Binder类,自己分析下。
认真的小伙伴看到现在,是不是有种恍然大悟的感觉!?好像之前好几个疑惑的地方到这里都得到了验证!?都有些啥?我们总结下:
-
Proxy类绝对是处理客户端远程调用请求的入口,当客户端与服务端不在同一进程时,就会返回一个
Proxy对象给客户端。 -
只要服务端接收到客户端RPC请求,服务端就会做出相应的动作,并将方法结果写入
_reply,无论服务端最终返回true还是false。同时,如果服务端返回
false,我们也可以尝试给sDefaultImpl赋值,通过调用sDefaultImpl对象的方法,来达到类似服务端的效果。不过我并没有这么用过,事实上对于这个sDefaultImpl的使用场景在哪,我也不清楚,希望有知道的小伙伴,能给我留言告诉我!非常感谢!!
四. 结尾
到这里,Binder的工作过程我们算是解析的差不多了。再总结一下:如果客户端与服务端在同一进程,则直接queryLocalInterface返回本地的Binder对象,直接调用具体的方法.反之两者不在同一进程,服务端返回给客户端一个Binder的代理对象Proxy,由这个Proxy对象向服务端发起远程过程调用请求,最终返回结果给客户端。
还是如开头所说,本文对Binder的解析,并没有从更深的角度去剖析Binder,没有提到一点Binder驱动或者ServiceManager等,事实上这些都是实现Binder远程通信的核心所在。
有兴趣更深入了解Binder的工作原理的同学,可以看看下面的文章:
还有就是任玉刚大佬所著的Android开发艺术探索中,关于Binder部分的知识也非常精炼。
这些文章都在我写这一篇文章时,提供了参考和建议,感谢这些作者!
兄dei,如果觉得我写的还不错,麻烦帮个忙呗 :-)
- 给俺点个赞被,激励激励我,同时也能让这篇文章让更多人看见,(#.#)
- 不用点收藏,诶别点啊,你怎么点了?这多不好意思!
- 噢!还有,我维护了一个路由库。。没别的意思,就是提一下,我维护了一个路由库 =.= !!
拜托拜托,谢谢各位同学!