Binder工作过程和源码解析

一. 前言

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.aidlPerson.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中有好几个类,分别是DefaultStubProxy类。其中DefaultStubIpeopleManager的直接子类,ProxyStub的直接子类,是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

现在应该很清楚了,其实这个方法就是将IInterfacedescriptor绑定起来,当调用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属性的getset方法。它是个静态属性,但值得一提的是,这个属性是被定义在Proxy类内部类中的,而它的getset方法却在外部类中。虽然静态属性被定义在外部类和内部类效果是相同的,但为啥系统不把它生成在外部类中呢?莫非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类,自己分析下。

认真的小伙伴看到现在,是不是有种恍然大悟的感觉!?好像之前好几个疑惑的地方到这里都得到了验证!?都有些啥?我们总结下:

  1. Proxy类绝对是处理客户端远程调用请求的入口,当客户端与服务端不在同一进程时,就会返回一个Proxy对象给客户端。

  2. 只要服务端接收到客户端RPC请求,服务端就会做出相应的动作,并将方法结果写入_reply,无论服务端最终返回true还是false

    同时,如果服务端返回false,我们也可以尝试给sDefaultImpl赋值,通过调用sDefaultImpl对象的方法,来达到类似服务端的效果。不过我并没有这么用过,事实上对于这个sDefaultImpl的使用场景在哪,我也不清楚,希望有知道的小伙伴,能给我留言告诉我!非常感谢!!

四. 结尾

到这里,Binder的工作过程我们算是解析的差不多了。再总结一下:如果客户端与服务端在同一进程,则直接queryLocalInterface返回本地的Binder对象,直接调用具体的方法.反之两者不在同一进程,服务端返回给客户端一个Binder的代理对象Proxy,由这个Proxy对象向服务端发起远程过程调用请求,最终返回结果给客户端。

还是如开头所说,本文对Binder的解析,并没有从更深的角度去剖析Binder,没有提到一点Binder驱动或者ServiceManager等,事实上这些都是实现Binder远程通信的核心所在。

有兴趣更深入了解Binder的工作原理的同学,可以看看下面的文章:

写给 Android 应用工程师的 Binder 原理剖析

Android Bander设计与实现 - 设计篇

还有就是任玉刚大佬所著的Android开发艺术探索中,关于Binder部分的知识也非常精炼。

这些文章都在我写这一篇文章时,提供了参考和建议,感谢这些作者!

兄dei,如果觉得我写的还不错,麻烦帮个忙呗 😃
  1. 给俺点个赞被,激励激励我,同时也能让这篇文章让更多人看见,(#.#)
  2. 不用点收藏,诶别点啊,你怎么点了?这多不好意思!

拜托拜托,谢谢各位同学!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值