这篇文章针对于对rn有些基础的同学,没有基础的同学可以先了解一下rn以后再看这篇文章。要想深入理解 React Native 的工作原理,有两个部分的源码需要阅读:rn的初始化和java与js通信的过程,首先分析rn的初始化过程。
一、RN的初始化过程
从官方rn的demo入手,看rn是如何一步步如何初始化的:
public class MainActivity extends ReactActivity {
/**
* 用来返回要显示的js端的组件的名称,这个要和js端注册的Component名称一一对应。
*/
@Override
protected String getMainComponentName() {
return "AwesomeProject";
}
}
MainActivity继承于ReactActivity,ReactActivity是rn中页面显示的入口,负责页面的显示,下面就进入源码看它怎么实现的:
进入ReactActivity的onCreate中发现ReactActivity只是一个空壳子,所有的逻辑都交给ReactActivityDelegate类实现,这是典型的代理模式,这样做的好处:1、实现和接口分开;2、可以在FragmentActivity也同样可以使用,不用维护两套逻辑。
接着查看ReactActivityDelegate的onCreate方法,这个函数中最重要的逻辑就是loadApp方法:
protected void loadApp(String appKey) {
...
mReactRootView = createRootView();
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
appKey,
getLaunchOptions());
getPlainActivity().setContentView(mReactRootView);
}
这个函数主要实现两个功能:
1、创建ReactRootView,并将这个view设置为activity的根view。ReactRootView继承FrameLayout,它主要负责native端事件(键盘事件、touch事件、页面大小变化等)的监听并将结果传递给js端以及负责页面元素的重新绘制。
2、调用ReactRootView的startReactApplication方来,来启动整个rn流。
startReactApplication函数:
public void startReactApplication(
ReactInstanceManager reactInstanceManager,
String moduleName,
@Nullable Bundle launchOptions) {
...
//rn的上下文没有创建
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
//创建rn的上下文ReactContext对象
mReactInstanceManager.createReactContextInBackground();
}
if (mWasMeasured) {
attachToReactInstanceManager();
}
}
由于是第一次调用,hasStartedCreatingInitialContext函数返回false,此时,进入mReactInstanceManager.createReactContextInBackground()函数,在分析这个函数之前,先介绍一下两个类:
ReactInstanceManager,rn的java端的控制器,它主要的功能是创建和管理CatalystInstance实例并和ReactActivity的生命周期保持一致。
CatalystInstance:jsc桥梁接口类,为java和js相互通信提供环境。
进入createReactContextInBackground函数,发现函数最后会走到recreateReactContextInBackgroundInner()函数。
recreateReactContextInBackgroundInner函数:
private void recreateReactContextInBackgroundInner() {
...
//是否使用开发者模式,默认情况下是true
if (mUseDeveloperSupport && mJSMainModuleName != null) {
final DeveloperSettings devSettings = mDevSupportManager.getDevSettings();
// 本地bundle文件是最新的并且不开启远程js调试,用本地的bundle文件
if (mDevSupportManager.hasUpToDateJSBundleInCache() &&
!devSettings.isRemoteJSDebugEnabled()) {
onJSBundleLoadedFromServer();
} else if (mBundleLoader == null) {
mDevSupportManager.handleReloadJS();
} else {
//调用okHttp下载bundle文件
mDevSupportManager.isPackagerRunning(
new DevServerHelper.PackagerStatusCallback() {
@Override
public void onPackagerStatusFetched(final boolean packagerIsRunning) {
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
if (packagerIsRunning) {
mDevSupportManager.handleReloadJS();
} else {
devSettings.setRemoteJSDebugEnabled(false);
recreateReactContextInBackgroundFromBundleLoader();
}
}
});
}
});
}
return;
}
recreateReactContextInBackgroundFromBundleLoader();
}
recreateReactContextInBackgroundFromBundleLoader函数:
private void recreateReactContextInBackgroundFromBundleLoader() {
recreateReactContextInBackground(
new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()),
mBundleLoader);
}
JSCJavaScriptExecutor:是JavaScriptExecutor的子类,是js执行器。
JSCJavaScriptExecutor.Factory:工厂模式,产生JSCJavaScriptExecutor实例。
public class JSCJavaScriptExecutor extends JavaScriptExecutor {
public static class Factory implements JavaScriptExecutor.Factory {
private ReadableNativeArray mJSCConfig;
public Factory(WritableNativeMap jscConfig) {
array.pushMap(jscConfig);
mJSCConfig = array;
}
//创建JSCJavaScriptExecutor实例
@Override
public JavaScriptExecutor create() throws Exception {
return new JSCJavaScriptExecutor(mJSCConfig);
}
}
public JSCJavaScriptExecutor(ReadableNativeArray jscConfig) {
super(initHybrid(jscConfig));
}
private native static HybridData initHybrid(ReadableNativeArray jscConfig);
}
JSCJavaScriptExecutor的构造函数中调用initHybridn函数,这个函数在rn中反复的出现,它的主要作用是找到于java相对应的c++类并调用其构造方法生成对象,把new出来对象的地址放到java的HybridData对象中。其中与JSCJavaScriptExecutor对应的C++类是JSCJavaScriptExecutorHolder(后面再介绍)。接着往下看,发现函数最后会调用ReactContextInitAsyncTask的doInBackground方法:
@Override
protected Result doInBackground(ReactContextInitParams... params) {
...
try {
//创建JavaScriptExecutor实例
JavaScriptExecutor jsExecutor = params[0].getJsExecutorFactory().create();
return Result.of(createReactContext(jsExecutor, params[0].getJsBundleLoader()));
} catch (Exception e) {
return Result.of(e);
}
}
createReactContext函数比较长,此处选择比较重要的功能进行分析,剩下的可以自自行阅读,在分析代码之前,先看几个重要的类:
JSBundleLoader:bundle.js文件加载器,在rn中有三种加载方式:1、加载本地文件;2、加载网络文件,并将文件缓存;3、加载网络文件,用于debug调试。
ModuleSpec:NativeModule的包装类,主要是为了实现module的懒加载,由于rn中native module比较多,为了节省成本,rn中采用时懒加载的策略,只有相应的module使用时才进行创建。
JavaScriptModule接口类,用于java调用js的接口,在rn中没有实现类,具体如何使用后面再介绍。
JavaScriptModuleRegistry:JavaScriptModule的注册表。
NativeModuleRegistry:NativeModule的注册表,用于管理NativeModule列表。
NativeModule:java暴露给js调用的api接口,如果想创建自己的module,需要继承这个接口。
ReactPackage:组件配置接口类,通过createNativeModules、createJSModules和createViewManagers等API去创建本地模块,JS模块及视图组件等。ReactPackage分为rn核心的CoreModulesPackage和业务方可选的基础MainReactPackage类,其中CoreModulesPackage封装了大部分通信功能。
createReactContext函数:
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
...
//初始化ReactApplicationContext实例
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
try {
CoreModulesPackage coreModulesPackage =
new CoreModulesPackage(
this,
mBackBtnHandler,
mUIImplementationProvider,
mLazyViewManagersEnabled);
//将ReactPackage配置的modeles添加到nativeRegistryBuilder和jsModulesBuilder中
processPackage(
coreModulesPackage,
reactContext,
moduleSpecs,
reactModuleInfoMap,
jsModulesBuilder);
} finally {
...
}
...
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSModuleRegistry(jsModulesBuilder.build())
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
final CatalystInstance catalystInstance;
try {
catalystInstance = catalystInstanceBuilder.build();
} finally {
...
}
reactContext.initializeWithInstance(catalystInstance);
catalystInstance.runJSBundle();
return reactContext;
}
这个函数主要有三个功能:
1、根据ReactPackage生成js module和native module的注册表
2、创建CatalystInstance实例
3、加载bundle文件
生成注册表代码比较简单,这里就不做介绍了,下面我们看CatalystInstanceImpl的构造函数:
private CatalystInstanceImpl(
final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,
final JavaScriptExecutor jsExecutor,
final NativeModuleRegistry registry,
final JavaScriptModuleRegistry jsModuleRegistry,
final JSBundleLoader jsBundleLoader,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
...
//native函数,主要是为了将创建C++实例并将指针地址保存到java中
mHybridData = initHybrid();
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
ReactQueueConfigurationSpec,
new NativeExceptionHandler());
mBridgeIdleListeners = new CopyOnWriteArrayList<>();
mJavaRegistry = registry;
mJSModuleRegistry = jsModuleRegistry;
mJSBundleLoader = jsBundleLoader;
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
mTraceListener = new JSProfilerTraceListener(this);
initializeBridge(
new BridgeCallback(this),
jsExecutor,
mReactQueueConfiguration.getJSQueueThread(),
mReactQueueConfiguration.getNativeModulesQueueThread(),
mJavaRegistry.getModuleRegistryHolder(this));
mMainExecutorToken = getMainExecutorToken();
}
构造函数有有两个重要的参数:
1、ReactQueueConfigurationSpec:用于配置消息线程,在rn中有三个消息线程:UI线程、JS线程、Native线程,其中native调用js的代码会JS线程运行,JS调用native的代码会在Native线程中执行。
2、ModuleRegistryHolder:Native Module的封装类,将java层Native Module信息传递给c++层。
public class ModuleRegistryHolder {
private final HybridData mHybridData;
private static native HybridData initHybrid(
CatalystInstanceImpl catalystInstanceImpl,
Collection javaModules,
Collection cxxModules);
public ModuleRegistryHolder(CatalystInstanceImpl catalystInstanceImpl,
Collection javaModules,
Collection cxxModules) {
mHybridData = initHybrid(catalystInstanceImpl, javaModules, cxxModules);
}
}
上面已经说过initHybrid的工作,此处会在创建C++中ModuleRegistryHolder对象。接着调用initializeBridge函数来初始化,initializeBridge是native方法:
CatalystInstanceImpl.cpp
void CatalystInstanceImpl::initializeBridge(
jni::alias_ref callback,
// This executor is actually a factory holder.
JavaScriptExecutorHolder* jseh,
jni::alias_ref jsQueue,
jni::alias_ref moduleQueue,
ModuleRegistryHolder* mrh) {
instance_->initializeBridge(folly::make_unique(callback),
jseh->getExecutorFactory(),
folly::make_unique(jsQueue),
folly::make_unique(moduleQueue),
mrh->getModuleRegistry());
}
JavaScriptExecutorHolder:对应于java层中的JavaScriptExecutor的C++对象,getExecutorFactory()返回JSCExecutorFactory对象,用于生成js解析器。
ModuleRegistryHolder:java中native module的包装类,其构造函数:
ModuleRegistryHolder.cpp
ModuleRegistryHolder::ModuleRegistryHolder(
CatalystInstanceImpl* catalystInstanceImpl,
jni::alias_ref::javaobject> javaModules,
jni::alias_ref::javaobject> cxxModules) {
std::vector> modules;
std::weak_ptr winstance(catalystInstanceImpl->getInstance());
for (const auto& jm : *javaModules) {
modules.emplace_back(folly::make_unique(jm));
}
for (const auto& cm : *cxxModules) {
modules.emplace_back(
folly::make_unique(winstance, std::move(cthis(cm)->getModule())));
}
registry_ = std::make_shared(std::move(modules));
}
}
将java层的对象分装成JavaNativeModule和CxxNativeModule对象,并将生成的对象注册到ModuleRegistry对象中,ModuleRegistry和上面提高的NativeModuleRegistry功能相似,是C++端module的注册表。继续往下走,会调用Instance的initializeBridge函数:
Instance.cpp
void Instance::initializeBridge(
std::unique_ptr callback,
std::shared_ptr jsef,
std::shared_ptr jsQueue,
std::unique_ptr nativeQueue,
std::shared_ptr moduleRegistry) {
callback_ = std::move(callback);
jsQueue->runOnQueueSync(
[this, &jsef, moduleRegistry, jsQueue,
nativeQueue=folly::makeMoveWrapper(std::move(nativeQueue))] () mutable {
nativeToJsBridge_ = folly::make_unique(
jsef.get(), moduleRegistry, jsQueue, nativeQueue.move(), callback_);
std::lock_guard lock(m_syncMutex);
m_syncReady = true;
m_syncCV.notify_all();
});
}
MessageQueueThread对应于java层MessageQueueThread,进行线程转换变成js thread,在这个函数会调用NativeToJsBridge的构造函数,NativeToJsBridge:Native调用JS的桥梁。
NativeToJsBridge.cpp
NativeToJsBridge::NativeToJsBridge(
JSExecutorFactory* jsExecutorFactory,
std::shared_ptr registry,
std::shared_ptr jsQueue,
std::unique_ptr nativeQueue,
std::shared_ptr callback)
: m_destroyed(std::make_shared(false))
, m_mainExecutorToken(callback->createExecutorToken())
, m_delegate(
std::make_shared(
this, registry, std::move(nativeQueue), callback)) {
std::unique_ptr
mainExecutor =
jsExecutorFactory->createJSExecutor(m_delegate, jsQueue);
m_mainExecutor = mainExecutor.get();
registerExecutor(m_mainExecutorToken, std::move(mainExecutor), jsQueue);
}
m_delegate是JsToNativeBridge,用于JS调用Native函数,和NativeToJsBridge一起作为连接java和js通信的桥梁。这个构造函数的主要是创建js的执行器,这里的mainExecutor对象对应于JSCExecutor对象,JSCExecutor构造函数中对js的执行环境进行初始化,并且向JavaScriptCore中注册了几个c++的方法供js端调用,这里就不做介绍了,有兴趣的可以自己阅读代码。到这里initializeBridge整个函数就全部介绍完毕了。
下面回到ReactInstanceManager中的createReactContext函数中,接着调用CatalystInstanceImpl的runJSBundle方法,接着看runJSBundle函数,发现runJSBundle函数会调用JSBundleLoader的loadScript方法,前面我们介绍过JSBundleLoader有三种方式,我们假设使用的时文件加载的方式,跟着函数一步步走下去,最后会走到CatalystInstanceImpl的loadScriptFromFile函数,这个又是一个native方法,查看CatalystInstanceImpl.cpp的代码,最后进入NativeToJsBridge.cpp中loadApplication方法:
NativeToJsBridge.cpp
void NativeToJsBridge::loadApplication(
std::unique_ptr unbundle,
std::unique_ptr startupScript,
std::string startupScriptSourceURL) {
//进行线程转换,把函数抛到js thread的队列中
runOnExecutorQueue(
m_mainExecutorToken,
[unbundleWrap=folly::makeMoveWrapper(std::move(unbundle)),
startupScript=folly::makeMoveWrapper(std::move(startupScript)),
startupScriptSourceURL=std::move(startupScriptSourceURL)]
(JSExecutor* executor) mutable {
auto unbundle = unbundleWrap.move();
if (unbundle) {
executor->setJSModulesUnbundle(std::move(unbundle));
}
executor->loadApplicationScript(std::move(*startupScript),
std::move(startupScriptSourceURL));
});
}
JSCExecutor.cpp中的loadApplicationScript函数:
JSCExecutor.cpp
void JSCExecutor::loadApplicationScript(std::unique_ptr script, std::string sourceURL) {
//JavaScriptCore函数,执行js代码
evaluateScript(m_context, jsScript, jsSourceURL);
if (m_delegate) {
bindBridge();
flush();
}
}
m_delegate为上面介绍的JsToNativeBridge对象,这里不为null,后面就会执行bindBridge函数:
JSCExecutor.cpp
void JSCExecutor::bindBridge() throw(JSException) {
auto global = Object::getGlobalObject(m_context);
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
if (batchedBridgeValue.isUndefined()) {
throwJSExecutionException("Could not get BatchedBridge, make sure your bundle is packaged correctly");
}
auto batchedBridge = batchedBridgeValue.asObject();
m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();
m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();
}
这个函数主要实现下面几个功能:
1、从js执行环境中取出全局变量__fbBatchedBridge放到global变量中。
2、将global中某些特定的函数对象映射到C++对象中,这样我们就可以通过C++对象调用js的代码,假设我们想要调用js端__fbBatchedBridge的flushQueue方法,在C++中就可以使用m_flushedQueueJS->callAsFunction()就可以实现,那么__fbBatchedBridge在js端到底是个什么东西那?
查看js代码,在BatchBridge.js中找到了__fbBatchedBridge的定义
const MessageQueue = require('MessageQueue');
const BatchedBridge = new MessageQueue();
...
Object.defineProperty(global, '__fbBatchedBridge', {
configurable: true,
value: BatchedBridge,
});
从上面可以看出__fbBatchedBridge就是MessageQueue对象,后面再介绍MessageQueue。
到这里整个rn的初始化和bundle加载就基本上介绍完毕了,下面介绍一下java和js之间如何通信的。
二、java和js之间通信的过程
2.1 java调用js过程
举一个列子:假设我们想要执行js端AppRegistry.runApplication()函数,我们应该怎么办那?
首先要拿到ReactContext实例,然后调用getJSModule方法拿到AppRegistry实例,最后调用runApplication方法就可以了,很简单吧,那么看代码里面如何实现的?
ReactContext.getJSModule函数:
public T getJSModule(Class jsInterface) {
...
return mCatalystInstance.getJSModule(jsInterface);
}
ReactContext中的getJSModule很简单,直接调用CatalystInstanceImpl的JavaScriptModule方法:
@Override
public T getJSModule(ExecutorToken executorToken, Class jsInterface) {
return Assertions.assertNotNull(mJSModuleRegistry)
.getJavaScriptModule(this, executorToken, jsInterface);
}
调用JavaScriptModuleRegistry的getJavaScriptModule方法,
JavaScriptModuleRegistry前面提过,是管理js module的注册表,接着看一下内部实现:
JavaScriptModuleRegistry.java
public synchronized T getJavaScriptModule(
CatalystInstance instance,
ExecutorToken executorToken,
Class moduleInterface) {
HashMap, JavaScriptModule> instancesForContext =
mModuleInstances.get(executorToken);
if (instancesForContext == null) {
instancesForContext = new HashMap<>();
mModuleInstances.put(executorToken, instancesForContext);
}
JavaScriptModule module = instancesForContext.get(moduleInterface);
if (module != null) {
return (T) module;
}
JavaScriptModuleRegistration registration =
Assertions.assertNotNull(
mModuleRegistrations.get(moduleInterface),
"JS module " + moduleInterface.getSimpleName() + " hasn't been registered!");
JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
moduleInterface.getClassLoader(),
new Class[]{moduleInterface},
new JavaScriptModuleInvocationHandler(executorToken, instance, registration));
instancesForContext.put(moduleInterface, interfaceProxy);
return (T) interfaceProxy;
}
这个函数的流程比较简单:首先判断js module是否已经生成了,如果已经生成了就直接返回内存中的对象,假设我们是第一次调用,那么就会执行后面的方法,使用java的动态代理来生成js module的实例,关于动态代理,我们知道实际的处理逻辑都在InvocationHandler中invoke方法中执行,直接看InvocationHandler的实现类JavaScriptModuleInvocationHandler中的invoke函数:
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
ExecutorToken executorToken = mExecutorToken.get();
...
NativeArray jsArgs = args != null ? Arguments.fromJavaArgs(args) : new WritableNativeArray();
mCatalystInstance.callFunction(
executorToken,
mModuleRegistration.getName(),
method.getName(),
jsArgs
);
return null;
}
invoke方法比较简单,直接调用mCatalystInstance的callFunction(...)方法,看着这里就比较明白了,调用AppRegistery的runApplictaion(args)方法实际上是调用CatalystInstance的callFuction(,"AppRegistry","runApplictaion",args)方法。其中NativeArray类,用于rn中java端和C++端进行数据传递的数据结构,主要目的是节省内存,便于管理,有兴趣的可以自行阅读源码。
这时大概java层流程就大概清楚了,我们看C++层,一步步往下走,最后会到NativeToJsBridge中的callFunction函数:
NativeToJsBridge.cpp
void NativeToJsBridge::callFunction(
ExecutorToken executorToken,
std::string&& module,
std::string&& method,
folly::dynamic&& arguments) {
runOnExecutorQueue(executorToken, [module = std::move(module), method = std::move(method), arguments = std::move(arguments), tracingName = std::move(tracingName), systraceCookie] (JSExecutor* executor) {
executor->callFunction(module, method, arguments);
});
}
runOnExecutorQueue的函数讲过,不清楚请往前看,后面会执行JSExecutor的callFunction函数,其中module是想要调用js端对象的名称,method是js对象中的方法名称,arguments是方法参数。JSCExecutor::callFunction函数:
JSCExecutor.cpp
void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
auto result = [&] {
try {
return m_callFunctionReturnFlushedQueueJS->callAsFunction({
Value(m_context, String::createExpectingAscii(m_context, moduleId)),
Value(m_context, String::createExpectingAscii(m_context, methodId)),
Value::fromDynamic(m_context, std::move(arguments))
});
} catch (...) {
}
}();
callNativeModules(std::move(result));
}
m_callFunctionReturnFlushedQueueJS是js全局对象_fbBatchedBridge中的callFunctionReturnFlushedQueue对象,对它调用callAsFunction方法来,相当于执行_fbBatchedBridge.callFunctionReturnFlushedQueue函数。callNativeModules函数主要的功能是将js的执行结果返回给native函数,这里需要注意的是这个过程不是线性的,前面说过_fbBatchedBridge是MessageQueue对象,查看MessageQueue.js中的callFunctionReturnFlushedQueue函数:
MessageQueue.js
MessageQueue
callFunctionReturnFlushedQueue(module: string, method: string, args: Array) {
//es6的新特性箭头函数
guard(() => {
this.__callFunction(module, method, args);
this.__callImmediates();
});
return this.flushedQueue();
}
MessageQueued的__callFunction函数:
__callFunction(module: string, method: string, args: Array) {
...
const moduleMethods = this._callableModules[module];
...
const result = moduleMethods[method].apply(moduleMethods, args);
...
return result;
}
MessageQueue内部有一个js模块的配置数组,每个js模块会在js加载时将自己注册到MessageQueue配置表_callableModules中,它以moduleId为key,module对象为value进行存储,此时这个函数的功能就是调用js中module模块中method方法。
到这里还有个问题:这个配置表是如何生成的?这里还是以AppRegistry为例,查看AppRegistry.js的代码:
BatchedBridge.registerCallableModule(
'AppRegistry',
AppRegistry
);
在加载AppRegistry.js时会调用BatchedBridge.registerCallableModule方法,前面我们讲过BatchedBridge对象就是MessageQueue对象,会调用MessageQueue.registerCallableModule函数:
registerCallableModule(name: string, module: Object) {
this._callableModules[name] = module;
}
js端配置表如何生成到这里也就结束了,总结来说,就是js文件加载时每个模块会将通过key和value的形式注册到MessageQueue中,工java端调用。在java调用接口时需要注意的时模块名称必须和js注册的名称完全一样,否则就会找不到这个module,执行不成功。到这里整个java调用js的过程就就结束了。接着介绍js到native的调用过程。
2.2 js调用native过程
还是一个例子来介绍这一过程,在Android中有个常用的控件Toast,那么js中如何调用native的Toast那?在rn中对这个过程进行了封装,我们想显示hello world,在js中只需要调用ToastAndroid.show('hello world', ToastAndroid.SHORT);就可以了,那么它是如何实现的那?查看ToastAndroid.js的代码:
ar RCTToastAndroid = require('NativeModules').ToastAndroid;
var ToastAndroid = {
SHORT: RCTToastAndroid.SHORT,
LONG: RCTToastAndroid.LONG,
TOP: RCTToastAndroid.TOP,
BOTTOM: RCTToastAndroid.BOTTOM,
CENTER: RCTToastAndroid.CENTER,
show: function (
message: string,
duration: number
): void {
RCTToastAndroid.show(message, duration);
},
showWithGravity: function (
message: string,
duration: number,
gravity: number,
): void {
RCTToastAndroid.showWithGravity(message, duration, gravity);
},
};
module.exports = ToastAndroid;
调用ToastAndroid的show函数就会RCTToastAndroid中的show函数,RCTToastAndroid是NativeModules.js中NativeModules对象,查看NativeModules.js的代码NativeModules是一个空的hashmap对象,key为moduleName的名称,value为module的对象,下面看一下NativeModules是如何生成的:
NativeModules.js
if (global.nativeModuleProxy) {
NativeModules = global.nativeModuleProxy;
} else {
const bridgeConfig = global.__fbBatchedBridgeConfig;
(bridgeConfig.remoteModuleConfig || []).forEach((config: ModuleConfig, moduleID: number) => {
const info = genModule(config, moduleID);
if (!info) {
return;
}
if (info.module) {
NativeModules[info.name] = info.module;
} else {
defineLazyObjectProperty(NativeModules, info.name, {
get: () => loadModule(info.name, moduleID)
});
}
});
}
上面解析过BatchBridge.js,里面对全局的global对象进行赋值为MessageQueue,查看MessageQueue是否nativeModuleProxy对象,发现整个js中没有找到nativeModuleProxy,但是有一点我们需要注意的是我们在JSCExecutor注册了很多C++的函数供js端调用,global中函数对象包含JSCExecutor注册的C++中的函数,在JSCExecutor.cpp中nativeModuleProxy对应这个C++端的getNativeModule函数,查看getNativeModule函数:
JSCExecutor.cpp
JSValueRef JSCExecutor::getNativeModule(JSObjectRef object, JSStringRef propertyName) {
...
return m_nativeModules.getModule(m_context, propertyName);
}
m_nativeModules是SCJNativeModules类对象,getModule函数:
SCJNativeModules
JSValueRef JSCNativeModules::getModule(JSContextRef context, JSStringRef jsName) {
std::string moduleName = String::ref(context, jsName).str();
//缓存中是否存在,如果存在直接返回缓存的配置表
const auto it = m_objects.find(moduleName);
if (it != m_objects.end()) {
return static_cast(it->second);
}
//如果缓存中没有则生成
auto module = createModule(moduleName, context);
if (!module.hasValue()) {
return Value::makeUndefined(context);
}
...
return static_cast(result->second);
}
createModule函数:
JSCNativeModules.cpp
folly::Optional
生成配置表的函数比较简单,这里不做分析,感兴趣的同学可以自行阅读代码,生成配置结构大致如下:
生成的配置表的格式大概是:
{ 'module名称', ,{ '常量key':'常量value'} { 'show', 'showWithGravity' },
}
后面会回调给global对象的__fbGenNativeModule方法,生成NativeModules列表,这里就不做介绍,解感兴趣的可以自行阅读源码,RCTToastAndroid.show(message, duration);最后实际上会变成MessageQueue.enqueueNativeCall(1,"show",{"hello world",0},null,null)函数;
MessageQueue.js中的enqueueNativeCall函数:
MessageQueue.js
enqueueNativeCall(moduleID: number, methodID: number, params: Array < any > , onFail: ? Function, onSucc : ? Function) {
...
//判断是否可以调用native的方法,当两个相继调用的函数超过MIN_TIME_BETWEEN_FLUSHES_MSs才会调用nativeFlushQueueImmediate方法
const now = new Date().getTime();
if (global.nativeFlushQueueImmediate &&
now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) {
global.nativeFlushQueueImmediate(this._queue);
this._queue = [
[],
[],
[], this._callID
];
this._lastFlush = now;
}
...
}
nativeFlushQueueImmediate是C++注册的本地函数,对应于JSCExecutor::nativeFlushQueueImmediate函数,一步步往下走下去最后会执行java端JavaModuleWrapper类中的invoke方法:
JavaModuleWrapper.java
@DoNotStrip
public void invoke(ExecutorToken token, int methodId, ReadableNativeArray parameters) {
mMethods.get(methodId).invoke(mCatalystInstance, token, parameters);
}
mMethods:native module中包含的的函数;
methodId:js端要执行的函数名称;
parameters:js端传递过来的参数;
这个方法最后会调用ToastModule的show方法,整个js调用native流程到这里就介绍完毕了。