RN 和 fbjni
前言
最近帮 RN 业务方查内存泄漏问题,发现了 fbjni 这个库挺有意思的
正好借助 RN 源码例子,讲述一下 fbjni 是如何控制 java 和 c++ 层对象的生命周期的
源码阅读
下面会贴一些 RN 中的源码 和 fbjni 的源码,不用关心 RN 源码中的对象是做什么的,我们这里只关注对象如何被创建和如何被释放的
// react/jni/CatalystInstanceImpl.h
class CatalystInstanceImpl : public jni::HybridClass<CatalystInstanceImpl> {
public:
static constexpr auto kJavaDescriptor =
"Lcom/facebook/react/bridge/CatalystInstanceImpl;";
static jni::local_ref<jhybriddata> initHybrid(
jni::alias_ref<jclass>,
bool enableRuntimeScheduler,
bool enableRuntimeSchedulerInTurboModule);
// 略
}
// react/jni/CatalystInstanceImpl.cpp
jni::local_ref<CatalystInstanceImpl::jhybriddata>
CatalystInstanceImpl::initHybrid
jni::alias_ref<jclass>,
bool enableRuntimeScheduler,
bool enableRuntimeSchedulerInTurboModule) {
return makeCxxInstance(
enableRuntimeScheduler, enableRuntimeSchedulerInTurboModule);
}
void CatalystInstanceImpl::initializeBridge(
jni::alias_ref<ReactCallback::javaobject> callback,
// This executor is actually a factory holder.
JavaScriptExecutorHolder *jseh,
jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
jni::alias_ref<JavaMessageQueueThread::javaobject> nativeModulesQueue,
jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject>
javaModules,
jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject>
cxxModules) {
set_react_native_logfunc(&log);
moduleRegistry_ = std::make_shared<ModuleRegistry>(buildNativeModuleList(
std::weak_ptr<Instance>(instance_),
javaModules,
cxxModules,
moduleMessageQueue_));
instance_->initializeBridge(
std::make_unique<JInstanceCallback>(callback, moduleMessageQueue_),
jseh->getExecutorFactory(),
std::make_unique<JMessageQueueThread>(jsQueue),
moduleRegistry_);
}
CatalystInstanceImpl::CatalystInstanceImpl(
bool enableRuntimeScheduler,
bool enableRuntimeSchedulerInTurboModule)
: instance_(std::make_unique<Instance>()),
enableRuntimeScheduler_(enableRuntimeScheduler),
enableRuntimeSchedulerInTurboModule_(
enableRuntimeScheduler && enableRuntimeSchedulerInTurboModule) {}
// com.facebook.react.bridge.CatalystInstanceImpl
private CatalystInstanceImpl(
final ReactQueueConfigurationSpec reactQueueConfigurationSpec,
final JavaScriptExecutor jsExecutor,
final NativeModuleRegistry nativeModuleRegistry,
final JSBundleLoader jsBundleLoader,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
FLog.d(ReactConstants.TAG, "Initializing React Xplat Bridge.");
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstanceImpl");
if (ReactFeatureFlags.enableRuntimeSchedulerInTurboModule
&& !ReactFeatureFlags.enableRuntimeScheduler) {
Assertions.assertUnreachable();
}
// jni 调用
mHybridData =
initHybrid(
ReactFeatureFlags.enableRuntimeScheduler,
ReactFeatureFlags.enableRuntimeSchedulerInTurboModule);
mReactQueueConfiguration =
ReactQueueConfigurationImpl.create(
reactQueueConfigurationSpec, new NativeExceptionHandler());
mBridgeIdleListeners = new CopyOnWriteArrayList<>();
mNativeModuleRegistry = nativeModuleRegistry;
mJSModuleRegistry = new JavaScriptModuleRegistry();
mJSBundleLoader = jsBundleLoader;
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
mNativeModulesQueueThread = mReactQueueConfiguration.getNativeModulesQueueThread();
mTraceListener = new JSProfilerTraceListener(this);
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
FLog.d(ReactConstants.TAG, "Initializing React Xplat Bridge before initializeBridge");
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "initializeCxxBridge");
if (ReactFeatureFlags.warnOnLegacyNativeModuleSystemUse) {
warnOnLegacyNativeModuleSystemUse();
}
// jni 调用
initializeBridge(
new BridgeCallback(this),
jsExecutor,
mReactQueueConfiguration.getJSQueueThread(),
mNativeModulesQueueThread,
mNativeModuleRegistry.getJavaModules(this),
mNativeModuleRegistry.getCxxModules());
FLog.d(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge");
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
mJavaScriptContextHolder = new JavaScriptContextHolder(getJavaScriptContext());
}
我们会发现 java 和 cpp 层都有对象叫做 CatalystInstance,且 cpp 层依靠 java 层通知实现释放,那么具体是怎么实现的呢?
首先有几个点:
- c++ 层 initializeBridge 调用的时候,native 层的 instance_ 已经被使用了,说明已经创建好了
- 基于1,说明是 initHybrid 被调用的时候,创建了 c++ 对象
- 基于2,搜索源码你会发现 cpp 层 没有地方显示的调用 了 new CatalystanceImpl() 这种构造函数
- c++ 层,initHybrid 方法返回了很多不熟悉的东西,比如 local_ref
- c++ 的 CatalystInstance 继承了
HybridClass
我们可以先看 java 代码,此处为 CatalystInstanceImpl 构造函数,内部进行了两次 jni 调用。
那么此时,cpp 层的 CatalystInstance 没有意外的话,肯定就是这两次 jni 调用的过程中创建的。
但是当我们搜索 cpp CatalystInstanceImpl 的构造函数调用时,发现其并没有调用。
我们细看 cpp 层的 initHybrid 方法,它只是简单调用了一下 makeCxxInstance 结束了。
一切的魔法就在这个 makeCxxInstance 方法中,流程如下:
initHybrid 触发 makeCxxInstance 进而
-
Hybrid.h new T=CatalystInstance 创建 c++ CatalystInstance 对象,绑定 instance_
-
HybridData::create()
此方法创建 java 层的 HybridData 对象,并完成绑定
2.1 第一行调用 CoreClasses-inl.h newInstance
fbjni 的封装,通过 newObject 创建对应 java 层的 HybridData 对象
2.2 setNativePointer
将 native 层指针传给 java 层的 HybridData 对象
至此,CatalystInstance java 对象 和 CatalystInstance c++ 对象生命周期绑定,java 对象通过 HybridData 来控制 c++ 对象(因为 c++ 对象指针已经传递到 java 层,只要 delete 就完成了释放)
所以,当 java CatalystInstance destroy 的时候,也会触发 java hybridData resetNative 方法,进而调用了 jni 方法,进而调用到了 c++ 的 deleteNative 方法,最终调用到 c++ 的 delete 方法,完成 c++ 对象释放
总结
fbjni 依靠 HybridClass 可以实现 java 和 cpp 对象生命周期的绑定
当然,fbjni 还有好多方便的工具,封装好了各种方法,比如可以更简单的调用 java 方法,而不必写一堆模版函数等等。