Binder 源码阅读指南之 java 层
[android_os_BinderInternal_getContextObject()](()
这里做了两件事情:
通过调用
ProcessState.getContextObject(NULL)
获取 sp<IBinder>,注意这里传递的参数 NULL,即要获取的是 IServiceManager 的 Binder,这一步留作 native Binder 解析流程中详细阐述调用
javaObjectForIBinder()
将 IBinder 转为 java 对象,即 BinderProxy 对象,转换的过程就是将 IBinder 的指针(long 类型)存储在 BinderProxy 的 mNativeData 中
[javaO 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源 bjectForIBinder()](()
检查是不是 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 很 Java 开源项目【ali1024.coding.net/public/P7/Java/git】 类似,之后我们再来讨论
之后调用
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 个基本类型组成,“解析”之后就变成一个个有序的字节序列,说它有序是因为“解析”的过程要按照一定的顺序,不然就不能“反解析”了,“反解析“的过程就是将有序的字节序列变成对象的过程,这个“解析”的过程就是序列化,“反解析”的过程就是反序列化
Java 中的序列话需要实现 Serializeable 以及设置一个 serialVersionUID,不过由于 Serializeable 会产生大量的临时对象,所以 Android 序列化使用的都是 Parceable
Parcel 的作用
Parcel 作用是将需要传递的所有的对象都序列化,变成有序字节序列(这也是 Parceable 的 createFromParcel()和 writeToParcel()写入顺序和读取顺序不能乱的原因),传递给 Binder 驱动,然后从 Binder 驱动读取数据,反序列化成对象,返回给调用者或者等待者。Parcel 支持的类型大致如下:
Java 的 6 种基本类型,除了 char 和 short,没有看到这两个的接口,之外都支持
String 或者 CharSequence
Parceable 的对象
Binder 对象
一些常见的集合类和上述类型的数组
[Parcel.writeInt()](()
writeByte()
,writeBoolean()
这两种类型最终调用的也是writeInt()
,以这个方法为例,看一下 Parcel 的过程,writeInt()
直接调用了nativeWriteInt()
方法
[android_os_Parcel_writeInt()](()
这个方法在android_os_Parcel.cpp
中,直接将 native Parcel 对象的指针转换过来,调用writeInt32()
将值写入,这里是由于 Java 中的 int 都是有符号的 32 位整数,于是这里引出了一个问题,这个Parcel
对象是怎么初始化的?回顾一下前面 Parcel 对象是咋获取的Parcel data = Parcel.obain()
[Parcel.obtain()](()
看一下缓存池里面有没有 Parcel 对象,如果有则返回
如果没有则创建一个新的 Parcel 对象返回
[Parcel 构造方法](()
里面就一句代码init(nativePtr);
[Parcel.init()](()
这里的方法签名如下private void init(long nativePtr)
,需要传一个参数 nativePtr,在构造方法中传的是 0,所以相当于空指针,所以走的是 else 的流程
private void init(long nativePtr) {if (nativePtr != 0) {mNativePtr = nativePtr;mOwnsNativeParcelObject = false;} else {mNativePtr = nativeCreate();mOwnsNativeParcelObject = true;}}
[android_os_Parcel_create()](()
nativeCreate()
对应到android_os_Parcel.cpp
中的static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
,很简单的一句话,创建了一个 Parcel 对象,并且将指针转为了 jlong 类型返回,没有初始化任何东西
为什么 Parcel 可以写入 IBinder(writeStrongBinder()
),IBinder 既不是 Parcelable 也不是基础类型,也不是 Parcelable 对象
针对这个问题,我们需要先了解 Binder 服务在 native 中对应的是什么东西。
[Binder 初始化](()
函数签名如下public Binder(String descriptor)
,这里面做了一件很重要的事情,调用getNativeBBinderHolder()
[android_os_Binder_getNativeBBinderHolder()](()
getNativeBBinderHolder()
对应android_util_Binder.cpp
JNI 函数android_os_Binder_getNativeBBinderHolder()
,里面创建了JavaBBinderHolder
对象,并返回指针所对应的 jlong
[Parcel.writeStrongBinder()](()
调用了nativeWriteStrongBinder()
[android_os_Parcel_writeStrongBinder()](()
nativeWriteStrongBinder()
对应android_os_Parcel.cpp
JNI 函数android_os_Parcel_writeStrongBinder()
,
将 java 的 Parcel 中存储的本地 Parcel 的 long 类型指针获取到
调用
ibinderForJavaObject()
获取 sp<IBinder>调用本地 Parcel 的
writeStrongBinder()
函数,这部分留到 native Binder 再进行梳理
[ibinderForJavaObject()](()
注意此时的方法已经跳转到了android_os_Binder.cpp
中,
判断 java 对象是否为 null
判断是否为 Binder 类型(相当与 instanceof),在这里为 true
获取 JavaBBinderHolder 指针
最后
看完美团、字节、腾讯这三家的面试问题,是不是感觉问的特别多,可能咱们又得开启面试造火箭、工作拧螺丝的模式去准备下一次的面试了。
开篇有提及我可是足足背下了 1000 道题目,多少还是有点用的呢,我看了下,上面这些问题大部分都能从我背的题里找到的,所以今天给大家分享一下互联网工程师必备的面试 1000 题。
注意不论是我说的互联网面试 1000 题,还是后面提及的算法与数据结构、设计模式以及更多的 Java 学习笔记等,皆可分享给各位朋友
互联网工程师必备的面试 1000 题
而且从上面三家来看,算法与数据结构是必备不可少的呀,因此我建议大家可以去刷刷这本左程云大佬著作的《程序员代码面试指南 IT 名企算法与数据结构题目最优解》,里面近 200 道真实出现过的经典代码面试题。
评论