ReactNative 4Android源码分析二: 《JNI智能指针之实现篇》

亦余心之所善兮,虽九死其犹未悔。这篇文章主要讲述ReactNative 4Android源码分析二: 《JNI智能指针之实现篇》相关的知识,希望能为你提供帮助。

文/Tamic
http://blog.csdn.net/sk719887916/article/details/53462268
回顾 上一篇介绍了《ReactNative4Android源码分析2: JNI智能指针之介绍篇》JNI智能指针与wrapper class的作用, 下面将对它们的具体实现进行分析, 并解答上篇提出的几个问题
前文回顾了java object在JNI中的引用对象jobject的3种类型。智能指针自然也有相应的如下类型:
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:
智能指针类图
ReactNative 4Android源码分析二: 《JNI智能指针之实现篇》

文章图片

智能指针 从上图可以看出, 由于功能区别, alias_ref别名指针是独立的一个类, 其余的智能指针有共同的父类base_owned_ref。最需要关注智能指针的存储, base_owned_ref与alias_ref均有同样的成员变量:
detail::ReprStorage storage_;
【ReactNative 4Android源码分析二: 《JNI智能指针之实现篇》】**storage**_用来存储创建出来的wrapper对象。这边的设计比较巧妙, 使用C+ + 中的类型萃取技术( type traits) 把wrapper对象和jobject关联, 并将jobject( JNI层) , javaobject( RN层) , wrapper对象( RN层) 三者在内存空间上统一了。先看ReprStorage的实现:
template < typename Repr> struct ReprStorage { explicit ReprStorage(JniType< Repr> obj) noexcept; void set(JniType< Repr> obj) noexcept; Repr& get() noexcept; const Repr& get() const noexcept; JniType< Repr> jobj() const noexcept; private:using Storage = typename std::aligned_storage< sizeof(JObjectBase), alignof(JObjectBase)> ::type; Storage storage_; }; template < typename Repr> void ReprStorage< Repr> ::set(JniType< Repr> obj) noexcept { new (& storage_) Repr; ReprAccess< Repr> ::set(get(), obj); }template < typename Repr> Repr& ReprStorage< Repr> ::get() noexcept { return *reinterpret_cast< Repr*> (& storage_); }

无关的代码已被略去。ReprStorage使用私有变量storage_做为存储空间, 尺寸为JObjectBase类的size。从set和get函数可以看出, storage_内存空间的分配是delay到设值的时候, 并将storage_内存空间的指针通过reinterpret_cast类型转换为Repr类型。ReprStorage的模板参数Repr是存储的wrapper class的类型, 在上章的使用范例中, 也就是MyClass:
struct MyClass : public JavaClass
wrapper class之间的继承关系如下:
ReactNative 4Android源码分析二: 《JNI智能指针之实现篇》

文章图片

wrapper class继承关系
JObjectBase JObjectBase是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 < typename T, typename Base = JObject, typename JType = void> class FBEXPORT JavaClass : public Base { public: static alias_ref< JClass> javaClassStatic(); static local_ref< JClass> javaClassLocal(); protected: /// Allocates a new object and invokes the specified constructor /// Like JClass' s 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< typename... Args> static local_ref< T> newInstance(Args... args) { return detail::newInstance< T> (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< JString, JObject, jstring> { }

另外一种情况就是非预定义类型, 也就是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 < typename T, typename Base = JObject, typename JType = void> class FBEXPORT JavaClass : public Base { using JObjType = typename detail::JTypeFor< T, Base, JType> ; public: using _javaobject = typename JObjType::_javaobject; using javaobject = typename JObjType::javaobject; }; namespace detail { template < typename, typename Base, typename JType> struct JTypeFor { static_assert( std::is_base_of< std::remove_pointer< jobject> ::type, typename std::remove_pointer< JType> ::type > ::value, " " ); using _javaobject = typename std::remove_pointer< JType> ::type; using javaobject = JType; }; template < typename T, typename Base> struct JTypeFor< T, Base, void> { // JNI pattern for jobject assignable pointer struct _javaobject :Base::_javaobject { // This allows us to map back to the defining type (in ReprType, for // example). typedef T JniRefRepr; }; using javaobject = _javaobject*; }; }

在JavaClass中, 将jobject拓展定义为javaobject。javaobject是typename JObjType::javaobject, 也就是JTypeFor的一个模板实例类型的成员指针类型。以例子代码中的MyClass为例, 父类JavaClass接收的三个模板参数分别为MyClass, JObject, void, JTypeFor的三个模板参数也依次是它们, 由于第三个参数是void, 故会使用上面代码中的JTypeFor
struct JTypeFor< MyClass, JObject, void> { 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< T> // is the corresponding JavaClass-derived type and JniType< T> is the // jobject-like type. template < typename T> using ReprType = typename detail::RefReprType< T> ::type; template < typename T> using JniType = typename detail::JavaObjectType< T> ::type;

ReprType用来从模板参数中获得wrapper class类型, JniType用来从模板参数中获得javaobject类型。通过这样的机制, 两个类型彼此打通, 故无论传递何种模板参数, 智能指针都能正确存储对应类型的wrapper class
模板参数起到的作用是什么?
从上可以了解到, 智能指针的模板参数用来获取存储的wrapper class的类型。对于local_ref和global_ref, 它们由于都是强引用, 可以用来直接调用存储的wrapper class提供的方法, 所以它们的实现模板类basic_strong_ref在base_owned_ref提供的存储功能的基础上, 继承扩展提供了指针操作符的重载, 以将对智能指针的访问转发到wrapper对象上, 代码如下:
template< typename T, typename Alloc> inline auto basic_strong_ref< T, Alloc> ::operator-> () noexcept -> Repr* { return & storage_.get(); }template< typename T, typename Alloc> inline auto basic_strong_ref< T, Alloc> ::operator-> () const noexcept -> const Repr* { return & storage_.get(); }

重载实现中, 从storage_中获得存储的wrapper class实例返回即可。对于模板参数Alloc, 则是分配器的类型, 封装了创建、销毁jobject的操作, 由智能指针在构造和析构时调用, 实现jobject生命周期的管理。
总结 总结一下, 本篇简述了wrapper class与智能指针的主干实现。wrapper class扩展jobject至javaobject, 使类型彼此关联; 构建了继承链, 将对java method的反射调用和java类自身的继承关系分解在链路的不同节点。智能指针通过类型萃取负责将jobject存储至正确的wrapper实例, 以对外提供镜像方法; 结合构造与析构函数, 自动进行jobject的生命周期管理。
原文: http://blog.csdn.net/eewolf/article/details/53307603

    推荐阅读