React Native 源码分析——通信机制
Posted 薛瑄
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React Native 源码分析——通信机制相关的知识,希望对你有一定的参考价值。
本篇来分析一下,RN js和java的通信机制,在上一篇启动流程 看完后,通信的过程,你应该也能猜出个大概。具体过程,也是很简单
1、React Native 源码分析(一)—— 启动流程
2、React Native 源码分析(二)—— 通信机制
3、React Native 源码分析(三)—— Native View创建流程
4、React Native 源码分析(四)—— 任务调度
5、React Native 源码分析(五)—— 事件分发
一、 Java->JS 通信
先来一张图,看一下通信的整体过程
图片来源
这里接着上一篇的代码1.9 reactRoot.runApplication();
继续分析,从java启动react的过程
1.1 runApplication
@Override
public void runApplication()
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "ReactRootView.runApplication");
try
if (mReactInstanceManager == null || !mIsAttachedToInstance)
return;
ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
if (reactContext == null)
return;
CatalystInstance catalystInstance = reactContext.getCatalystInstance();
//在自己的activity中,指定的mainComponentName名称
String jsAppModuleName = getJSModuleName();
//onMeasure执行后,该变量会设为true,如果再次调用到runApplication,需要重新绘制
if (mWasMeasured)
//本质是会通过FabricUIManager或UIManagerModule 来更新界面
updateRootLayoutSpecs(true, mWidthMeasureSpec, mHeightMeasureSpec);
WritableNativeMap appParams = new WritableNativeMap();
appParams.putDouble("rootTag", getRootViewTag());
@Nullable Bundle appProperties = getAppProperties();
if (appProperties != null)
appParams.putMap("initialProps", Arguments.fromBundle(appProperties));
mShouldLogContentAppeared = true;
//启动js中的 AppRegistry register的Component,并传入参数appParams
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
finally
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
getJSModule的调用流程catalystInstance.getJSModule ->mJSModuleRegistry.getjavascriptModule
,调用到了JavaScriptModuleRegistry中,这里是通过动态代理的方式,让 继承了JavaScriptModule的Interface中的方法的 调用,都被代理到 mCatalystInstance.callFunction
2、JavaScriptModuleRegistry # getJavaScriptModule
public final class JavaScriptModuleRegistry
private final HashMap<Class<? extends JavaScriptModule>, JavaScriptModule> mModuleInstances;
public JavaScriptModuleRegistry()
mModuleInstances = new HashMap<>();
//catalystInstance.getJSModule 调到这里
public synchronized <T extends JavaScriptModule> T getJavaScriptModule(
CatalystInstance instance, Class<T> moduleInterface)
JavaScriptModule module = mModuleInstances.get(moduleInterface);
if (module != null)
return (T) module;
JavaScriptModule interfaceProxy =
(JavaScriptModule)
Proxy.newProxyInstance(
moduleInterface.getClassLoader(),
//此时指的是AppRegistry.class
new Class[] moduleInterface,
new JavaScriptModuleInvocationHandler(instance, moduleInterface));
mModuleInstances.put(moduleInterface, interfaceProxy);
return (T) interfaceProxy;
private static class JavaScriptModuleInvocationHandler implements InvocationHandler
private final CatalystInstance mCatalystInstance;
private final Class<? extends JavaScriptModule> mModuleInterface;
private @Nullable String mName;
public JavaScriptModuleInvocationHandler(
CatalystInstance catalystInstance, Class<? extends JavaScriptModule> moduleInterface)
mCatalystInstance = catalystInstance;
mModuleInterface = moduleInterface;
...
....
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable
NativeArray jsArgs = args != null ? Arguments.fromJavaArgs(args) : new WritableNativeArray();
//getJSModuleName 获取js module的类名,method.getName() 调用的方法名,jsArgs 传入的参数
//下面分析该函数
mCatalystInstance.callFunction(getJSModuleName(), method.getName(), jsArgs);
return null;
....
1.3 CatalystInstance的函数 callFunction 和 runJSBundle
public void callFunction(PendingJSCall function)
if (mDestroyed)
final String call = function.toString();
FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed: " + call);
return;
//如果js bundle还未加载,就把任务加入到mJSCallsPendingInit,直接返回
if (!mAcceptCalls)
// Most of the time the instance is initialized and we don't need to acquire the lock
synchronized (mJSCallsPendingInitLock)
if (!mAcceptCalls)
mJSCallsPendingInit.add(function);
return;
function.call(this);
mAcceptCalls 的初始值是false,callFunction
和 runJSBundle
都是从React Native 源码分析(一)—— 启动流程的代码1.4的 runCreateReactContextOnNewThread,callFunction 是在nativeModule线程运行,由于并发的原因,为了保证js bundle加载后,再去调用js的函数。引入mAcceptCalls 来控制,
@Override
public void runJSBundle()
//上篇文章代码1.4分析的,mJSBundleLoader 有几种类型,由js bundle的位置来决定
mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
synchronized (mJSCallsPendingInitLock)
// Loading the bundle is queued on the JS thread, but may not have
// run yet. It's safe to set this here, though, since any work it
// gates will be queued on the JS thread behind the load.
mAcceptCalls = true;
for (PendingJSCall function : mJSCallsPendingInit)
function.call(this);
mJSCallsPendingInit.clear();
mJSBundleHasLoaded = true;
最终都是调用了function.call(this);
,下面来分析该函数
1.4 PendingJSCall # call
void call(CatalystInstanceImpl catalystInstance)
NativeArray arguments = mArguments != null ? mArguments : new WritableNativeArray();
//这里调用到c++ 层
catalystInstance.jniCallJSFunction(mModule, mMethod, arguments);
1.5 CatalystInstanceImpl::jniCallJSFunction
void CatalystInstanceImpl::jniCallJSFunction(
std::string module,
std::string method,
NativeArray *arguments)
// We want to share the C++ code, and on ios, modules pass module/method
// names as strings all the way through to JS, and there's no way to do
// string -> id mapping on the objc side. So on android, we convert the
// number to a string, here which gets passed as-is to JS. There, they they
// used as ids if isFinite(), which handles this case, and looked up as
// strings otherwise. Eventually, we'll probably want to modify the stack
// from the JS proxy through here to use strings, too.
instance_->callJSFunction(
std::move(module), std::move(method), arguments->consume());
1.6 Instance::callJSFunction
void Instance::callJSFunction(
std::string &&module,
std::string &&method,
folly::dynamic &¶ms)
//该回调是在上篇文章的代码1.7 的new BridgeCallback(this) 创建的
callback_->incrementPendingJSCalls();
//该变量 在上篇文章,也有介绍,下面直接看callFunction
nativeToJsBridge_->callFunction(
std::move(module), std::move(method), std::move(params));
1.7 NativeToJsBridge::callFunction
void NativeToJsBridge::callFunction(
std::string &&module,
std::string &&method,
folly::dynamic &&arguments)
...
//把lambda代码发送到js线程队列,所以java->js的通信是异步的
runOnExecutorQueue([this,
module = std::move(module),
method = std::move(method),
arguments = std::move(arguments),
systraceCookie](JSExecutor *executor)
if (m_applicationScriptHasFailure)
...
// This is safe because we are running on the executor's thread: it won't
// destruct until after it's been unregistered (which we check above) and
// that will happen on this thread
//executor 这里作为lambda的parameter,argument 传的是m_executor(上篇文章代码2.5 被赋值)
executor->callFunction(module, method, arguments);
);
最终调用m_executor ,(本例)也就是JSIExecutor 的callFunction,(也可能是其他的jsexecutor 的callFunction)
1.8 JSIExecutor:: callFunction
void JSIExecutor::callFunction(
const std::string &moduleId,
const std::string &methodId,
const folly::dynamic &arguments)
if (!callFunctionReturnFlushedQueue_)
//把js 中的函数映射到c++层,例如这里的callFunctionReturnFlushedQueue_
bindBridge();
...
try
scopedTimeoutInvoker_(
[&]
//调用js 的同名函数
ret = callFunctionReturnFlushedQueue_->call(
*runtime_,
moduleId,
methodId,
valueFromDynamic(*runtime_, arguments));
,
std::move(errorProducer));
catch (...)
std::throw_with_nested(
std::runtime_error("Error calling " + moduleId + "." + methodId));
//返回值,传到java
callNativeModules(ret, true);
下面到js看看,callFunctionReturnFlushedQueue_
的实现
1.9 MessageQueue & callFunctionReturnFlushedQueue
MessageQueue 类的位置在 Libraries/BatchedBridge/MessageQueue.js
callFunctionReturnFlushedQueue(
module: string,
method: string,
args: mixed[],
): null | [Array<number>, Array<number>, Array<mixed>, number]
this.__guard(() =>
//找到对应的module,调用方法
this.__callFunction(module, method, args);
);
//这个会调用 JSTimer 的callImmediates
return this.flushedQueue();
__callFunction(module: string, method: string, args: mixed[]): void
this._lastFlush = Date.now();
this._eventLoopStartTime = this._lastFlush;
...
//从_lazyCallableModules 中取出对应的module
const moduleMethods = this.getCallableModule(module);
...
//moduleMethods[method] 方法名,用 apply 来调用
moduleMethods[method].apply(moduleMethods, args);
至此,java-> js 的通信流程就分析完了,下面来看看js->java的
二、 JS->Java 通信
在开始分析之前,首先要知道,JS->Java 通信是两种的,表现在通信中的差异,下面会介绍一些。具体原因是传递的 JavaScriptExecutorFactory不同(可在ReactNativeHost中设置),但是 如果你本地跑了metro,那么会自动使用 ProxyJavaScriptExecutor(可自己翻阅代码 ReactInstanceManager 的onReloadWithJSDebugger函数)
我们在JS中调用Java的方法,是通过这样的形式
- 继承 ReactContextBaseJavaModule,使用@ReactMethod 注解 暴露给js的方法,
- 把 上一步创建的类 添加到ReactPackage
- 把ReactPackage添加到ReactNativeHost 的getPackages() 的返回值中
- 在js侧,通过
NativeModules.xxxModuleName.xxxMethodName()
调用到原生
下面我们就从 NativeModules.xxxModuleName
这样的代码开始分析:
先来看张图,对整体过程,有个感觉。图片来源
首先需要搞清楚,这个NativeModules 是什么,在js侧,代码路径 Libraries/BatchedBridge/NativeModules.js
2.1、 NativeModules.js
let NativeModules: [moduleName: string]: $FlowFixMe, ... = ;
if (global.nativeModuleProxy)
//nativeModuleProxy 的赋值,是在 JSIExecutor::initializeRuntime() ,详见上篇文章
//如果执行到这里,那么JavaScriptExecutor 使用的是JSIExecutor
NativeModules = global.nativeModuleProxy;
else if (!global.nativeExtensions)
//如果你是debug,本地跑metro服务,会运行到这里
const bridgeConfig = global.__fbBatchedBridgeConfig;
const defineLazyObjectProperty = require('../Utilities/defineLazyObjectProperty');
(bridgeConfig.remoteModuleConfig || []).forEach(
(config: ModuleConfig, moduleID: number) =>
// Initially this config will only contain the module name when running in JSC. The actual
// configuration of the module will be lazily loaded.
const info = genModule(config, moduleID);
if (!info)
return;
if (info.module)
NativeModules[info.name] = info.module;
// If there's no module config, define a lazy getter
else
defineLazyObjectProperty(NativeModules, info.name,
get: () => loadModule(info.name, moduleID),
);
,
);
module.exports = NativeModules;
不同的JavaScriptExecutor 最终都会执行到genModule,下面我们分析JSIExecutor 分支 的流程。
简单回忆一下,在 JSIExecutor::initializeRuntime()
中设置了 global.nativeModuleProxy
的值,
runtime_->global().setProperty(
*runtime_,
"nativeModuleProxy",
Object::createFromHostObject(
*runtime_, std::make_shared<NativeModuleProxy>(nativeModules_)));
可以看到global.nativeModuleProxy
是NativeModuleProxy 类型,在获取对应的module,会执行NativeModuleProxy的get方法,下面来到c++层:
2.2、 JSIExecutor::NativeModuleProxy
class JSIExecutor::NativeModuleProxy : public jsi::HostObject
public:
NativeModuleProxy(std::shared_ptr<JSINativeModules> nativeModules)
: weakNativeModules_(nativeModules)
Value get(Runtime &rt, const PropNameID &name) override
...
auto nativeModules = weakNativeModules_.lock();
if (!nativeModules)
return nullptr;
//调用 JSIExecutor的getModule
return nativeModules->getModule(rt, name);
...
private:
std::weak_ptr<JSINativeModules> weakNativeModules_;
;
2.3、 JSIExecutor # getModule
Value JSINativeModules::getModule(Runtime &rt, const PropNameID &name)
if (!m_moduleRegistry)
return nullptr;
std::string moduleName = name.utf8(rt);
//先从缓存中查找,如果已经被加载过,直接返回
const auto it = m_objects.find(moduleName);
if (it != m_objects.end())
...
return Value(rt, it->second);
// 缓存没有命中,创建module。
// 创建module,解析module对应的method,为每个method创建对应的js方法(用于把对应的调用信息,发送到js线程队列)
auto module = createModule(rt, moduleName);
...省略判空逻辑...
// 把创建的module缓存起来
auto result = m_objects.emplace(std::move(moduleName), std::move(*module)).first;
Value ret = Value(rt, result->second);
//直接返回给js了
return ret;
createModule 是核心,下面来分析
2.4 JSINativeModules::createModule
folly::Optional<Object> JSINativeModules::createModule(
Runtime &rt,
const std::string &name)
//获取到js层的__fbGenNativeModule ,在NativeModule.js中有这句 global.__fbGenNativeModule = genModule;
if (!m_genNativeModuleJS)
m_genNativeModuleJS =
rt.global().getPropertyAsFunction(rt, "__fbGenNativeModule");以上是关于React Native 源码分析——通信机制的主要内容,如果未能解决你的问题,请参考以下文章