写点什么

Binder 源码阅读指南之 java 层,Java 程序员如何有效提升学习效率

用户头像
极客good
关注
发布于: 刚刚

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);}


  1. mRemote = remote;这个是老的方式,gityuan 的Binder系列7-framework层分析,就是使用这种方式

  2. 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));}}


逻辑很简单


  1. 如果是相同进程,直接返回 Binder 对象,由于 ServiceManager 是单独处于一个进程,这里不会是相同进程,至于本地 Service 是怎么连接到的,我们稍后再讨论

  2. 如果不是相同进程,则创建IServiceManager.Stub.Proxy


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码

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.cppandroid_os_BinderInternal_getContextObject()

android_os_BinderInternal_getContextObject()

这里做了两件事情:


  1. 通过调用ProcessState.getContextObject(NULL)获取 sp<IBinder>,注意这里传递的参数 NULL,即要获取的是 IServiceManager 的 Binder,这一步留作 native Binder 解析流程中详细阐述

  2. 调用javaObjectForIBinder()将 IBinder 转为 java 对象,即 BinderProxy 对象,转换的过程就是将 IBinder 的指针(long 类型)存储在 BinderProxy 的 mNativeData 中

javaObjectForIBinder()

  1. 检查是不是 Binder 类型,这一步是判断是不是传入的是JavaBBinder类型

  2. 创建 BinderProxyNativeData 类型的指针,并初始化对应的字段

  3. 将其 IBinder 的智能指针引用放到 BinderProxyNativeData.mObject 当中

  4. 通过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)


  1. 下面的代码没有看懂,但是关系不是很大


如此一来返回的就是BinderProxy的对象实例,它也是实现了IBinder的接口

BinderProxy.getInstance()

方法签名如下private static BinderProxy getInstance(long nativeData, long iBinder),是一个私有的静态方法,返回的是 BinderProxy 的对象实例,再来对比一下CallStaticObjectMethod(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mGetInstance, (jlong) nativeData, (jlong) val.get()),应该会觉得很相似。前面两个参数gBinderProxyOffsets.mClassgBinderProxyOffsets.mGetInstance代表的分别是 BinderProxy.class 对象以及 getInstance 的方法名字,具体的可以看static int int_register_android_os_BinderProxy(JNIEnv* env)函数是怎么初始化 gBinderProxyOffsets 的字段的;后面两个参数就是 getInstance 的两个参数,将指针转成 jlong 类型传入到BinderProxy.getInstance()当中。接下来看一下 getInstance 中的逻辑


  1. 看看有没有缓存的 BinderProxy 对象,有则直接返回,这里可以缓存 BinderProxy 的原因是 BinderProxy<==>Binder 是一一对应的,所以对于一个 Binder 服务来说,只需要要一个 BinderProxy 即可,所以可以是全局的

  2. 如果没有,则创建一个 BinderProxy 实例,并存入到缓存中

  3. 之后的 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()

这个方法的代码在上面有贴出,逻辑如下


  1. 获取 Parcel 对象,分为两种,传递数据的 data 和获取回复的 reply

  2. 写入两个 String 对象,分别是 token 和服务名称

  3. 写入 Binder 对象,这里我们在下面进行讨论

  4. 调用IBinder.transact()方法,之前获取 IServiceManager 的时候就说过返回的是BinderProxy的对象实例,所以看BinderProxy.transact()方法

BinderProxy.transact()

  1. 判断是不是异步的 Binder,这里有个变量mWarnOnBlocking,还记得之前在调用BinderInternal.getContextObject()之后还做了一个操作Binder.allowBlocking(BinderInternal.getContextObject()),这里会将mWarnOnBloking置为 false,所以这块逻辑一般都是走不到

  2. 是否添加 Trace,主要是性能跟踪

  3. 接下来的一堆操作不是很理解是要干嘛的,最后是调用到transactNative(),这是个 native 方法,方法签名如下transactNative(int code, Parcel data, Parcel reply, int flags),对应android_util_Binder.cppandroid_os_BinderProxy_transact()

android_os_BinderProxy_transact()

  1. 判断 dataObj 是否为 NULL,所以即使不传任何数据,也要在调用transact()之前调用Parcel.obtain()获取 Parcel 对象

  2. 将 java 端的 Parcel 对象转为 native 的 Parcel,包括 data 和 reply,转换的方式其实和 BinderProxy 很类似,之后我们再来讨论

  3. 之后调用getBPNativeData()BinderProxy.mNativeData转为指向BinderProxyNativeData的指针,从而获取到 sp<IBinder>

  4. 之后调用 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 个基本类型组成,“解析”之后就变成一个个有序的字节序列,说它有序是因为“解析”的过程要按照一定的顺序,不然就不能“反解析”了,“反解析“的过程就是将有序的字节序列变成对象的过程,这个“解析”的过程就是序列化,“反解析”的过程就是反序列化

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
Binder源码阅读指南之java层,Java程序员如何有效提升学习效率