- 5. Binder机制的Java接口
通常,应用程序框架中基于Java语言的Binder接口是通过JNI来调用基于C/C++语言的Binder运行库来为Java应用程序提供进程间通信服务的。在应用程序中,Server被实现为Service的形式,并通过IServiceManager.addService接口来把这个Service添加到Service Manager中;Client也是通过IServiceManager.getService接口来获得Service接口,这样就可以使用这个Service提供的功能了。
这里,我们以XXXService代表XXX的Service服务为例,把整个交互的过程分为如下几个部分来分析:
-
初始化Framework层Binder框架
-
C/S获得ServiceManager的Java远程接口过程
-
XXXService的接口定义和启动过程,添加自己到SM中
-
Client获得XXXService的Java远程接口过程
-
Client通过Java远端接口使用XXXService提供的服务
5.1 Binder framework层的初始化
在java层工作前,需建立与Native层的关系,建立这个关系的函数是 android_util_Binder.cpp::register_android_os_Binder(),流程如下:
- 初始化Java Binder类和Native层的关系:调用 int_register_android_os_Binder(env)
- 找到Java层的Binder
- 通过gBinderOffsets对象保存和Binder类相关的某些在JNI层中使用的信息
- 注册Binder类中native函数的实现
- 初始化Java BinderInternal类和Native层的关系:调用 int_register_android_os_BinderInternal(env)
- 根据BinderInternal的全路径(com/android/internal/os/BinderInternal)找到代表该类的jclass对象
- 通过gBinderInternal静态对象,保存BinderInternal类的一些信息:如methodID和filedID
- 注册BinderInternal类中native函数的实现
- 初始化 Java BinderProxy类和Native层的关系:调用 int_register_android_os_BinderProxy(env)
- 通过gWeakReferenceOffsets来和WeakReference类交互,包括:获取WeakReference类get函数的methodID
- 通过gErrorOffsets来和Error类交互
- 通过gBinderProxyOffsets来和BinderProxy类交互,包括:获取BinderProxy的一些信息
- 通过gClassOffsets来和Class类交互
- 注册BinderProxy native函数的实现
- 初始化Java Parcel类和Native层的关系:调用 int_register_android_os_Parcel(env)
作用:初始化其实就是提前获取一些JNI层的使用信息,这样可以节省每次使用时获取这些信息的时间
5.2 Binder framework层构架总览
这里解释一下Java Binder, Java Internal, Java Proxy,以及IBinder的关系:
- 系统定义了一个IBinder接口类以及DeathRecipient接口。IBinder接口类中定义了FLAG_ONEWAY标志,作用是实现非阻塞,只把请求发送到Binder驱动即可返回,不用等待服务端的结果。
- Binder类和BinderProxy类分别实现了IBinder接口。Binder类作为服务端Bn的代表;BinderProxy类作为客户端Bp的代表
- BinderInternal类仅供Binder架构使用,其内部有一个GcWatcher类,专门用于处理和Binder架构相关的垃圾回收
- Java层同样提供一个用于承载通信数据的Parcel类
5.3 C/S获得ServiceManager的Java远程接口过程
我们要获取的ServiceManager的Java远程接口是一个ServiceManagerProxy对象的IServiceManager接口,如上图。IServiceManager接口提供了getService()和addService()两个函数来管理Service。从serviceManagerProxy的构造函数中发现,它需要一个BinderProxy对象的IBinder接口作为参数;所以得先获得BinderProxy对象。
上图中,我们可以看到获取SM的Java远程接口ServiceManagerProxy的路径,是通过ServiceManager.getIServiceManager()来获取,而该函数又是通过ServiceManagerNative来获取。
我们先来分析getIServiceManager()
5.3.1 ServiceManager.getIServiceManager()
这个函数定义在frameworks/base/core/java/android/os/ServiceManager.java文件中,里面的核心代码:
- sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
- return sServiceManager;
如果其静态成员变量sServiceManager尚未创建,首先要通过BinderInternal.getContextObject()来获得一个BinderProxy对象,再调用ServiceManagerNative.asInterface()来创建对应的ServiceManagerProxy对象
接下来通过BinderInternal.getContextObject() 和ServiceManagerNative.asInterface()两个部分来分析。
5.3.1.1 BinderInternal.getContextObject()
BinderInternal.getContextObject()这个函数定义在frameworks/base/core/java/com/android/internal/os/BinderInternal.java文件中,调用了JNI层的android_os_BinderInternal_getContextObject()。android_os_BinderInternal_getContextObject()是一个JNI方法,在frameworks/base/core/jni/android_util_Binder.cpp文件中,核心代码为:
- sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
- ProcessState::self()->getContextObject(NULL)返回一个BpBinder对象,handle值为0,代码即可以写成:
- sp<IBinder> b = new BpBinder(0);
- return javaObjectForIBinder(env, b); 把这个BpBinder对象转换成一个BinderProxy对象
- 在该函数中,有两个变量gBinderOffsets和gBinderProxyOffsets:
- gBinderOffsets变量是用来记录”ppt 2.”左下角第二个类图中的Binder类的相关信息的,它是在注册Binder类的JNI方法的int_register_android_os_Binder函数初始化的
gBinderProxyOffsets是用来变量是用来记录”ppt 2.”右上角第一个图中的BinderProxy类的相关信息的,它是在注册BinderProxy类的JNI方法的int_register_android_os_BinderProxy函数初始化的
核心代码:- object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor); 创建了一个BinderProxy对象
- env->SetIntField(object, gBinderProxyOffsets.mObject, (int)val.get()); 把BpBinder对象和BinderProxy对象关联起来;BinderProxy.mObject成员记录了这个BpBinder对象的地址
- 最后,把BinderProxy返回到android_os_BinderInternal_getContextObject函数,再返回到ServiceManager.getIServiceManager()中,我们就获得一个BinderProxy对象了
- object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor); 创建了一个BinderProxy对象
- 在getIServiceManager()中的:sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
相当于:sServiceManager = ServiceManagerNative.asInterface(new BinderProxy());
5.3.1.2 ServiceManagerNative.java::asInterface()
接下来调用asInterface(),定义在frameworks/base/core/java/android/os/ServiceManagerNative.java 文件中:
- asInterface(IBinder obj):obj是一个BpProxy对象或者BinderProxy对象(取决于这个申请SM的Java层远程接口的对象是Client还是Server),构造一个和业务相关的Proxy对象。这里我们假设申请者是client,构造了ServiceManagerProxy对象
这样,在getIServiceManager()中:sServiceManager = ServiceManagerNative.asInterface(new BinderProxy()); 相当于:sServiceManager =new ServiceManagerProxy(new BinderProxy());
5.3.2 获得SM的Java远端接口整体流程
这样,申请者在Java层拥有了一个Service Manager远程接口ServiceManagerProxy,这个ServiceManagerProxy对象在JNI层有一个handle值为0的BpBinder对象与之通过gBinderProxyOffsets关联起来,整体流程如下:
5.4 Service启动过程
XXXService继承了IXXXService.Stub类,并通过本地方法实现了Stub类的业务函数,有了XXXService之后,我们就会把它的Server类SystemServer启动起来。
定义在frameworks/base/services/java/com/android/server/SystemServer.java文件中,SystemServer对象是在系统启动时创建的,创建后会启动一个线程来创建XXXService,并把它添加到SM中去
在serverThread extends Thread中:ServiceManager.addService("XXX", new XXXService());
下面我们会进行new XXXService() 和 ServiceManager.addService()两部分的分析
5.4.1 new XXXService()
New XXXService()会调用XXXService类的构造函数,而XXXService类继承于IXXXService.Stub类(XXXNative),Stub类又继承于Binder类,因此会调用Binder类的构造函数,在这个构造函数里,又会调用init()来初始化这个Binder对象
init()只做了一件事,就是创建一个JavaBBinderHolder对象,然后把这个对象的地址保存在Binder类的mObject成员变量中
那么结果为:获得了一个新的实例XXXService(); 且Java层的Binder对象把Native层的JavaBBinderHolder(就是BBinder)保存在变量mObject中
5.4.2 ServiceManager.addService()的实现
分析完了new XXXService(),再来看下ServiceManager.addService()的实现,核心代码:
- getIServiceManager().addService(name, service);
getIServiceManager()之前分析过,返回一个ServiceManagerProxy对象的IServiceManager接口,那么我们看ServiceManagerProxy.addService()的实现:
- 获得Parcel类型的data和reply
- data.writeStrongBinder(service)
- 调用mRemote.transact(ADD_SERVICE_TRANSACTION,data,reply,0);这里的mRemote就是BinderProxy对象,调用transact把封装好的请求数据发送出去
addService()最终会调用到Framework层的BinderProxy.transact();最后调用到Native层的BpBinder::transact()进入到Binder驱动,然后驱动唤醒SM响应这个ADD_SERVICE_TRANSACTION请求,把自己注册到SM中;
AMS在SM中注册服务流程图
XXXService注册服务的类图
5.5.1 Client获得XXXService的Java远程接口过程
Client是通过IServiceManager.getService()来获得XXXService的远程接口的;在client这边的onCreate()中调用IXXXService.Stub.asInterface(ServiceManager.getService(“XXX”)); 先看ServiceManager.getService(“XXX”)
ServiceManager.getService(“XXX”)。实际是调用了ServiceManagerProxy.getService(),这个函数通过mRemote.transact执行操作;和前面一样mRemote是一个BinderProxy对象。
然后调用IBinder binder = reply.readStrongBinder();作用是:调用JNI层的 android_os_Parcel_readStrongBinder(),其作用是把Java语言的Parcel对象转换成C++语言的Parcel对象parcel,并通过parcel->readStrongBinder函数来获得一个Binder引用的BpBinder对象
最后:
- return javaObjectForIBinder(env, parcel->readStrongBinder());
javaObjectForIBinder()之前介绍过,会创建一个BinderProxy对象。相当于: return javaObjectForIBinder(env, new BpBinder(handle)); 返回给上层getService()的binder对象
那么 XXXService = IXXXService.Stub.asInterface(ServiceManager.getService("XXX")); 相当于:XXXService = IXXXService.Stub.asInterface(new BinderProxy()));
5.5.2 IXXXService.Stub.asInterface()
这个函数的核心代码为:
- android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);
- if (((iin!=null)&&(iin instanceof android.os.IXXXService))) {
- return ((android.os.IXXXService)iin);
- }
- return new android.os.IXXXService.Stub.Proxy(obj);
- }
这里的obj是一个BinderProxy对象,它的queryLocalInterface返回null,于是调用下面语句获得XXXService的远程接口: return new android.os.IXXXService.Stub.Proxy(obj);
相当于:return new android.os.IXXXService.Stub.Proxy(new BinderProxy());
这样就获得了XXXService的远程接口了,实质上是实现了IXXXService接口的IXXXService.Stub.Proxy对象
获得XXXService的类图
client使用XXXService框架图
5.6 framework层总结
Native层和Java层的关键点:
- native层中,client会有一个BpBinder引用,Server会有一个BBinder引用来实现通信;
- Java层中,client会有一个BinderProxy对象,server会有一个JavaBBinder对象来负责通信,但实质的通信实现是基于native层,Native层又基于Binder驱动的
针对Java层C/S交互的5个步骤的总结:
- 初始化Java层Binder框架,是为了减少在以后通信过程中初始化的时间
- C/S获得ServiceManager的Java远程接口过程,实际上就是获得ServiceManagerProxy,为Java层提供C/S与SM通信的对象
- XXXService的接口定义和启动过程,添加自己到SM中,实际上是为XXXService对象创建与SM通信的BinderProxy对象(就是步骤2),和创建为与client通信的JavaBBinder对象
- Client获得XXXService的Java远程接口过程,实际上是为client创建与SM通信的BinderProxy对象(就是步骤2),和创建与XXXService通信的BinderPorxy对象
- Client通过Java远端接口使用XXXService提供的服务,实际上是利用client的BinderProxy对象与XXXService的JavaBBinder对象通信,达到client使用服务的目的
虽然Binder机制的代码层层嵌套,逻辑复杂,但Binder机制的实质就是实现不同进程间的通信,通过SM来管理跨进程的服务;理解这一点,就可以理清Binder的核心:通信的实现最终都会由Binder的驱动实现,Native层和Java层的复杂构架,是出于2方面考虑:
- Native层的构架是为了提供统一的接口,并把业务层和逻辑层分开
- Java层的构架是为应用层提供统一的接口,把内部实现封装起来,隔开了底层实现和上层应用