關於做者javascript
郭孝星,程序員,吉他手,主要從事Android平臺基礎架構方面的工做,歡迎交流技術方面的問題,能夠去個人Github提issue或者發郵件至guoxiaoxingse@163.com與我交流。前端
文章目錄java
從2016年中開始,我司開始籌措推動React Native在全公司的推廣使用,從最基礎的基礎框架搭建開始,到各類組件庫、開發工具的完善,經歷了諸多波折,也累積了不少經驗。今年的工做也 立刻接近尾聲,打算寫幾篇文章來對這一年多的實踐經驗作個總結。讀者有什麼問題或者想要交流的地方,能夠去vinci提issue。node
預先善其事,必先利其器。開篇第一篇文章咱們仍是從React Native實現原理講起,事實上原理分析的文章以前就有寫過,可是鑑於最新的版本0.52.0源碼有 很多的改動,咱們就再從新溫習一下,這樣過渡到後續的內容,才更加容易理解。react
源碼地址:https://github.com/facebook/react-nativec++
當你拿到React Native的源碼的時候,它的目錄結構是這樣的:程序員
整體來看,整套React Native框架分爲三層,以下圖所示:github
注:JSCore,即JavaScriptCore,JS解析的核心部分,IOS使用的是內置的JavaScriptCore,Androis上使用的是 https://webkit.org 家的jsc.so。web
經過上面的分析,咱們理解了React Native的框架結構,除此以外,咱們還要理解整套框架裏的一些重要角色,以下所示:
以上即是整套框架中關鍵的角色,值得一提的是,當頁面真正渲染出來之後,它實際上仍是Native代碼,React Native的做用就是把JavaScript代碼映射成Native代碼以及實現兩端 的通訊,因此咱們在React Native基礎框架搭建的過程當中,指導思路之一就是弱化Native與RN的邊界與區別,讓業務開發組感覺不到二者的區別,從而簡化開發流程。
好,有了對React Native框架的總體理解,咱們來繼續分析一個RN頁面是如何啓動並渲染出來的,這也是咱們關心的主要問題。後續的基礎框架的搭建、JS Bundle分包加載、渲染性能優化 等都會圍繞着着一塊作文章。
咱們知道RN的頁面也是依託Activity,React Native框架裏有一個ReactActivity,它就是咱們RN頁面的容器。ReactActivity裏有個ReactRootView,正如它的名字那樣,它就是 ReactActivity的root View,最終渲染出來的view都會添加到這個ReactRootView上。ReactRootView調用本身的startReactApplication()方法啓動了整個RN頁面,在啓動的過程 中先去建立頁面上下文ReactContext,而後再去加載、執行並將JavaScript映射成Native Widget,最終一個RN頁面就顯示在了用戶面前。
整個RN頁面的啓動流程圖以下所示:
這個流程看起來有點長,但實際上重要的東西並很少,咱們當前只須要重點關注四個問題:
咱們先來看第一個問題,咱們都知道要使用RN頁面,就須要先初始化一個ReactNativeHost,它是一個抽象類,ReactInstanceManager就是在這個類裏被建立的,以下所示:
public abstract class ReactNativeHost {
protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
//應用上下文
.setApplication(mApplication)
//JSMainModuleP至關於應用首頁的js Bundle,能夠傳遞url從服務器拉取js Bundle
//固然這個只在dev模式下可使用
.setJSMainModulePath(getJSMainModuleName())
//是否開啓dev模式
.setUseDeveloperSupport(getUseDeveloperSupport())
//紅盒的回調
.setRedBoxHandler(getRedBoxHandler())
//JS執行器
.setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
//自定義UI實現機制,這個咱們通常用不到
.setUIImplementationProvider(getUIImplementationProvider())
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE);
//添加咱們外面設置的Package
for (ReactPackage reactPackage : getPackages()) {
builder.addPackage(reactPackage);
}
//獲取js Bundle的加載路徑
String jsBundleFile = getJSBundleFile();
if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
}
return builder.build();
}
}
複製代碼
咱們再來看第二個問題,ReactContext建立流程序列圖以下所示:
能夠發現,最終建立ReactContext是createReactContext()方法,咱們來看看它的實現。
public class ReactInstanceManager {
private ReactApplicationContext createReactContext( JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader) {
Log.d(ReactConstants.TAG, "ReactInstanceManager.createReactContext()");
ReactMarker.logMarker(CREATE_REACT_CONTEXT_START);
//ReactApplicationContext是ReactContext的包裝類。
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
//debug模式裏開啓異常處理器,就是咱們開發中見到的調試工具(紅色錯誤框等)
if (mUseDeveloperSupport) {
reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
}
//建立JavaModule註冊表
NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
? mNativeModuleCallExceptionHandler
: mDevSupportManager;
//建立CatalystInstanceImpl的Builder,它是三端通訊的管理類
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
//JS執行器
.setJSExecutor(jsExecutor)
//Java Module註冊表
.setRegistry(nativeModuleRegistry)
//JS Bundle加載器
.setJSBundleLoader(jsBundleLoader)
//Java Exception處理器
.setNativeModuleCallExceptionHandler(exceptionHandler);
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START);
// CREATE_CATALYST_INSTANCE_END is in JSCExecutor.cpp
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
final CatalystInstance catalystInstance;
//構建CatalystInstance實例
try {
catalystInstance = catalystInstanceBuilder.build();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);
}
if (mBridgeIdleDebugListener != null) {
catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
}
if (Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) {
catalystInstance.setGlobalVariable("__RCTProfileIsProfiling", "true");
}
ReactMarker.logMarker(ReactMarkerConstants.PRE_RUN_JS_BUNDLE_START);
//開啓加載執行JS Bundle
catalystInstance.runJSBundle();
//關聯catalystInstance與reactContext
reactContext.initializeWithInstance(catalystInstance);
return reactContext;
}
}
複製代碼
在這個方法裏完成了RN頁面上下文ReactContext的建立,咱們先來看看這個方法的兩個入參:
能夠看到在ReactContext建立的過程當中,主要作了如下幾件事情:
另外一個重要的角色CatalystInstance出現了,前面咱們也說過它是三端通訊的中樞。關於通訊的具體實現咱們會在接下來的通訊機制小節來說述,咱們先來接着看JS的加載過程。
在分析JS Bundle的加載流程以前,咱們先來看一下上面提到CatalystInstance,它是一個接口,其實現類是CatalystInstanceImpl,咱們來看看它的構造方法。
public class CatalystInstanceImpl implements CatalystInstance {
private CatalystInstanceImpl( final ReactQueueConfigurationSpec reactQueueConfigurationSpec, final JavaScriptExecutor jsExecutor, final NativeModuleRegistry nativeModuleRegistry, final JSBundleLoader jsBundleLoader, NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge.");
mHybridData = initHybrid();
//建立三大線程:UI線程、Native線程與JS線程
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
reactQueueConfigurationSpec,
new NativeExceptionHandler());
mBridgeIdleListeners = new CopyOnWriteArrayList<>();
mNativeModuleRegistry = nativeModuleRegistry;
//建立JS Module註冊表實例,這個在之前的代碼版本中是在上面的createReactContext()方法中建立的
mJSModuleRegistry = new JavaScriptModuleRegistry();
mJSBundleLoader = jsBundleLoader;
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
mNativeModulesQueueThread = mReactQueueConfiguration.getNativeModulesQueueThread();
mTraceListener = new JSProfilerTraceListener(this);
Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge before initializeBridge");
//在C++層初始化通訊橋
initializeBridge(
new BridgeCallback(this),
jsExecutor,
mReactQueueConfiguration.getJSQueueThread(),
mNativeModulesQueueThread,
mNativeModuleRegistry.getJavaModules(this),
mNativeModuleRegistry.getCxxModules());
Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge");
mJavaScriptContextHolder = new JavaScriptContextHolder(getJavaScriptContext());
}
}
複製代碼
這個函數的入參大部分咱們都已經很熟悉了,咱們單獨說說這個ReactQueueConfigurationSpec,它用來建立ReactQueueConfiguration的實例,ReactQueueConfiguration 一樣是個接口,它的實現類是ReactQueueConfigurationImpl。
ReactQueueConfiguration的定義以下:
public interface ReactQueueConfiguration {
//UI線程
MessageQueueThread getUIQueueThread();
//Native線程
MessageQueueThread getNativeModulesQueueThread();
//JS線程
MessageQueueThread getJSQueueThread();
void destroy();
}
複製代碼
能夠看着這個接口的做用就是建立三個帶消息隊列的線程:
能夠看到CatalystInstance對象在構建的時候,主要作了兩件事情:
咱們接着來看JS Bundle的加載流程,JS Bundle的加載其實是指C++層完成的,咱們看一下序列圖。
注:JS Bundle有三種加載方式:
從這個序列圖上咱們能夠看出,真正加載執行JS的地方就是JSCExector.cpp的loadApplicationScript()方法。
void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) {
SystraceSection s("JSCExecutor::loadApplicationScript", "sourceURL", sourceURL);
...
switch (jsStatus) {
case JSLoadSourceIsCompiled:
if (!bcSourceCode) {
throw std::runtime_error("Unexpected error opening compiled bundle");
}
//調用JavaScriptCore裏的方法驗證JS是否有效,並解釋執行
evaluateSourceCode(m_context, bcSourceCode, jsSourceURL);
flush();
ReactMarker::logMarker(ReactMarker::CREATE_REACT_CONTEXT_STOP);
ReactMarker::logTaggedMarker(ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());
return;
case JSLoadSourceErrorVersionMismatch:
throw RecoverableError(explainLoadSourceStatus(jsStatus));
case JSLoadSourceErrorOnRead:
case JSLoadSourceIsNotCompiled:
// Not bytecode, fall through.
break;
}
}
...
複製代碼
能夠看到這個方法主要是調用JavaScriptCore裏的evaluateSourceCode()方法驗證JS是否有效,並解釋執行。而後在調用flush()方法層調用JS層的裏 方法執行JS Bundle。
void JSCExecutor::flush() {
SystraceSection s("JSCExecutor::flush");
if (m_flushedQueueJS) {
//調用MessageQueue.js的flushedQueue()方法
callNativeModules(m_flushedQueueJS->callAsFunction({}));
return;
}
// When a native module is called from JS, BatchedBridge.enqueueNativeCall()
// is invoked. For that to work, require('BatchedBridge') has to be called,
// and when that happens, __fbBatchedBridge is set as a side effect.
auto global = Object::getGlobalObject(m_context);
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
// So here, if __fbBatchedBridge doesn't exist, then we know no native calls
// have happened, and we were able to determine this without forcing
// BatchedBridge to be loaded as a side effect.
if (!batchedBridgeValue.isUndefined()) {
// If calls were made, we bind to the JS bridge methods, and use them to
// get the pending queue of native calls.
bindBridge();
callNativeModules(m_flushedQueueJS->callAsFunction({}));
} else if (m_delegate) {
// If we have a delegate, we need to call it; we pass a null list to
// callNativeModules, since we know there are no native calls, without
// calling into JS again. If no calls were made and there's no delegate,
// nothing happens, which is correct.
callNativeModules(Value::makeNull(m_context));
}
}
複製代碼
上面提到flush()方法調用MessageQueue.js的flushedQueue()方法,這是如何作到的呢?🤔。
事實上這是由bindBridge()方法來完成的,bindBridge()從__fbBatchedBridge(__fbBatchedBridge也是被MessageQueue.js設置爲全局變量,供C++層讀取)對象中取出MessageQueue.js裏的四個方法:
並分別存在三個C++變量中:
這樣C++就能夠調用這四個JS方法。
void JSCExecutor::bindBridge() throw(JSException) {
SystraceSection s("JSCExecutor::bindBridge");
std::call_once(m_bindFlag, [this] {
auto global = Object::getGlobalObject(m_context);
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
if (batchedBridgeValue.isUndefined()) {
auto requireBatchedBridge = global.getProperty("__fbRequireBatchedBridge");
if (!requireBatchedBridge.isUndefined()) {
batchedBridgeValue = requireBatchedBridge.asObject().callAsFunction({});
}
if (batchedBridgeValue.isUndefined()) {
throw JSException("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();
});
}
複製代碼
至此,JS Bundle已經加載解析完成,進入MessageQueue.js開始執行。
JS Bundle加載完成之後,前面說的createReactContext()就執行完成了,而後開始執行setupReacContext()方法,綁定ReactContext與ReactRootView。 咱們來看一下它的實現。
public class ReactInstanceManager {
private void setupReactContext(final ReactApplicationContext reactContext) {
//...
//執行Java Module的初始化
catalystInstance.initialize();
//通知ReactContext已經被建立愛女
mDevSupportManager.onNewReactContextCreated(reactContext);
//內存狀態回調設置
mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
//復位生命週期
moveReactContextToCurrentLifecycleState();
ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_START);
synchronized (mAttachedRootViews) {
//將全部的ReactRootView與catalystInstance進行綁定
for (ReactRootView rootView : mAttachedRootViews) {
attachRootViewToInstance(rootView, catalystInstance);
}
}
//...
}
private void attachRootViewToInstance( final ReactRootView rootView, CatalystInstance catalystInstance) {
//...
UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
//將ReactRootView做爲根佈局
final int rootTag = uiManagerModule.addRootView(rootView);
rootView.setRootViewTag(rootTag);
//執行JS的入口bundle頁面
rootView.invokeJSEntryPoint();
//...
}
x
}
複製代碼
setupReactContext()方法主要完成每一個ReactRootView與catalystInstance的綁定,綁定的過程主要作兩件事情:
JS的頁面入口咱們能夠設置mJSEntryPoint來自定義入口,若是不設置則是默認的入口AppRegistry。
private void defaultJSEntryPoint() {
//...
try {
//...
String jsAppModuleName = getJSModuleName();
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
複製代碼
這裏的調用方式實際上就是原生調用JS的方法,它調用的正是咱們很熟悉的AppRegistry.js,AppRegistry.js調用runApplication()開始執行JS頁面的渲染,最終轉換爲 Native UI顯示在手機上。
到此爲止,整個RN頁面的啓動流程就分析完了,咱們接着來看看RN頁面是如何渲染最終顯示在手機上的。
上面咱們也說了,RN頁面的入口通常是AppRegistry.js,咱們就從這個頁面入手開始分析RN頁面的渲染流程。先看一下RN頁面的渲染流程序列圖,以下所示:
這個流程比較長,其實主要是方法的調用鏈多,原理仍是很簡單的,咱們先歸納性的總結一下:
如上圖所示AppRegistry.registerComponent用來註冊組件,在該方法內它會調用AppRegistry.runApplication()來啓動js的渲染流程。AppRegistry.runApplication() 會將傳入的Component轉換成ReactElement,並在外面包裹一層AppContaniner,AppContaniner主要用來提供一些debug工具(例如:紅盒)。
以下所示:
function renderApplication<Props: Object>( RootComponent: ReactClass<Props>, initialProps: Props, rootTag: any ) {
invariant(
rootTag,
'Expect to have a valid rootTag, instead got ', rootTag
);
ReactNative.render(
<AppContainer rootTag={rootTag}> <RootComponent {...initialProps} rootTag={rootTag} /> </AppContainer>, rootTag ); } 複製代碼
咱們拋開函數調用鏈,分析其中關鍵的部分,其餘部分都是簡單的函數調用。
/** * @param {ReactComponent} instance Instance to render. * @param {containerTag} containerView Handle to native view tag */
renderComponent: function( nextElement: ReactElement<*>, containerTag: number, callback?: ?(() => void) ): ?ReactComponent<any, any, any> {
//將RectElement使用相同的TopLevelWrapper進行包裹
var nextWrappedElement = React.createElement(
TopLevelWrapper,
{ child: nextElement }
);
var topRootNodeID = containerTag;
var prevComponent = ReactNativeMount._instancesByContainerID[topRootNodeID];
if (prevComponent) {
var prevWrappedElement = prevComponent._currentElement;
var prevElement = prevWrappedElement.props.child;
if (shouldUpdateReactComponent(prevElement, nextElement)) {
ReactUpdateQueue.enqueueElementInternal(prevComponent, nextWrappedElement, emptyObject);
if (callback) {
ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);
}
return prevComponent;
} else {
ReactNativeMount.unmountComponentAtNode(containerTag);
}
}
if (!ReactNativeTagHandles.reactTagIsNativeTopRootID(containerTag)) {
console.error('You cannot render into anything but a top root');
return null;
}
ReactNativeTagHandles.assertRootTag(containerTag);
//檢查以前的節點是否已經mount到目標節點上,若是有則進行比較處理
var instance = instantiateReactComponent(nextWrappedElement, false);
ReactNativeMount._instancesByContainerID[containerTag] = instance;
// The initial render is synchronous but any updates that happen during
// rendering, in componentWillMount or componentDidMount, will be batched
// according to the current batching strategy.
//將mount任務提交給回調Queue,最終會調用ReactReconciler.mountComponent()
ReactUpdates.batchedUpdates(
batchedMountComponentIntoNode,
instance,
containerTag
);
var component = instance.getPublicInstance();
if (callback) {
callback.call(component);
}
return component;
},
複製代碼
該方法主要作了如下事情:
在分析這個函數以前,咱們先來補充一下React組件相關知識。React組件能夠分爲兩種:
instantiateReactComponent(node, shouldHaveDebugID)方法根據對象的type生成元組件或者複合組件。
/** * Given a ReactNode, create an instance that will actually be mounted. * * @param {ReactNode} node * @param {boolean} shouldHaveDebugID * @return {object} A new instance of the element's constructor. * @protected */
function instantiateReactComponent(node, shouldHaveDebugID) {
var instance;
if (node === null || node === false) {
instance = ReactEmptyComponent.create(instantiateReactComponent);
} else if (typeof node === 'object') {
var element = node;
var type = element.type;
if (typeof type !== 'function' && typeof type !== 'string') {
var info = '';
if (process.env.NODE_ENV !== 'production') {
if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) {
info += ' You likely forgot to export your component from the file ' + 'it\'s defined in.';
}
}
info += getDeclarationErrorAddendum(element._owner);
!false ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s', type == null ? type : typeof type, info) : _prodInvariant('130', type == null ? type : typeof type, info) : void 0;
}
//若是對象的type爲string,則調用ReactHostComponent.createInternalComponent(element)來注入生成組件的邏輯
if (typeof element.type === 'string') {
instance = ReactHostComponent.createInternalComponent(element);
}
//若是是內部元組件,則建立一個type實例
else if (isInternalComponentType(element.type)) {
// This is temporarily available for custom components that are not string
// representations. I.e. ART. Once those are updated to use the string
// representation, we can drop this code path.
instance = new element.type(element);
// We renamed this. Allow the old name for compat. :(
if (!instance.getHostNode) {
instance.getHostNode = instance.getNativeNode;
}
}
//不然,則是用戶建立的複合組件,這個時候建立一個ReactCompositeComponentWrapper實例,該實例用來描述複合組件
else {
instance = new ReactCompositeComponentWrapper(element);
}
//當對象爲string或者number時,調用ReactHostComponent.createInstanceForText(node)來注入組件生成邏輯。
} else if (typeof node === 'string' || typeof node === 'number') {
instance = ReactHostComponent.createInstanceForText(node);
} else {
!false ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Encountered invalid React node of type %s', typeof node) : _prodInvariant('131', typeof node) : void 0;
}
if (process.env.NODE_ENV !== 'production') {
process.env.NODE_ENV !== 'production' ? warning(typeof instance.mountComponent === 'function' && typeof instance.receiveComponent === 'function' && typeof instance.getHostNode === 'function' && typeof instance.unmountComponent === 'function', 'Only React Components can be mounted.') : void 0;
}
// These two fields are used by the DOM and ART diffing algorithms
// respectively. Instead of using expandos on components, we should be
// storing the state needed by the diffing algorithms elsewhere.
instance._mountIndex = 0;
instance._mountImage = null;
if (process.env.NODE_ENV !== 'production') {
instance._debugID = shouldHaveDebugID ? getNextDebugID() : 0;
}
// Internal instances should fully constructed at this point, so they should
// not get any new fields added to them at this point.
if (process.env.NODE_ENV !== 'production') {
if (Object.preventExtensions) {
Object.preventExtensions(instance);
}
}
return instance;
}
複製代碼
該方法根據對象的type生成元組件或者複合組件,具體流程以下:
咱們經過前面的分析,瞭解了整個UI開始渲染的時機,以及js層的整個渲染流程,接下來,咱們開始分析每一個js的組件時怎麼轉換成Android的組件,最終顯示在屏幕上的。
上面咱們提到元組件與複合組件,事實上覆合組件也是遞歸遍歷其中的元組件,而後進行渲染。因此咱們重點關注元組件的生成邏輯。
咱們能夠看到,UI渲染主要經過UIManager來完成,UIManager是一個ReactModule,UIManager.js裏的操做都會對應到UIManagerModule裏來。咱們接着來看看Java層的 渲染流程。
從上圖咱們能夠很容易看出,Java層的組件渲染分爲如下幾步:
Java層與JavaScript層的相互調用都不是直接完成的,而是間接經過C++層來完成的。在介紹通訊機制以前咱們先來理解一些基本的概念。
JavaScript Module註冊表
提及JavaScript Module註冊表,咱們須要先理解3個類/接口:JavaScriptModule、JavaScriptModuleRegistration、JavaScriptModuleRegistry。
Java Module註冊表
要理解Java Module註冊表,咱們一樣也須要理解3個類/接口:NativeModule、ModuleHolder、NativeModuleRegistry。
關於NativeModuleRegistry和JavaScriptModuleRegistry的建立,咱們前面都已經提到管,你們還都記得嗎。
這些都是在CatalystInstanceImpl的構建方法裏經過native方法initializeBridge()傳入了C++層,以下所示:
CatalystInstanceImpl.cpp
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> moduleQueue,
jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject> cxxModules) {
instance_->initializeBridge(folly::make_unique<JInstanceCallback>(callback),
jseh->getExecutorFactory(),
folly::make_unique<JMessageQueueThread>(jsQueue),
folly::make_unique<JMessageQueueThread>(moduleQueue),
buildModuleRegistry(std::weak_ptr<Instance>(instance_),
javaModules, cxxModules));
}
複製代碼
這個方法的參數含義以下所示:
咱們注意這些傳入了兩個集合:
這兩個集合在CatalystInstanceImpl::initializeBridge()被打包成ModuleRegistry傳入Instance.cpp.、,以下所示:
**ModuleRegistryBuilder.cpp**
```java
std::unique_ptr<ModuleRegistry> buildModuleRegistry(
std::weak_ptr<Instance> winstance,
jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject> cxxModules) {
std::vector<std::unique_ptr<NativeModule>> modules;
for (const auto& jm : *javaModules) {
modules.emplace_back(folly::make_unique<JavaNativeModule>(winstance, jm));
}
for (const auto& cm : *cxxModules) {
modules.emplace_back(
folly::make_unique<CxxNativeModule>(winstance, cm->getName(), cm->getProvider()));
}
if (modules.empty()) {
return nullptr;
} else {
return folly::make_unique<ModuleRegistry>(std::move(modules));
}
}
複製代碼
打包好的ModuleRegistry經過Instance::initializeBridge()傳入到NativeToJsBridge.cpp中,並在NativeToJsBridge的構造方法中傳給JsToNativeBridge,之後JS若是調用Java就能夠經過 ModuleRegistry來進行調用。
這裏的NativeToJsBridge.cpp與JsToNativeBridge.cpp就是Java與JS相互調用的通訊橋,咱們來看看它們的通訊方式。
關於整個RN的通訊機制,能夠用一句話來歸納:
JNI做爲C++與Java的橋樑,JSC做爲C++與JavaScript的橋樑,而C++最終鏈接了Java與JavaScript。
RN應用通訊橋結構圖以下所示:
理解了整個通訊橋的結構,Java與JS是如何互相掉用的就很清晰了。
注:若是想要了解關於通訊機制的更多細節,能夠去看個人這篇文章6ReactNative源碼篇:通訊機制。