啟動流程
RN的頁面也是依托Activity,React Native框架里有一個ReactActivity
归榕,它就是我們RN頁面的容器。
ReactActivity里有個ReactRootView
绩郎,正如它的名字那樣叉寂,它就是 ReactActivity的root View萍启,最終渲染出來的view都會添加到這個ReactRootView上。
ReactRootView調(diào)用自己的startReactApplication()
方法啟動了整個RN頁面,在啟動的過程中先去創(chuàng)建頁面上下文ReactContext
勘纯,然后再去加載局服、執(zhí)行并將JavaScript映射成Native Widget,最終一個RN頁面就顯示在了用戶面前驳遵。
整個RN頁面的啟動流程圖如下所示
- 創(chuàng)建
ReactRootView
過程
public class MainActivity extends ReactActivity {}
public abstract class ReactActivity extends AppCompatActivity
implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {
private final ReactActivityDelegate mDelegate;
protected ReactActivity() {
mDelegate = createReactActivityDelegate();
}
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName());
}
}
public class ReactActivityDelegate {
public ReactActivityDelegate(ReactActivity activity, @Nullable String mainComponentName) {
mActivity = activity;
mMainComponentName = mainComponentName;
}
protected void onCreate(Bundle savedInstanceState) {
if (mMainComponentName != null) {
loadApp(mMainComponentName);
}
mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
}
protected void loadApp(String appKey) {
if (mReactRootView != null) {
throw new IllegalStateException("Cannot loadApp while app is already running.");
}
mReactRootView = createRootView();
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
appKey,
getLaunchOptions());
getPlainActivity().setContentView(mReactRootView);
}
}
- 創(chuàng)建
ReactInstanceManager
public ReactInstanceManager getReactInstanceManager() {
if (mReactInstanceManager == null) {
ReactMarker.logMarker(ReactMarkerConstants.GET_REACT_INSTANCE_MANAGER_START);
mReactInstanceManager = createReactInstanceManager();
ReactMarker.logMarker(ReactMarkerConstants.GET_REACT_INSTANCE_MANAGER_END);
}
return mReactInstanceManager;
}
protected ReactInstanceManager createReactInstanceManager() {
ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_START);
ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
// 應(yīng)用上下文
.setApplication(mApplication)
//JSMainModuleP相當(dāng)于應(yīng)用首頁的js Bundle淫奔,可以傳遞url從服務(wù)器拉取js Bundle
//當(dāng)然這個只在dev模式下可以使用
.setJSMainModulePath(getJSMainModuleName())
//是否開啟dev模式
.setUseDeveloperSupport(getUseDeveloperSupport())
//紅盒的回調(diào)
.setRedBoxHandler(getRedBoxHandler())
//JS執(zhí)行器
.setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
//自定義UI實現(xiàn)機制,這個我們一般用不到
.setUIImplementationProvider(getUIImplementationProvider())
.setJSIModulesPackage(getJSIModulePackage())
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE);
//添加我們外面設(shè)置的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()));
}
ReactInstanceManager reactInstanceManager = builder.build();
ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_END);
return reactInstanceManager;
}
- 創(chuàng)建
ReactContext
ReactContext
我們看到從最初的startReactApplication
最終會調(diào)用到這里
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模式里開啟異常處理器唆迁,就是我們開發(fā)中見到的調(diào)試工具(紅色錯誤框等)
if (mUseDeveloperSupport) {
reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
}
//創(chuàng)建JavaModule注冊表
NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
? mNativeModuleCallExceptionHandler
: mDevSupportManager;
//創(chuàng)建CatalystInstanceImpl的Builder,它是三端通信的管理類
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
//JS執(zhí)行器
.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;
//構(gòu)建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);
//開啟加載執(zhí)行JS Bundle
catalystInstance.runJSBundle();
//關(guān)聯(lián)catalystInstance與reactContext
reactContext.initializeWithInstance(catalystInstance);
return reactContext;
}
}
在這個方法里完成了RN頁面上下文ReactContext的創(chuàng)建竞穷,我們先來看看這個方法的兩個入?yún)ⅲ?/p>
-
jsExecutor
:JSCJavaScriptExecutor
繼承于JavaScriptExecutor
唐责,當(dāng)該類被加載時,它會自動去加載"reactnativejnifb.so"庫来庭,并會調(diào)用Native方法initHybrid()
初始化C++層RN與JSC通信的框架妒蔚。 -
jsBundleLoader
:緩存了JSBundle的信息,封裝了上層加載JSBundle的相關(guān)接口月弛,CatalystInstanceImpl
實現(xiàn)了JSBundleLoaderDelegate
接口,可以間接調(diào)用ReactBridge
的 native 方法去加載JS文件科盛,不同的場景會創(chuàng)建不同的加載器帽衙,具體可以查看類JSBundleLoader。
可以看到在ReactContext創(chuàng)建的過程中贞绵,主要做了以下幾件事情:
- 構(gòu)建
ReactApplicationContext
對象厉萝,ReactApplicationContext是ReactContext的包裝類。 - 利用
jsExecutor
榨崩、nativeModuleRegistry
谴垫、jsBundleLoader
、exceptionHandler
等參數(shù)構(gòu)建CatalystInstance
實例母蛛,作為以為三端通信的中樞翩剪。 - 調(diào)用
CatalystInstance.runJSBundle()
開始加載執(zhí)行JS。
另一個重要的角色CatalystInstance出現(xiàn)了彩郊,前面我們也說過它是三端通信的中樞前弯。關(guān)于通信的具體實現(xiàn)我們會在接下來的通信機制小節(jié)來講述,我們先來接著看JS的加載過程秫逝。
加載 JS Bundle
首先是CatalystInstanceImpl
private CatalystInstanceImpl(
final ReactQueueConfigurationSpec reactQueueConfigurationSpec,
final JavaScriptExecutor jsExecutor,
final NativeModuleRegistry nativeModuleRegistry,
final JSBundleLoader jsBundleLoader,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge.");
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstanceImpl");
mHybridData = initHybrid();
//創(chuàng)建三大線程:UI線程恕出、Native線程與JS線程
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
reactQueueConfigurationSpec,
new NativeExceptionHandler());
mBridgeIdleListeners = new CopyOnWriteArrayList<>();
mNativeModuleRegistry = nativeModuleRegistry;
//創(chuàng)建JS Module注冊表實例,這個在以前的代碼版本中是在上面的createReactContext()方法中創(chuàng)建的
mJSModuleRegistry = new JavaScriptModuleRegistry();
mJSBundleLoader = jsBundleLoader;
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
mNativeModulesQueueThread = mReactQueueConfiguration.getNativeModulesQueueThread();
mTraceListener = new JSProfilerTraceListener(this);
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge before initializeBridge");
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "initializeCxxBridge");
//在C++層初始化通信橋
initializeBridge(
new BridgeCallback(this),
jsExecutor,
mReactQueueConfiguration.getJSQueueThread(),
mNativeModulesQueueThread,
mNativeModuleRegistry.getJavaModules(this),
mNativeModuleRegistry.getCxxModules());
Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge");
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
mJavaScriptContextHolder = new JavaScriptContextHolder(getJavaScriptContext());
}
ReactQueueConfigurationSpec
用來創(chuàng)建ReactQueueConfiguration
的實例违帆,ReactQueueConfiguration
同樣是個接口浙巫,它的實現(xiàn)類是ReactQueueConfigurationImpl
。ReactQueueConfiguration
的定義如下
public interface ReactQueueConfiguration {
//UI線程
MessageQueueThread getUIQueueThread();
//Native線程
MessageQueueThread getNativeModulesQueueThread();
//JS線程
MessageQueueThread getJSQueueThread();
void destroy();
}
JS Bundle的加載實際上是指C++層完成的刷后,我們看一下序列圖
JSBundle 有三種加載方式:
setSourceURLs
, loadScriptFromAssets
, loadScriptFromFile
而真正執(zhí)行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");
}
//調(diào)用JavaScriptCore里的方法驗證JS是否有效的畴,并解釋執(zhí)行
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;
}
}
void JSCExecutor::flush() {
SystraceSection s("JSCExecutor::flush");
if (m_flushedQueueJS) {
//調(diào)用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));
}
}
可以看到這個方法主要是調(diào)用JavaScriptCore里的evaluateSourceCode()
方法驗證JS是否有效廉油,并解釋執(zhí)行。然后再調(diào)用flush()
方法調(diào)用JS層里的方法執(zhí)行JS Bundle苗傅。
上面提到flush()
方法調(diào)用MessageQueue.js的flushedQueue()
方法抒线,這是如何做到的呢?
事實上這是由bindBridge()
方法來完成的渣慕,它從__fbBatchedBridge
對象中取出MessageQueue.js里的四個方法
callFunctionReturnFlushedQueue()
invokeCallbackAndReturnFlushedQueue()
flushedQueue()
callFunctionReturnResultAndFlushedQueue()
并分別存在四個C++變量中:
m_callFunctionReturnFlushedQueueJS
m_invokeCallbackAndReturnFlushedQueueJS
m_flushedQueueJS
m_callFunctionReturnResultAndFlushedQueueJS
這樣C++就可以調(diào)用這四個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();
});
}
綁定ReactContext與ReactRootView
JS Bundle加載完成以后,前面說的createReactContext()
就執(zhí)行完成了逊桦,然后開始執(zhí)行setupReacContext()
方法眨猎,綁定ReactContext
與ReactRootView
。 我們來看一下它的實現(xiàn)强经。
public class ReactInstanceManager {
private void setupReactContext(final ReactApplicationContext reactContext) {
//...
//執(zhí)行Java Module的初始化
catalystInstance.initialize();
//通知ReactContext已經(jīng)被創(chuàng)建愛女
mDevSupportManager.onNewReactContextCreated(reactContext);
//內(nèi)存狀態(tài)回調(diào)設(shè)置
mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
//復(fù)位生命周期
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);
//執(zhí)行JS的入口bundle頁面
rootView.invokeJSEntryPoint();
//...
}
}
JS的頁面入口我們可以設(shè)置mJSEntryPoint
來自定義入口睡陪,如果不設(shè)置則是默認的入口AppRegistry。
private void defaultJSEntryPoint() {
//...
try {
//...
String jsAppModuleName = getJSModuleName();
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
這里的調(diào)用方式實際上就是原生調(diào)用JS的方法匿情,它調(diào)用的正是我們很熟悉的AppRegistry.js兰迫,AppRegistry.js調(diào)用runApplication()開始執(zhí)行JS頁面的渲染,最終轉(zhuǎn)換為Native UI顯示在手機上炬称。
到此為止汁果,整個RN頁面的啟動流程就分析完了,我們接著來看看RN頁面是如何渲染最終顯示在手機上的玲躯。
渲染
RN頁面的入口一般是AppRegistry.js据德,先看一下RN頁面的渲染流程序列圖,如下所示:
大體過程:
- React Native將代碼由JSX轉(zhuǎn)化為JS組件跷车,啟動過程中利用
instantiateReactComponent
將ReactElement
轉(zhuǎn)化為復(fù)合組件ReactCompositeComponent
與元組件ReactNativeBaseComponent
棘利,利用ReactReconciler
對他們進行渲染。 - UIManager.js利用C++層的Instance.cpp將UI信息傳遞給UIManagerModule.java朽缴,并利用UIManagerModule.java構(gòu)建UI善玫。
- UIManagerModule.java接收到UI信息后,將UI的操作封裝成對應(yīng)的Action不铆,放在隊列中等待執(zhí)行蝌焚。各種UI的操作,例如創(chuàng)建誓斥、銷毀只洒、更新等便在隊列里完成,UI最終得以渲染在屏幕上劳坑。
JavaScript層組件渲染
-
AppRegistry.registerComponent
用來注冊組件毕谴,在該方法內(nèi)它會調(diào)用AppRegistry.runApplication()
來啟動js的渲染流程。 -
AppRegistry.runApplication()
會將傳入的Component轉(zhuǎn)換成ReactElement,并在外面包裹一層AppContaniner涝开,AppContaniner主要用來提供一些debug工具(例如:紅盒)
// ReactNativeMount.js
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);
//檢查之前的節(jié)點是否已經(jīng)mount到目標節(jié)點上循帐,如果有則進行比較處理
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任務(wù)提交給回調(diào)Queue,最終會調(diào)用ReactReconciler.mountComponent()
ReactUpdates.batchedUpdates(
batchedMountComponentIntoNode,
instance,
containerTag
);
var component = instance.getPublicInstance();
if (callback) {
callback.call(component);
}
return component;
},
該方法主要做了以下事情:
- 將傳入的
RectElement
使用相同的TopLevelWrapper進行包裹舀武,生成nextWrappedElement
拄养。 - 檢查之前的節(jié)點是否已經(jīng)mount到目標節(jié)點上,如果有則進行比較處理银舱,將上一步生成的
nextWrappedElement
傳入instantiateReactComponent(nextWrappedElement, false)
方法瘪匿。 - 將mount任務(wù)提交給回調(diào)Queue,最終會調(diào)用
ReactReconciler.mountComponent()
寻馏,ReactReconciler.mountComponent()又會去調(diào)用C++層Instance::mountComponent()
方法棋弥。
React組件可以分為兩種:
- 元組件:框架內(nèi)置的,可以直接使用的組件诚欠。例如:View顽染、Image等。它在React Native中用
ReactNativeBaseComponent
來描述轰绵。 - 復(fù)合組件:用戶封裝的組件粉寞,一般可以通過
React.createClass()
來構(gòu)建,提供render()
方法來返回渲染目標藏澳。它在React Native中用ReactCompositeComponent來描述仁锯。
instantiateReactComponent(node, shouldHaveDebugID)
方法就是根據(jù)對象的type生成元組件或者復(fù)合組件。
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翔悠,則調(diào)用ReactHostComponent.createInternalComponent(element)來注入生成組件的邏輯
if (typeof element.type === 'string') {
instance = ReactHostComponent.createInternalComponent(element);
}
//如果是內(nèi)部元組件,則創(chuàng)建一個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;
}
}
//否則野芒,則是用戶創(chuàng)建的復(fù)合組件蓄愁,這個時候創(chuàng)建一個ReactCompositeComponentWrapper實例,該實例用來描述復(fù)合組件
else {
instance = new ReactCompositeComponentWrapper(element);
}
//當(dāng)對象為string或者number時狞悲,調(diào)用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;
}
該方法根據(jù)對象的type生成元組件或者復(fù)合組件,具體流程如下:
- 如果對象的type為string摇锋,則調(diào)用
ReactHostComponent.createInternalComponent(element)
來注入生成組件的邏輯丹拯,如果是內(nèi)部元組件,則創(chuàng)建一個type實例荸恕, - 否則乖酬,則是用戶創(chuàng)建的復(fù)合組件,這個時候創(chuàng)建一個
ReactCompositeComponentWrapper
實例融求,該實例用來描述復(fù)合組件咬像。 - 當(dāng)對象為string或者number時,調(diào)用
ReactHostComponent.createInstanceForText(node)
來注入組件生成邏輯。 - 以上都不是县昂,則報錯肮柜。
我們可以看到,UI渲染主要通過UIManager來完成倒彰,UIManager是一個ReactModule审洞,UIManager.js里的操作都會對應(yīng)到UIManagerModule里來。我們接著來看看Java層的渲染流程
Java層的渲染流程
Java層的組件渲染分為以下幾步:
- JS層通過C++層把創(chuàng)建View的請求發(fā)送給Java層的UIManagerModule待讳。
- UIManagerModule通過UIImplentation對操作請求進行包裝芒澜。
- 包裝后的操作請求被發(fā)送到View處理隊列UIViewOperationQueue隊列中等待處理。
- 實際處理View時耙箍,根據(jù)class name查詢對應(yīng)的ViewNManager撰糠,然后調(diào)用原生View的方法對View進行相應(yīng)的操作。
通信機制
Java層與JavaScript層的相互調(diào)用都不是直接完成的辩昆,而是間接通過C++層來完成的阅酪。在介紹通信機制之前我們先來理解一些基本的概念。
JavaScript Module注冊表
說起JavaScript Module注冊表汁针,我們需要先理解幾個類/接口:
-
JavaScriptModule
:這是一個接口术辐,JS Module都會繼承此接口,它表示在JS層會有一個相同名字的js文件施无,該js文件實現(xiàn)了該接口定義的方法辉词,JavaScriptModuleRegistry會利用動態(tài)代理將這個接口生成代理類,并通過C++傳遞給JS層猾骡,進而調(diào)用JS層的方法瑞躺。 -
JavaScriptModuleRegistration
用來描述JavaScriptModule的相關(guān)信息,它利用反射獲取接口里定義的Method兴想。 -
JavaScriptModuleRegistry
:JS Module注冊表幢哨,內(nèi)部維護了一個HashMap:HashMap<Class<? extends JavaScriptModule>, JavaScriptModuleRegistration> mModuleRegistrations,JavaScriptModuleRegistry利用動態(tài)代理生成接口JavaScriptModule對應(yīng)的代理類嫂便,再通過C++傳遞到JS層捞镰,從而調(diào)用JS層的方法。
Java Module注冊表
要理解Java Module注冊表毙替,我們同樣也需要理解3個類/接口:
-
NativeModule
:是一個接口岸售,實現(xiàn)了該接口則可以被JS層調(diào)用,我們在為JS層提供Java API時通常會繼承BaseJavaModule/ReactContextBaseJavaModule厂画,這兩個類就實現(xiàn)了NativeModule接口凸丸。 -
ModuleHolder
:NativeModule的一個Holder類,可以實現(xiàn)NativeModule的懶加載木羹。 -
NativeModuleRegistry
:Java Module注冊表甲雅,內(nèi)部持有Map:Map<Class<? extends NativeModule>, ModuleHolder> mModules解孙,NativeModuleRegistry可以遍歷并返回Java Module供調(diào)用者使用。
其中 NativeModuleRegistry是在createReactContext()
方法里構(gòu)建的抛人。
JavaScriptModuleRegistry是在CatalystInstanceImpl
的構(gòu)建方法里構(gòu)建的.
這些都是在CatalystInstanceImpl的構(gòu)建方法里通過native方法initializeBridge()傳入了C++層弛姜,如下所示:
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));
}
-
callback
:CatalystInstanceImpl的靜態(tài)內(nèi)部類ReactCallback,負責(zé)接口回調(diào)妖枚。 -
jsExecutor
:JS執(zhí)行器廷臼,將JS的調(diào)用傳遞給C++層。 -
jsQueue.getJSQueueThread()
:JS線程绝页,通過mReactQueueConfiguration.getJSQueueThread()
獲得荠商,mReactQueueConfiguration通過ReactQueueConfigurationSpec.createDefault()
創(chuàng)建。 -
moduleQueue
:Native線程续誉,通過mReactQueueConfiguration.getNativeModulesQueueThread()
獲得莱没,mReactQueueConfiguration 通過ReactQueueConfigurationSpec.createDefault()
創(chuàng)建。 -
javaModules
:java modules酷鸦,來源于mJavaRegistry.getJavaModules(this)
饰躲。它對應(yīng)了C++層JavaModuleWrapper.cpp, JS在Java的時候最終會調(diào)用到這個類的inovke()
方法上臼隔。 -
cxxModules
:c++ modules嘹裂,來源于mJavaRegistry.getCxxModules()
。ModuleHolder是NativeModule的一個Holder類摔握,可以實現(xiàn)NativeModule的懶加載寄狼。
cxxModules
javaModules
這兩個集合在CatalystInstanceImpl::initializeBridge()
被打包成ModuleRegistry傳入Instance.cpp 中,并在NativeToJsBridge的構(gòu)造方法中傳給JsToNativeBridge氨淌,以后JS如果調(diào)用Java就可以通過 ModuleRegistry 來進行調(diào)用泊愧。
JNI作為C++與Java的橋梁,JSC作為C++與JavaScript的橋梁盛正,而C++最終連接了Java與JavaScript拼卵。