一. 前言
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,如果觉得我写的还不错,麻烦帮个忙呗 😃
- 给俺点个赞被,激励激励我,同时也能让这篇文章让更多人看见,(#.#)
- 不用点收藏,诶别点啊,你怎么点了?这多不好意思!
拜托拜托,谢谢各位同学!