亦余心之所善兮,虽九死其犹未悔。这篇文章主要讲述ReactNative 4Android源码分析二: 《JNI智能指针之实现篇》相关的知识,希望能为你提供帮助。
文/Tamic回顾 上一篇介绍了《ReactNative4Android源码分析2: JNI智能指针之介绍篇》JNI智能指针与wrapper class的作用, 下面将对它们的具体实现进行分析, 并解答上篇提出的几个问题
http://blog.csdn.net/sk719887916/article/details/53462268
前文回顾了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_;【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 JavaClasswrapper 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的生命周期管理。
原文: http://blog.csdn.net/eewolf/article/details/53307603
推荐阅读
- Android WebView启动Chromium渲染引擎的过程分析
- android-解决EditText的inputType为Password时, 字体不一致的问题
- Android 修改默认输入法
- Android 反编译初探 应用是如何被注入广告的
- Android5.1和Android6.0定时编译项目方法 (转)
- 安卓屏幕适配
- 8.Android 系统状态栏沉浸式/透明化解决方案
- Unity3D 实现简单的语音聊天 [Android版本]
- Android ViewPager切换之PageTransformer接口中transformPage方法解析