文/Tamic
http://blog.****.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:
智能指针类图
智能指针
从上图可以看出,由于功能区别,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 <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之间的继承关系如下:
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的生命周期管理。