前言
專案中用到RN也有一段時間了, 從剛開始的懵懂到現在的熟能生巧,其實還是很長的,每次接觸一個新的東西的時候,剛開始一定是比較痛苦的,不知道要如何下手,這裡從js到Android原生去一步步解析React Native
是如何做到如此巧妙的通訊的。希望能對你們有所幫助。
這裡以我目前專案中用到的版本為例:
{
"react": "16.0.0-alpha.12",
"react-native": "^0.46.4",
}
複製程式碼
JS
我們先從js端入手
import { AppRegistry } from 'react-native'
...省略程式碼
AppRegistry.registerComponent('xemall', () => Index)
複製程式碼
上述程式碼就是JS程式的入口,將當前APP物件註冊到AppRegistry
元件中,AppRegistry元件是js module。
啟動流程
我們新建一個RN的專案,在原生程式碼中會生成MainActivity
和MainApplication
兩個Java類。顧名思義,MainAcitivity
就是我們的Native的入口了,
我們先來看下MainApplication
都做了哪些操作:
public class MainApplication extends Application implements ReactApplication {
//ReactNativeHost:持有ReactInstanceManager例項,做一些初始化操作。
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage()
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
//SoLoader:載入C++底層庫,準備解析JS。
SoLoader.init(this, /* native exopackage */ false);
}
}
}
複製程式碼
我們再來看下MainActivity的程式碼:
public class MainActivity extends ReactActivity {
@Override
protected String getMainComponentName() {
return "xemall";
}
}
複製程式碼
可以看到其實是繼承了ReactActivity
類,只是重寫了getMainComponentName
方法,有沒有看出來,其方法的返回值和我們在JS端的值是一樣的。如果不一致會怎麼樣,你可以自己試一下。
ReactActivity
我們來看下ReactActivity
的方法的onCreate
方法:
public abstract class ReactActivity extends Activity
implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {
private final ReactActivityDelegate mDelegate;
...省略程式碼
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDelegate.onCreate(savedInstanceState);
}
}
複製程式碼
ReactActivity全權委託給ReactActivityDelegate
來處理。
ReactActivityDelegate
public class ReactActivityDelegate {
protected void onCreate(Bundle savedInstanceState) {
// 彈框許可權判斷
boolean needsOverlayPermission = false;
if (getReactNativeHost().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Get permission to show redbox in dev builds.
if (!Settings.canDrawOverlays(getContext())) {
needsOverlayPermission = true;
Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getContext().getPackageName()));
FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
Toast.makeText(getContext(), REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
((Activity) getContext()).startActivityForResult(serviceIntent, REQUEST_OVERLAY_PERMISSION_CODE);
}
}
// 載入組建邏輯 mMainComponentName為getMainComponentName返回的值
if (mMainComponentName != null && !needsOverlayPermission) {
loadApp(mMainComponentName);
}
// 雙擊判斷工具類
mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
}
protected void loadApp(String appKey) {
//空判斷
if (mReactRootView != null) {
throw new IllegalStateException("Cannot loadApp while app is already running.");
}
// 建立 RN容器根檢視
mReactRootView = createRootView();
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
appKey,
getLaunchOptions());
//將rootview新增入activity
getPlainActivity().setContentView(mReactRootView);
}
}
複製程式碼
loadApp
做了三件事:建立RootView、建立ReactApplication、建立ReactInstanceManager
ReactRootView
ReactRootView是一個自定義的View,其父類是FrameLayout。因此,可以把RN看成是一個特殊的 “自定義View”。
我們來看下startReactApplication
方法:
public void startReactApplication(
ReactInstanceManager reactInstanceManager,
String moduleName,
@Nullable Bundle initialProperties) {
...省略程式碼
try {
//在UI執行緒中進行
UiThreadUtil.assertOnUiThread();
Assertions.assertCondition(
mReactInstanceManager == null,
"This root view has already been attached to a catalyst instance manager");
// 賦值
mReactInstanceManager = reactInstanceManager;
mJSModuleName = moduleName;
mAppProperties = initialProperties;
// 判斷ReactContext是否初始化,沒有就非同步進行初始化
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
mReactInstanceManager.createReactContextInBackground();
}
//寬高計算完成後新增布局監聽
attachToReactInstanceManager();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
複製程式碼
startReactApplication
中的三個引數:
形參 | 描述 |
---|---|
reactInstanceManager | ReactInstanceManager型別,建立和管理CatalyInstance的例項 |
moduleName | 就是之前的元件名 |
initialProperties | 是Native向JS傳遞的資料,以後可能由POJO代替,預設是null,需要的話要重寫createReactActivityDelegate ,並重寫其中getLaunchOptions方法 |
startReactApplication
中呼叫了ReactInstanceManager
的createReactContextInBackground
方法。
ReactInstanceManager
public void createReactContextInBackground() {
//首次執行
mHasStartedCreatingInitialContext = true;
recreateReactContextInBackgroundInner();
}
複製程式碼
該方法只會在application中執行一次,JS過載時,會走recreateReactContextInBackground
, 這兩個方法最終都會呼叫recreateReactContextInBackgroundInner
方法。
@ThreadConfined(UI)
private void recreateReactContextInBackgroundInner() {
// 確保在UI執行緒中執行
UiThreadUtil.assertOnUiThread();
if (mUseDeveloperSupport && mJSMainModuleName != null &&
!Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JSC_CALLS)) {
// 除錯模式,載入伺服器bundle
return;
}
// 載入本地bundle
recreateReactContextInBackgroundFromBundleLoader();
}
@ThreadConfined(UI)
private void recreateReactContextInBackgroundFromBundleLoader() {
recreateReactContextInBackground(
new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()),
mBundleLoader);
}
複製程式碼
形參 | 描述 |
---|---|
jsExecutorFactory | C++和JS雙向通訊的中轉站 |
jsBundleLoader | bundle載入器,根據ReactNativeHost中的配置決定從哪裡載入bundle檔案 |
private void recreateReactContextInBackground(
JavaScriptExecutor.Factory jsExecutorFactory,
JSBundleLoader jsBundleLoader) {
UiThreadUtil.assertOnUiThread();
//建立ReactContextInitParams物件
final ReactContextInitParams initParams = new ReactContextInitParams(
jsExecutorFactory,
jsBundleLoader);
if (mCreateReactContextThread == null) {
// 新增執行緒初始化ReactContext
runCreateReactContextOnNewThread(initParams);
} else {
mPendingReactContextInitParams = initParams;
}
}
複製程式碼
runCreateReactContextOnNewThread中有一個核心方法createReactContext
來建立ReactContext。
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
// 包裝ApplicationContext
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
//建立JavaModule登錄檔Builder,用來建立JavaModule登錄檔,JavaModule登錄檔將所有的JavaModule註冊到CatalystInstance中。
NativeModuleRegistryBuilder nativeModuleRegistryBuilder = new NativeModuleRegistryBuilder(
reactContext,
this,
mLazyNativeModulesEnabled);
// 建立JavaScriptModule登錄檔Builder
JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();
if (mUseDeveloperSupport) {
// 除錯模式下,將錯誤交給DevSupportManager處理
reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
}
...省略程式碼
try {
//建立CoreModulesPackage,其中封裝了RN Framework核心功能,通訊、除錯等。
CoreModulesPackage coreModulesPackage =
new CoreModulesPackage(
this,
mBackBtnHandler,
mUIImplementationProvider,
mLazyViewManagersEnabled);
//把各自的Module新增到對應的登錄檔中
processPackage(coreModulesPackage, nativeModuleRegistryBuilder, jsModulesBuilder);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
// 將我們Application中的ReactPackage迴圈處理,加入對應的登錄檔中。
for (ReactPackage reactPackage : mPackages) {
...省略程式碼
try {
processPackage(reactPackage, nativeModuleRegistryBuilder, jsModulesBuilder);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
...省略程式碼
//生成Java登錄檔,將Java可呼叫的API暴露給JS
NativeModuleRegistry nativeModuleRegistry;
try {
nativeModuleRegistry = nativeModuleRegistryBuilder.build();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
}
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
? mNativeModuleCallExceptionHandler
: mDevSupportManager;
//構建CatalystInstanceImpl例項
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(mUseSeparateUIBackgroundThread ?
ReactQueueConfigurationSpec.createWithSeparateUIBackgroundThread() :
ReactQueueConfigurationSpec.createDefault())
//JS執行通訊類
.setJSExecutor(jsExecutor)
//Java模組登錄檔
.setRegistry(nativeModuleRegistry)
// JS登錄檔
.setJSModuleRegistry(jsModulesBuilder.build())
// Bundle載入工具類
.setJSBundleLoader(jsBundleLoader)
// 異常處理器
.setNativeModuleCallExceptionHandler(exceptionHandler);
// 省略程式碼
final CatalystInstance catalystInstance;
try {
catalystInstance = catalystInstanceBuilder.build();
} finally {
//省略程式碼
}
if (mBridgeIdleDebugListener != null) {
catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
}
if (Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JSC_CALLS)) {
//呼叫CatalystInstanceImpl的Native方法把Java Registry轉換為Json,再由C++層傳送到JS層。 catalystInstance.setGlobalVariable("__RCTProfileIsProfiling", "true");
}
//關聯ReacContext與CatalystInstance
reactContext.initializeWithInstance(catalystInstance);
//通過CatalystInstance開始載入JS Bundle
catalystInstance.runJSBundle();
return reactContext;
}
複製程式碼
這段程式碼比較長,看的話會花一定時間,它主要做了這幾件事:
- 建立JavaModule登錄檔和JavaScriptModule登錄檔,交給CatalystInstance管理。
- 處理ReactPackage,將各自的Module放入對應的登錄檔中。
- 通過上面的各個引數建立CatalystInstance例項。
- CatalystInstance關聯ReactContext,開始載入JS Bundle
CatalystInstance
我們來看下CatalystInstance的實現類CatalystInstanceImpl的構造方法:
private CatalystInstanceImpl(
final ReactQueueConfigurationSpec reactQueueConfigurationSpec,
final JavaScriptExecutor jsExecutor,
final NativeModuleRegistry registry,
final JavaScriptModuleRegistry jsModuleRegistry,
final JSBundleLoader jsBundleLoader,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
//用來建立JNI相關方法,並返回mHybridData
mHybridData = initHybrid();
// Android UI執行緒、JS執行緒、NativeMOdulesQueue執行緒
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
reactQueueConfigurationSpec,
new NativeExceptionHandler());
// 省略程式碼
//呼叫 C++ 層程式碼進行初始化Bridge
initializeBridge(
new BridgeCallback(this),
jsExecutor,
mReactQueueConfiguration.getJSQueueThread(),
mNativeModulesQueueThread,
mUIBackgroundQueueThread,
mJavaRegistry.getJavaModules(this),
mJavaRegistry.getCxxModules());
}
複製程式碼
private native void initializeBridge(
ReactCallback callback,
JavaScriptExecutor jsExecutor,
MessageQueueThread jsQueue,
MessageQueueThread moduleQueue,
MessageQueueThread uiBackgroundQueue,
Collection<JavaModuleWrapper> javaModules,
Collection<ModuleHolder> cxxModules);
複製程式碼
形參 | 描述 |
---|---|
ReactCallback | CatalystInstanceImpl的靜態內部類ReactCallback,負責介面回撥 |
JavaScriptExecutor | JS執行器,將JS的呼叫傳給C++層 |
MessageQueueThread | JS執行緒 |
MessageQueueThread moduleQueue | Java執行緒 |
MessageQueueThread uiBackgroundQueue | UI背景執行緒 |
javaModules | java module |
cxxModules | c++ module |
createReactContext
方法中用catalystInstance.runJSBundle() 來載入 JS bundle
@Override
public void runJSBundle() {
...
mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
...
}
複製程式碼
JSBundleLoader
CatalystInstanceImpl.runJSBundle()會呼叫JSBundleLoader去載入JS Bundle,由於不同的情況可能會有不同的JSBundleLoader,我們假設其中一種:
public abstract class JSBundleLoader {
/**
* This loader is recommended one for release version of your app. In that case local JS executor
* should be used. JS bundle will be read from assets in native code to save on passing large
* strings from java to native memory.
*/
public static JSBundleLoader createAssetLoader(
final Context context,
final String assetUrl,
final boolean loadSynchronously) {
return new JSBundleLoader() {
@Override
public String loadScript(CatalystInstanceImpl instance) {
instance.loadScriptFromAssets(context.getAssets(), assetUrl, loadSynchronously);
return assetUrl;
}
};
}
複製程式碼
可以看到它會繼續呼叫CatalystInstance中的loadScriptFromAssets方法
public class CatalystInstanceImpl {
/* package */ void loadScriptFromAssets(AssetManager assetManager, String assetURL) {
mSourceURL = assetURL;
jniLoadScriptFromAssets(assetManager, assetURL);
}
private native void jniLoadScriptFromAssets(AssetManager assetManager, String assetURL);
}
複製程式碼
最終呢,還是會呼叫CatalystInstanceImpl.cpp去載入JS Bundle,我們去C++層看一下實現
我們先看下原始碼的結構圖
CatalystInstanceImpl.cpp
在ReactAndroid的Jni中,我們看下相關程式碼:
void CatalystInstanceImpl::jniLoadScriptFromAssets(
jni::alias_ref<JAssetManager::javaobject> assetManager,
const std::string& assetURL,
bool loadSynchronously) {
const int kAssetsLength = 9; // strlen("assets://");
// 獲取soure js Bundle的路徑名
auto sourceURL = assetURL.substr(kAssetsLength);
// 獲取AssetManager
auto manager = extractAssetManager(assetManager);
// 讀取JS Bundle裡的內容
auto script = loadScriptFromAssets(manager, sourceURL);
// unbundle命令打包判斷
if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) {
instance_->loadUnbundle(
folly::make_unique<JniJSModulesUnbundle>(manager, sourceURL),
std::move(script),
sourceURL,
loadSynchronously);
return;
} else {
//bundle命令打包走次流程,instance_是Instan.h中類的例項
instance_->loadScriptFromString(std::move(script), sourceURL, loadSynchronously);
}
}
複製程式碼
Instance.cpp
void Instance::loadScriptFromString(std::unique_ptr<const JSBigString> string,
std::string sourceURL,
bool loadSynchronously) {
SystraceSection s("reactbridge_xplat_loadScriptFromString", "sourceURL", sourceURL);
if (loadSynchronously) {
loadApplicationSync(nullptr, std::move(string), std::move(sourceURL));
} else {
loadApplication(nullptr, std::move(string), std::move(sourceURL));
}
}
void Instance::loadApplicationSync(
std::unique_ptr<JSModulesUnbundle> unbundle,
std::unique_ptr<const JSBigString> string,
std::string sourceURL) {
std::unique_lock<std::mutex> lock(m_syncMutex);
m_syncCV.wait(lock, [this] { return m_syncReady; });
SystraceSection s("reactbridge_xplat_loadApplicationSync", "sourceURL", sourceURL);
//nativeToJsBridge_也是在Instance::initializeBridget()方法裡初始化的,具體實現在NativeToJsBridge.cpp裡。
nativeToJsBridge_->loadApplicationSync(std::move(unbundle), std::move(string), std::move(sourceURL));
}
複製程式碼
NativeToJsBridge.cpp
void NativeToJsBridge::loadApplication(
std::unique_ptr<JSModulesUnbundle> unbundle,
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL) {
//獲取一個MessageQueueThread,探後線上程中執行一個Task。
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從runOnExecutorQueue()返回的map中取得,與OnLoad中的JSCJavaScriptExecutorHolder對應,也與
//Java中的JSCJavaScriptExecutor對應。它的例項在JSExecutor.cpp中實現。
executor->loadApplicationScript(std::move(*startupScript),
std::move(startupScriptSourceURL));
});
}
關於unbundle命令
<unbundle命令,使用方式和bundle命令完全相同。unbundle命令是在bundle命令的基礎上增加了一項功能,除了生成整合JS檔案index.android.bundle外,還會
生成各個單獨的未整合JS檔案(但會被優化),全部放在js-modules目錄下,同時會生成一個名為UNBUNDLE的標識檔案,一併放在其中。UNBUNDLE標識檔案的前4個位元組
固定為0xFB0BD1E5,用於載入前的校驗。
複製程式碼
該函式進一步呼叫JSExecutor.cpp的loadApplicationScript()方法。
到了這個方法,就是去真正載入JS檔案了。
JSCExecutor.cpp
void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) {
...
//使用Webkit JSC去解釋執行JS
evaluateSourceCode(m_context, bcSourceCode, jsSourceURL);
flush();
}
複製程式碼
void JSCExecutor::flush() {
...
//繫結bridge,核心就是通過getGlobalObject()將JS與C++通過Webkit jSC實現繫結
bindBridge();
//返回給callNativeModules
callNativeModules(m_flushedQueueJS->callAsFunction({}));
...
}
複製程式碼
void JSCExecutor::callNativeModules(Value&& value) {
...
//把JS層相關通訊資料轉換為JSON格式
auto calls = value.toJSONString();
//m_delegate為JsToNativeBridge物件。
m_delegate->callNativeModules(*this, folly::parseJson(calls), true);
...
}
複製程式碼
m_flushedQueueJS支線的是MessageQueue.js的flushedQueue()方法,此時JS已經被載入到佇列中,等待Java層來驅動它。
JS Bundle載入並解析完成後,我們回到Java程式碼中看看後續的流程
我們在之前的runCreateReactContextOnNewThread
方法中,在creatReactContext之後還有一句核心的程式碼
setupReactContext(reactApplicationContext);
複製程式碼
這就是載入JS Bundle之後執行的程式碼
public class ReactInstanceManager {
private void setupReactContext(ReactApplicationContext reactContext) {
...
// Native Java module初始化
catalystInstance.initialize();
//重置ReactContext
mDevSupportManager.onNewReactContextCreated(reactContext);
//記憶體狀態回撥設定 mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
// 復位生命週期
moveReactContextToCurrentLifecycleState();
ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_START);
synchronized (mAttachedRootViews) {
//mAttachedRootViews儲存的是ReactRootView
for (ReactRootView rootView : mAttachedRootViews) {
attachRootViewToInstance(rootView, catalystInstance);
}
}
...
}
}
private void attachMeasuredRootViewToInstance ( final ReactRootView rootView,
CatalystInstance catalystInstance) {
...
//將ReactRootView作為根佈局
UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
int rootTag = uiManagerModule.addMeasuredRootView(rootView);
//設定相關
rootView.setRootViewTag(rootTag);
rootView.runApplication();
...
}
複製程式碼
/* package */ void runApplication() {
...
CatalystInstance catalystInstance = reactContext.getCatalystInstance();
WritableNativeMap appParams = new WritableNativeMap();
appParams.putDouble("rootTag", getRootViewTag());
@Nullable Bundle appProperties = getAppProperties();
if (appProperties != null) {
appParams.putMap("initialProps", Arguments.fromBundle(appProperties));
}
String jsAppModuleName = getJSModuleName();
//啟動流程入口:由Java層呼叫啟動
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
...
}
複製程式碼
可以看到,最終呼叫的是catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams)
, AppRegistry.class是JS層暴露給Java層的介面方法。它的真正實現在AppRegistry.js裡,AppRegistry.js是執行所有RN應用的JS層入口,我們來看看它的實現:
在Libraries/ReactNative
中的AppRegistry.js
AppRegistry.js
runApplication(appKey: string, appParameters: any): void {
const msg =
'Running application "' + appKey + '" with appParams: ' +
JSON.stringify(appParameters) + '. ' +
'__DEV__ === ' + String(__DEV__) +
', development-level warning are ' + (__DEV__ ? 'ON' : 'OFF') +
', performance optimizations are ' + (__DEV__ ? 'OFF' : 'ON');
infoLog(msg);
BugReporting.addSource('AppRegistry.runApplication' + runCount++, () => msg);
invariant(
runnables[appKey] && runnables[appKey].run,
'Application ' + appKey + ' has not been registered.\n\n' +
'Hint: This error often happens when you\'re running the packager ' +
'(local dev server) from a wrong folder. For example you have ' +
'multiple apps and the packager is still running for the app you ' +
'were working on before.\nIf this is the case, simply kill the old ' +
'packager instance (e.g. close the packager terminal window) ' +
'and start the packager in the correct app folder (e.g. cd into app ' +
'folder and run \'npm start\').\n\n' +
'This error can also happen due to a require() error during ' +
'initialization or failure to call AppRegistry.registerComponent.\n\n'
);
SceneTracker.setActiveScene({name: appKey});
runnables[appKey].run(appParameters);
},
複製程式碼
到這裡就會去呼叫JS進行渲染,在通過UIManagerModule將JS元件轉換成Android元件,最終顯示在ReactRootView上。
最後總結一下,就是先在應用終端啟動並建立上下文物件,啟動JS Runtime,進行佈局,將JS端的程式碼通過C++層,UIManagerMoodule轉化成Android元件,再進行渲染,最後將渲染的View新增到ReactRootView上,最終呈現在使用者面前。
如果還是不太懂的話,可以看下大佬們畫的圖
ReactNative系統框架圖如下所示
啟動流程圖:
參考資料
20分鐘理解React Native For Android原理