已阅读5页,还剩5页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
ReactNative 4Android源码分析二: JNI智能指针JNI智能指针之介绍篇:JNI指针通常的app中, JNI提供的native函数主要充当Java类的扩展,逻辑层在Java端,JNI端较少使用OOP的设计思想。 而对于native端功能较重的模块,例如开源的阅读器FBReader,native端与Java端有较多交互,即native会主动创建Java对象并调用它们的方法以实现功能,这时就需要考虑将native至Java的操作与访问框架化,形成更高层次的封装,以避免直接使用原始的JNI反射API集去操作Java对象。 对于ReactNative For Android而言,这套访问框架尤其重要,其核心就是JNI智能指针这个基本数据类型。它的实现基于C11标准,将先用几篇对这套native至Java的操作框架进行介绍,为后续分析打下良好基础。Native引用首先回顾一下Java Object(jobject)在native端的三种引用类型:全局引用类似于C语言中的全局变量。使用NewGlobalRef创建,支持跨线程访问 ,在调用释放DeleteGlobalRef销毁前,GC无法回收该引用对应的java object。局部引用概念上与C语言中的局部变量有相似点,但不等同。使用NewLocalRef创建, 只能在本线程内安全访问,当创建该引用的native调用链返回至JVM时,未销毁的局部引用会被JVM自动GC回收。但由于局部引用表容量有限,在返回至JVM前,可以调用DeleteLocalRef先行销毁,避免局部引用表超限引起崩溃。弱全局引用与全局引用一样具有全局作用域,但不会影响GC回收, GC可以随时回收该引用对应的java object。使用NewWeakGlobalRef创建,当需要使用时,需要将其升级为全局引用或者局部引用,若已被回收,会返回null,使用DeleteWeakGlobalRef销毁。该引用类型使用场景较少。由上可见,JNI智能指针的第一个需求,就是要自动管理jobject的生命周期,当进入与离开对应作用域时,需要自动调用对应生命周期的创建与销毁函数。这在C+中,通常会结合构造与析构函数来进行配对调用。若功能仅限于此,就与普通的智能指针和mutext锁管理机制类似了,更重要的需求是在C+层提供与被管理的Java对象镜像结构的C+对象,形成高层次封装。这样,对jobject的访问与操作就会被封装在对应的镜像C+对象中,相关JNI反射调用的细节被隐藏,对于其他native模块而言,与Java层的交互被转化成了与这些镜像C+对象的交互,整个实现风格OOP化了。这些镜像C+对象被称为wrapper对象,其定义代码位于ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses.h文件中。先看一个使用范例: struct MyClass : public JavaClass constexpr static auto kJavaDescriptor = Lcom/example/package/MyClass; void foo() static auto method = javaClassStatic()-getMethod(foo); method(self(); static local_ref create(int i) return newInstance(i); ;auto obj = MyClass:create(10);obj-foo();Native的需求是在native端创建com.example.package.MyClass这个自定义的Java类的对象,并访问它的foo方法。实现步骤例子中实现的步骤是:定义java的MyClass的wrapper C+类MyClass,所有wrapper均需要继承于JavaClass的一个模板实例,并将自身类型做为JavaClass的第一个模板类型参数,以供JavaClass获取具体wrapper的类型。给static成员变量kJavaDescriptor赋值为对应Java类的全类名。 在wrapper类实现镜像方法foo(), 其会获取jclass的包装类JClass对象,并获取jmethod的包装类JMethod进行调用。create工厂方法中使用newInstance构建镜像对象的实例,并将其存至局部智能指针local_ref。这样就可以通过智能指针访问wrapper class提供的foo方法,实现了native至Java的镜像映射。除了实现对一个java类的的映射,还需要支持对java继承关系的映射。若java的MyClass有一子类MyChildClass,native层为其建立的wrapper class可如下:struct MyChildClass : public JavaClass constexpr static auto kJavaDescriptor = Lcom/example/package/MyChildClass;这里需要用到JavaClass的第二个模板参数,设为MyClass,它是JavaClass疑问这就带来几个问题:javaObject与jobject的关系是什么?为什么智能指针的模板参数能够接受多种类型?模板参数起到的作用是什么?结尾这些问题将在下一篇智能指针的具体实现篇中解答。 总结一下,在ReactNative for Android中,为了简化native层对Java层的调用,提供了镜像结构的wrapper class,结合智能指针,将jobject的生命周期管理、java method的反射调用等“样板”代码封装起来,是比较优雅的JNI调用框架。JNI智能指针之实现篇global_ref全局指针与jobject全局引用相对应,使用场景包括全局变量、成员变量等。这些场景中的jobject,不应该从native返回至JVM时释放,故使用global_ref进行包裹。local_ref局部指针与jobject局部引用相对应,使用场景包括局部变量、函数返回值等。当local_ref离开所在作用域时,会释放自身对jobject的引用,即在析构函数中调用DeleteLocalRef。weak_ref弱指针与jobject弱全局引用相对应,在目前版本的RN代码中未实际使用。alias_ref别名指针,不对持有的jobject进行生命周期管理。即在构造与析构别名智能指针对象时,不会对持有的jobject进行创建与销毁的JNI操作。该指针的目的只是为了提供调用wrapper对象方法的能力,jobject的生命周期由另外的智能指针或直接由JVM进行管理和保证有效性,指针自身不对其额外进行管理。以上智能指针均未提供引用计数功能,而是通过在智能指针间交换被管理的对象来进行指针转换。智能指针的类图如下,其代码位于ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/References.h:智能指针类图智能指针从上图可以看出,由于功能区别,alias_ref别名指针是独立的一个类,其余的智能指针有共同的父类base_owned_ref。最需要关注智能指针的存储,base_owned_ref与alias_ref均有同样的成员变量:detail:ReprStorage storage_;*storage*_用来存储创建出来的wrapper对象。这边的设计比较巧妙,使用C+中的类型萃取技术(type traits)把wrapper对象和jobject关联,并将jobject(JNI层),javaobject(RN层),wrapper对象(RN层)三者在内存空间上统一了。先看ReprStorage的实现:template struct ReprStorage explicit ReprStorage(JniType obj) noexcept; void set(JniType obj) noexcept; Repr& get() noexcept; const Repr& get() const noexcept; JniType jobj() const noexcept; private: using Storage = typename std:aligned_storage:type; Storage storage_;template void ReprStorage:set(JniType obj) noexcept new (&storage_) Repr; ReprAccess:set(get(), obj);template Repr& ReprStorage:get() noexcept return *reinterpret_cast(&storage_);无关的代码已被略去。ReprStorage使用私有变量storage_做为存储空间,尺寸为JObjectBase类的size。从set和get函数可以看出,storage_内存空间的分配是delay到设值的时候,并将storage_内存空间的指针通过reinterpret_cast类型转换为Repr类型。ReprStorage的模板参数Repr是存储的wrapper class的类型,在上章的使用范例中,也就是MyClass:struct MyClass : public JavaClasswrapper class之间的继承关系如下:wrapper class继承关系JObjectBaseJObjectBase是wrapper class的根父类,这里显然存在一个问题:为何能用父类size的内存空间去存放任意子类对象?完成继承关系的讨论后,再回顾这个问题。图中的CustomJavaClass是一个自定义wrapper class,它继承于JavaClass的一个模板实例。所有Java类(除去Object类)的native镜像wrapper class,均需要继承于JavaClass的某个模板实例。JavaClass起到两个桥梁作用:当前定义的wrapper class与对应Java类父类的wrapper class之间继承关系的桥梁;当前定义的wrapper class与对应Java对象的JNI jobject的桥梁。它有三个模板参数,下面是它的类声明,其代码位于ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/CoreClasses.h:template class FBEXPORT JavaClass : public Base public: static alias_ref javaClassStatic(); static local_ref javaClassLocal();protected: / Allocates a new object and invokes the specified constructor / Like JClasss getConstructor, this function can only check at runtime if / the class actually has a constructor that accepts the corresponding types. / While a JavaClass-type can expose this function directly, it is recommended / to instead to use this to explicitly only expose those constructors that / the Java class actually has (i.e. with static create() functions). template static local_ref newInstance(Args. args) return detail:newInstance(args.); javaobject self() const noexcept;无关的代码已被略去。第一个模板参数是子wrapper class的类型。 JavaClass的这个模板实例作为这个wrapper class的父类,提供了创建wrapper class对象的工厂方法和与对应jobject关联的能力,故需要获得子类的类型。第二个模板参数是该JavaClass模板实例的父类。 它的默认类型是JObject,代表java.lang.Object类的wrapper class,是唯一不需要继承于JavaClass的wrapper对象。JObject提供了对java对象的Class、Field、Method等的访问封装方法,wrapper class通过对它的继承关系,获得了去调用Java method的能力。若wrapper class无需提供Java类父类方法的调用能力,则第二个模板参数保持默认值JObject即可,否则,第二个模板参数就为Java类父类的wrapper class,例子在上章中已提供。由于JavaClass帮助构建了继承链,wrapper class具备了提供父java类的native镜像方法的能力。第三个模板参数是定义的wrapper class对应Java对象的JNI jobject的类型JavaClass会将wrapper class与jobject建立起绑定关系。根据jobject的具体类型,会分两种情况,如果为JNI预定义的jobject类型,例如jclass、jthrowable、jarray、jstring等,第三个模板参数就是它们,RN中已经预定义了它们的wrapper class。例如:class FBEXPORT JString : public JavaClass 另外一种情况就是非预定义类型,也就是jobject这个通用类型(jclass、jstring等预定义类型也是它的子类),这时第三个参数就应为默认值void。即不由模板参数指定jobject的具体子类,而是使用wrapper class内部嵌套定义的扩展子类。浏览jobject内部定义前,先回顾刚才的存储问题。既然sizeof(JObjectBase)的内存空间能够正确放置任意wrapper class子类的实例,就说明子类所占的内存空间与根类JObjectBase一样。而对于一个C+类而言,对象的size就是所有非static成员变量的size之和(需考虑内存对齐),这就约束了wrapper class作为子类不能额外声明任何非static成员变量,才能与根父类JObjectBase保持size的一致。从wrapper class的设计目的考虑,它只是Java类在native空间的镜像类和接口包装类,业务逻辑应由调用者实现,wrapper class自身应该是无状态的,所以不允许wrapper class定义非static成员变量是合理的。智能指针存储的是wrapper class的实例,wrapper class中存储的是jobject,从以上分析可以知道,存储的jobject成员变量只能由根父类JObjectBase去承载。下面是JObjectBase的类定义,代码位于ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/References-forward.h:struct JObjectBase jobject get() const noexcept; void set(jobject reference) noexcept; jobject this_;JObjectBase是一个简单的bean类,唯一的成员变量就是jobject,这也是所有wrapper class唯一的成员变量。在JavaClass模板类中,为了实现jobject与wrapper class两者的关联,对jobject做了内部扩展定义。为了理解它,先回顾下jobject在jni.h中的原始定义:class _jobject ;typedef _jobject* jobject;class _jstring : public _jobject ;所以,jobject就是个指针,指向java object在JVM中的内存对象,对于像*dexposed*这样的热修复框架,就是利用这些指针去修改java对象模型来改变java method的属性以实现hook。这里的定义_jobject是空类,只是为了定义指针语法以在JNI中去引用内存对象,并不意味Java内存对象真的是个空对象,真正定义是JVM内部的、平台相关的,而不需要将实现细节暴露给JNI。在JavaClass中,对jobject的扩展定义javaobject类型如下:template class FBEXPORT JavaClass : public Base using JObjType = typename detail:JTypeFor;public: using _javaobject = typename JObjType:_javaobject; using javaobject = typename JObjType:javaobject;namespace detail template struct JTypeFor static_assert( std:is_base_ofstd:remove_pointer:type,typename std:remove_pointer:type :value, ); using _javaobject = typename std:remove_pointer:type; using javaobject = JType;template struct JTypeFor / JNI pattern for jobject assignable pointer struct _javaobject : Base:_javaobject / This allows us to map back to the defining type (in ReprType, for/ ple).typedef T JniRefRepr; ; using javaobject = _javaobject*;在JavaClass中,将jobject拓展定义为javaobject。javaobject是typename JObjType:javaobject,也就是JTypeFor的一个模板实例类型的成员指针类型。以例子代码中的MyClass为例,父类JavaClass接收的三个模板参数分别为MyClass,JObject,void,JTypeFor的三个模板参数也依次是它们,由于第三个参数是void,故会使用上面代码中的JTypeForstruct JTypeFor struct _javaobject : JObject:_javaobject typedef MyClass JniRefRepr; ; using javaobject = _javaobject*;可看到_javaobject继承于JObject: _javaobject,它的定义如下: typedef _jobject _javaobject; typedef _javaobject* javaobject;JObject:_javaobject就是jni.h中的_jobject类型,故MyClass中的_javaobject对_jobject的继承扩展,只是添加了一个嵌套类成员类型JniRefRepr,来指向当前_javaobject所对应的wrapper class类型,这就是所谓的C+类型萃取技术。因为_jobject是用来指向Java内存对象,所以不能用继承后添加成员变量的方式来扩展,否则会破坏内存对象,而成员类型是属于类定义,不会占用对象的空间。另,javaobject是指向_javaobject的指针,jobject是指向_jobject的指针。问题解答现在来解答上章的三个问题:javaobject与jobject的关系是什么?两者本质是一样的,都是指向java内存对象的JNI引用;区别是javaobject是jobject继承扩展,继承后的javaobject拥有一个类成员类型变量,指向对应的wrapper class,使得两者彼此关联。从内存上看,sizeof(JObjectBase)=sizeof(任意wrapper class)=sizeof(jobject)=sizeof(javaobject),达到了有机的统一。为什么智能指针的模板参数能够接受多种类型?在上章例子中,local_ref与local_ref传递了不同模板参数,从语法上看区别很大,但在内部实现时,都会进行类型萃取。即无论传递的何种类型,都会萃取出对应的ReprType(wrapper class类型)、JniType(javaobject类型)。javaobject类是wrapper class的成员类型,故从wrapper class可以获得对应javaobject引用的类型;javaobject类的成员类型是wrapper class,故从javaobject业可以获得对应wrapper class的类型。在框架中提供了两个工具来进行类型萃取和转换:/ Given T, either a jobject-like type or a JavaClass-derived type, ReprType/ is the corresponding JavaC
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 《GBT 35197-2017 土方机械 非公路电传动矿用自卸车 试验方法》专题研究报告
- 吸入药物联合使用策略
- 吞咽障碍在心衰患者的营养解决方案
- 神经系统疾病诊疗进展
- 合并慢性代谢综合征抗凝治疗的个体化噻唑烷二酮类方案
- 合并先天性心脏病新生儿听力筛查方案
- 2025年国际航空货运合同
- 2025年古建筑数字化保护合同协议
- 口岸公卫事件应对方案
- 变应性鼻炎特异性免疫治疗的个性化方案
- 初中数学教学随笔10篇
- 超星尔雅学习通《明史十讲(复旦大学)》2025章节测试附答案
- 术中压力性损伤预防团体标准深度解读
- 小学数学教学中融入中国传统文化的实践研究
- 机械伤害的安全培训课件
- 同等学力申硕(临床医学)考试题库(全真题库)
- 第15课 两次鸦片战争 课件高一上学期统编版(2019)必修中外历史纲要上-1
- 临床输血管理委员会年终的工作总结
- 国家安全教育高教-第六章坚持以经济安全为基础
- 足部固定器产品技术要求2022
- 韦莱韬悦-东方明珠新媒体集团一体化职位职级体系方案-2018
评论
0/150
提交评论