Binder 源码阅读指南之 java 层,Java 程序员如何有效提升学习效率
ServiceManager.addService()
最终调用到addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
,它里面的代码就一行
getIServiceManager().addService(name, service, allowIsolated, dumpPriority)
看到正主getIServiceManager()
了
ServiceManager.getIServiceManager()
private static IServiceManager getIServiceManager() {if (sServiceManager != null) {return sServiceManager;}
// Find the service managersServiceManager = ServiceManagerNative.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));return sServiceManager;}
这里使用的是单例模式,没有加线程保护,因为这个接口并不给应用使用,以及应用也不能直接操作addService()``getService()
等接口,所以getIServiceManager()
可以是认为运行在主线程中,没错,我们与 ServiceManager 的通信也是采用 Binder,只是 ServiceManager 的 Binder 是有点特殊,我们先看ServiceManagerNative.asInterface()
这个方法
ServiceManagerNative.asInterface()
public static IServiceManager asInterface(IBinder obj) {if (obj == null) {return null;}
// ServiceManager is never localreturn new ServiceManagerProxy(obj);}
直接创建了一个ServiceManagerProxy
对象
ServiceManagerProxy初始化
public ServiceManagerProxy(IBinder remote) {mRemote = remote;mServiceManager = IServiceManager.Stub.asInterface(remote);}
mRemote = remote;
这个是老的方式,gityuan 的Binder系列7-framework层分析,就是使用这种方式mServiceManager = IServiceManager.Stub.asInterface(remote);
是通过 AIDL 方式进行通信,显得更加简洁一点
这里说明一点 AIDL 并不等于 Binder 通信,它只是让 Binder 通信变得更加简单,就如同 Retrofit 和 okhttp 的关系,AIDL 生成的 java 代码在 out 目录下,所以想要分析的话得要先编译过 Android 源码才行,具体的路径是out/soong/.intermediates/frameworks/base/framework-minus-apex/android_common/javac/shard30/classes/android/os/IServiceManager.class
使用 AndroidStudio,IDEA 或者反编译工具可以打开查看,如果想直接看的话我把它拷贝了一份IServiceManager.java
IServiceManager.Stub.asInterface()
public static IServiceManager asInterface(IBinder obj) {if (obj == null) {return null;} else {IInterface iin = obj.queryLocalInterface("android.os.IServiceManager");return (IServiceManager)(iin != null && iin instanceof IServiceManager ? (IServiceManager)iin : new IServiceManager.Stub.Proxy(obj));}}
逻辑很简单
如果是相同进程,直接返回 Binder 对象,由于 ServiceManager 是单独处于一个进程,这里不会是相同进程,至于本地 Service 是怎么连接到的,我们稍后再讨论
如果不是相同进程,则创建
IServiceManager.Stub.Proxy
IServiceManager.Stub.Proxy初始化
Proxy(IBinder remote) {this.mRemote = remote;}
这个和 ServiceManagerProxy 老的初始化方式是不是一模一样,再来看看IServiceManager.Stub.Proxy.addService()
public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority) throws RemoteException {Parcel _data = Parcel.obtain();Parcel _reply = Parcel.obtain();
try {_data.writeInterfaceToken("android.os.IServiceManager");_data.writeString(name);_data.writeStrongBinder(service);_data.writeInt(allowIsolated ? 1 : 0);_data.writeInt(dumpPriority);boolean _status = this.mRemote.transact(3, _data, _reply, 0);if (!_status && IServiceManager.Stub.getDefaultImpl() != null) {IServiceManager.Stub.getDefaultImpl().addService(name, service, allowIsolated, dumpPriority);return;}
_reply.readException();} finally {_reply.recycle();_data.recycle();}}
和老方式也基本一样,所以新方式只是自动生成了这部分代码,减少了代码量,让 IPC 看起来就是一个方法调用,但最终都是调用IBinder.transact()
方法进行 IPC,接下来回到ServiceManager.getIServiceManager()
方法中,上面的只能算作是一些代码技巧而已,接下来就是 Binder 的核心,获取 ServiceManager 的IBinder
对象。
BinderInternal.getContextObject()
这是一个 native 方法,对应到android_util_Binder.cpp
的android_os_BinderInternal_getContextObject()
android_os_BinderInternal_getContextObject()
这里做了两件事情:
通过调用
ProcessState.getContextObject(NULL)
获取 sp<IBinder>,注意这里传递的参数 NULL,即要获取的是 IServiceManager 的 Binder,这一步留作 native Binder 解析流程中详细阐述调用
javaObjectForIBinder()
将 IBinder 转为 java 对象,即 BinderProxy 对象,转换的过程就是将 IBinder 的指针(long 类型)存储在 BinderProxy 的 mNativeData 中
javaObjectForIBinder()
检查是不是 Binder 类型,这一步是判断是不是传入的是
JavaBBinder
类型创建 BinderProxyNativeData 类型的指针,并初始化对应的字段
将其 IBinder 的智能指针引用放到 BinderProxyNativeData.mObject 当中
通过
CallStaticObjectMethod()
调用BinderProxy.getInstance()
并传递对应的参数,这里又引出了一个问题,**我们都知道调用一个类的静态方法是会触发类的加载,可以看到这里直接传的是gBinderProxyOffsets.mClass
,说明 BinderProxy.class 对象已经加载完成了,那么这个gBinderProxyOffsets
又是怎么初始化的呢?**答案是在虚拟机启动的时候就加载完成了,具体的调用栈大致如下
//AndroidRuntime.cpp,这部分是将函数指针进行绑定,使用宏的目的是为了方便 DEBUG,可以定义不同的结构体,进行不同的初始化操作 #define REG_JNI(name) { name }struct RegJNIRec {int (mProc)(JNIEnv);};extern int register_android_os_Binder(JNIEnv* env);//申明函数,使用静态链接链入 static const RegJNIRec gRegJNI[] = {...REG_JNI(register_android_os_Binder),//宏展开就是结构体的初始化操作{register_android_os_Binder}...};//AndroidRuntime.cpp,这部分在进程启动时调用 AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)int AndroidRuntime::startReg(JNIEnv* env)static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)//android_util_Binder.cppint register_android_os_Binder(JNIEnv* env)static int int_register_android_os_BinderInternal(JNIEnv* env)
下面的代码没有看懂,但是关系不是很大
如此一来返回的就是BinderProxy
的对象实例,它也是实现了IBinder
的接口
BinderProxy.getInstance()
方法签名如下private static BinderProxy getInstance(long nativeData, long iBinder)
,是一个私有的静态方法,返回的是 BinderProxy 的对象实例,再来对比一下CallStaticObjectMethod(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get())
,应该会觉得很相似。前面两个参数gBinderProxyOffsets.mClass
和gBinderProxyOffsets.mGetInstance
代表的分别是 BinderProxy.class 对象以及 getInstance 的方法名字,具体的可以看static int int_register_android_os_BinderProxy(JNIEnv* env)
函数是怎么初始化 gBinderProxyOffsets 的字段的;后面两个参数就是 getInstance 的两个参数,将指针转成 jlong 类型传入到BinderProxy.getInstance()
当中。接下来看一下 getInstance 中的逻辑
看看有没有缓存的 BinderProxy 对象,有则直接返回,这里可以缓存 BinderProxy 的原因是 BinderProxy<==>Binder 是一一对应的,所以对于一个 Binder 服务来说,只需要要一个 BinderProxy 即可,所以可以是全局的
如果没有,则创建一个 BinderProxy 实例,并存入到缓存中
之后的 BinderProxy 的初始化只做了一件事,将传递过来的
BinderProxyNativeData
的指针对应的 jlong,存入到 mNativeData 中,实现了 BinderProxy 和 native binder 的关联
以上就是获取 IServiceManager 整个过程,需要记住一点就是 IServiceManager 的所有接口,最终都是调用BinderProxy.transact()
方法中
ServiceManager.addService()
回到最开始添加服务的位置,方法签名如下
public static void addService(String name, IBinder service, boolean allowIsolated,int dumpPriority){try {getIServiceManager().addService(name, service, allowIsolated, dumpPriority);} catch (RemoteException e) {Log.e(TAG, "error in addService", e);}}
在获取到IserviceManager
之后的addService()
则会调用到 AIDL 生成的方法当中
IServiceManager.Proxy.addService()
这个方法的代码在上面有贴出,逻辑如下
获取 Parcel 对象,分为两种,传递数据的 data 和获取回复的 reply
写入两个 String 对象,分别是 token 和服务名称
写入 Binder 对象,这里我们在下面进行讨论
调用
IBinder.transact()
方法,之前获取 IServiceManager 的时候就说过返回的是BinderProxy
的对象实例,所以看BinderProxy.transact()
方法
BinderProxy.transact()
判断是不是异步的 Binder,这里有个变量
mWarnOnBlocking
,还记得之前在调用BinderInternal.getContextObject()
之后还做了一个操作Binder.allowBlocking(BinderInternal.getContextObject())
,这里会将mWarnOnBloking
置为 false,所以这块逻辑一般都是走不到是否添加 Trace,主要是性能跟踪
接下来的一堆操作不是很理解是要干嘛的,最后是调用到
transactNative()
,这是个 native 方法,方法签名如下transactNative(int code, Parcel data, Parcel reply, int flags)
,对应android_util_Binder.cpp
的android_os_BinderProxy_transact()
android_os_BinderProxy_transact()
判断 dataObj 是否为 NULL,所以即使不传任何数据,也要在调用
transact()
之前调用Parcel.obtain()
获取 Parcel 对象将 java 端的 Parcel 对象转为 native 的 Parcel,包括 data 和 reply,转换的方式其实和 BinderProxy 很类似,之后我们再来讨论
之后调用
getBPNativeData()
将BinderProxy.mNativeData
转为指向BinderProxyNativeData
的指针,从而获取到 sp<IBinder>之后调用 native 的 Binder 的
transact()
方法进行,这部分留到 native 的解析的时候再说
IServiceManager.getService()
其实是一样的逻辑,都是通过BinderProxy.tansact()
进行传递到 JNI 再到 native binder,包括开头的 Demo 中客户端调用sayHello()
向服务进行通信,是不是和addService()
很像,其中的逻辑是一模一样的。getService()
和addService()
唯一区别是对于 reply 的处理,getService()
需要获取 native 传回来的 IBinder,我们接下来讨论 Parcel 的时候看一下
Parcel 和 Parceable
为什么要 Parcelable
这里涉及到对象的深拷贝和浅拷贝,由于 java 对象都是通过引用来使用的,引用说白了还是指针。问题就在于此,浅拷贝只是将对象中的数据一字节一字节的拷贝,如果对象含有另一对象的引用,就会造成拷贝出来的对象指向同一个对象,这还只是其一,对于远程调用来说,如果只是浅拷贝,拷贝过来的对象中的引用根本就是 null(进程的内存隔离)。
解决的方法就是深拷贝,所谓深拷贝就是将对象中引用不断的进行“解析”,怎么“解析”呢?不要忘记了 Java 中的 8 个基本类型,所有的 java 对象都是由这 8 个基本类型组成,“解析”之后就变成一个个有序的字节序列,说它有序是因为“解析”的过程要按照一定的顺序,不然就不能“反解析”了,“反解析“的过程就是将有序的字节序列变成对象的过程,这个“解析”的过程就是序列化,“反解析”的过程就是反序列化
评论