團隊的項目已經使用RN,有必要對React Native For Android有一個深入的了解,于是就寫了這篇文章。注意本文分析的代碼版本為:
- "react": "15.3.1",
- "react-native": "0.32.0"
環境配置
首先按照React Native的官方文檔安裝必要的環境并下載Demo程序,請參考Getting Started. 為了更方便的修改RN的源碼,建議按照Building React Native from source配置一下。這篇文章第三步這是這樣
從Demo入手
先把Demo跑起來看一下效果:
很簡單,就是幾個TextView. 我們來看一下JS是如何寫的,在index.android.js中
...
class AwesomeProject extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.android.js
</Text>
<Text style={styles.instructions}>
Double tap R on your keyboard to reload,{'\n'}
Shake or press menu button for dev menu
</Text>
</View>
);
}
}
...
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);
看著貌似很簡單,那他們是怎么生成Android的頁面呢。下面開始我們的分析之旅吧。
查看AwesomeProject下的android目錄,發現只有MainActivity和MainApplication兩個java文件。從MainActivity 入手
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "AwesomeProject";
}
}
內容也很簡單,只有一個返回組件名字的函數。再關聯到上面的js文件,Component名字是一樣的。看一下ReactActvity的繼承它就是一個Activity的子類。頁面顯示什么內容完全是由它控制的。先看一下它的啟動時序圖:
接下來就到源碼里看看它的實現吧。
深入ReactAndroid
看了下ReactActivity的代碼,發現在它只是一個空殼子,方法都是交給ReactActivityDelegate去處理的。這個delegate在ReactActivity的構造函數里就創建了。看一下它的onCreate最主要的就是調用loadApp().
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);
}
最主要的是干了三件事:
- 創建ReactRootView
- 創建ReactInstanceManager
- 啟動ReactApplication
接下來逐一分析。
1. 創建ReactRootView
protected ReactRootView createRootView() {
return new ReactRootView(getContext());
}
就是new了一個ReactRootView. 正如名字一樣,它就是一個View,繼承于FrameLayout。實現的功能包括,計算View的大小,監聽手勢、onTouch事件并交由JS處理,監聽View大小變化和鍵盤變化。
2. 創建ReactInstanceManager
ReactInstanceManager是用來創建及管理CatalyInstance的實例的上層接口、控制開發調試,生命周期與ReactRootView所在activity保持一致。使用ReactInstanceManager.Builder創建,準備的參數包括:
ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
.setApplication(mApplication)
.setJSMainModuleName(getJSMainModuleName())
.setUseDeveloperSupport(getUseDeveloperSupport())
.setRedBoxHandler(getRedBoxHandler())
.setUIImplementationProvider(getUIImplementationProvider())
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE);
for (ReactPackage reactPackage : getPackages()) {
builder.addPackage(reactPackage);
}
String jsBundleFile = getJSBundleFile();
if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
}
builder.build();
- Application :Android的Context
- Module Name : 對應index.android.js中的AppRegistry.registerComponent('Module Name'), 即是應用入口注冊的模塊名
- DeveloperSupport和RedBox: 調試相關
- UIImplementationProvider : 獲取一個UIImplementation,這個類用于接收JS命令,然后處理Android的View
- ReactPackage: 包含一些Native和JS模塊,RN自己提供了一個MainPackage
- BundleFile : JS代碼和資源,文件的位置如何確定稍后會介紹
build之后會生成XReactInstanceManagerImpl。
到此startReactApplication的所有參數都已經準備好了,來看一下它的實現
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
mReactInstanceManager.createReactContextInBackground();
}
主要實現就是判斷ReactContext是否已經創建,沒有就在后臺線程創建
3. 創建ReactContext
/**
* @return instance of {@link ReactContext} configured a {@link CatalystInstance} set
*/
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
mSourceUrl = jsBundleLoader.getSourceUrl();
NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
if (mUseDeveloperSupport) {
reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
}
try {
CoreModulesPackage coreModulesPackage =
new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
} finally {
}
for (ReactPackage reactPackage : mPackages) {
try {
processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
} finally {
}
}
NativeModuleRegistry nativeModuleRegistry;
try {
nativeModuleRegistry = nativeRegistryBuilder.build();
} finally {
}
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
? mNativeModuleCallExceptionHandler
: mDevSupportManager;
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSModuleRegistry(jsModulesBuilder.build())
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START);
try {
catalystInstance = catalystInstanceBuilder.build();
} finally {
}
if (mBridgeIdleDebugListener != null) {
catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
}
try {
catalystInstance.getReactQueueConfiguration().getJSQueueThread().callOnQueue(
new Callable<Void>() {
@Override
public Void call() throws Exception {
reactContext.initializeWithInstance(catalystInstance);
try {
catalystInstance.runJSBundle();
} finally {
}
return null;
}
}).get();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException) e.getCause();
} else {
throw new RuntimeException(e);
}
}
return reactContext;
}
需要兩個參數:JavaScriptExecutor和JSBundleLoader,都是之前創建的,具體作用后續用到會解釋,先看一下繼承關系:
它就是一個普通的Android Context子類,使用的是Application Context.
這個方法的重點不是創建Context,而是創建CatalystInstance,用于鏈接Java與JS引擎。
這兩個方法調用了兩次processPackage。?
-
CoreModulesPackage
- AndroidInfoModule : 獲取Android版本號和ServerHost
- DeviceEventManagerModule:事件處理
- ExceptionsManagerModule :異常處理
- Timing :Timer,會監聽屏幕每幀的變化,做一些處理
- SourceCodeModule: 可以獲取到Bundle的地址
- UIManagerModule:UI管理器,允許JS創建和修改native的View
- 一些debug相關的模塊
還有一些JS Module接口,和JS模塊的名字一致,還沒有看JS代碼,具體功能就不描述了,看名字應該能猜出來: - RCTDeviceEventEmitter
- JSTimersExecution
- RCTEventEmitter
- RCTNativeAppEventEmitter
- AppRegistry
- Systrace
- HMRClient
- SamplingProfiler
- RCTDebugComponentOwnership
-
MainPackage
- AppStateModule :獲取App前后臺狀態
- AsyncStorageModule:操作數據庫,保存的是catalyst的一些狀態
- CameraRollManager :獲取設備相冊的照片
- ClipboardModule : 允許JS操作剪貼板
- DatePickerDialogModule :JS可以調起native的日期控件
- DialogModule:JS調起native的對話框
- FrescoModule:Fresco庫初始化和清理數據
- I18nManagerModule :國際化相關
- ImageEditingManager :圖片裁剪
- ImageLoaderModule:圖片加載器
- ImageStoreManager :圖片內存緩存
- IntentModule : 打開其他的URL或Activity
- LocationModule:提供JS獲取位置信息
- NativeAnimatedModule :創建、管理Native的動畫
- XMLHttpRequest :實現了JS的XMLHttpRequest接口
- NetInfoModule :獲取網絡狀態信息
- PermissionsModule : Android權限管理
- ShareModule :Android自帶的分享
- StatusBarModule :改變狀態欄樣式
- TimePickerDialogModule : 調起Android選擇時間控件
- ToastModule : 使用Android Toast
- VibrationModule : 管理設備震動
- WebSocketModule:基于OkHttp的ws實現
該模塊中還一些ViewManager(創建管理Android的View),具體功能 先不介紹了,之后再專門寫一篇關于View的文章 - ARTRenderableViewManager
- ARTRenderableViewManager
- ARTRenderableViewManager
- ARTSurfaceViewManager
- ReactDialogPickerManager
- ReactDrawerLayoutManager
- ReactDropdownPickerManager
- ReactHorizontalScrollViewManager
- ReactImageManager
- ReactModalHostManager
- ReactProgressBarViewManager
- ReactRawTextManager
- ReactScrollViewManager
- ReactSliderManager
- ReactSwitchManager
- FrescoBasedReactTextInlineImageViewManager
- ReactTextInputManager
- ReactTextViewManager
- ReactToolbarManager
- ReactViewManager
- ReactViewPagerManager
- ReactVirtualTextViewManager
- ReactWebViewManager
- RecyclerViewBackedScrollViewManager
- SwipeRefreshLayoutManager
列出上面這些是為了方便查看,以后開發時看能不能復用。回到先面說的processPackage,會把上面列出的module分別放到NativeModuleRegistry和JavaScriptModuleRegistry的map中。接下來就該創建CatalystInstance.來看一下它的構造函數:
private CatalystInstanceImpl(
final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,
final JavaScriptExecutor jsExecutor,
final NativeModuleRegistry registry,
final JavaScriptModuleRegistry jsModuleRegistry,
final JSBundleLoader jsBundleLoader,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
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();
}
終于看到一些核心點的東西了。先介看下第一個參數: ReactQueueConfigurationSpec,是用于配置執行消息的線程的,其它的參數上面都看到過。
從這里猜測JS與Native之間交互,雙工通訊都有一個線程,每個線程有一個對列,特別像Android的Handler機制。看了一下ReactQueueConfigurationImpl.create的實現,確實是使用了Android的Handler機制,其準備好了三個線程:Android UI線程,JS 線程和NativeModulesQueue線程,后兩個后強線程并結合looper使用。第一線程很明顯是更新UI的,JS線程是JSCExecutor使用,native調用js會在這個線程執行,Native線程是執行JS調用native。
這里還有一個關鍵參數: ModuleRegistryHolder
mJavaRegistry.getModuleRegistryHolder(this)
這個要創建一個ModuleRegistryHolder對象,并把所有Native Module傳遞給c++層。
4. Module處理
執行到這里Native Module放在NativeModuleRegistry,JS Module放在JavaScriptModuleRegistry中,先來看一下它們相關的類圖
Native Module的信息在Java和C ++中都保存了一份。
JS Module只在Java中保存。在JS代碼里應該會有Module的處理,這些Module真正的實現在JS中,Native調用只需要傳遞類名和方法名就可以。
JS Module在Native中只是一個接口,并未真正的實現
5. Native初始化
所有的參數準備好了之后,就需要初始化Bridge,這是JS與Native通訊的關鍵,是個native函數(注意這塊native的含意,對于RN來說,native是指Android或iOS,而在java或Android中Native就是指c/c++).
這里有一個細節,initializeBridge是一個jni方法,CatalystInstanceImpl.cpp只是一個空殼子,真的實現是在Instance.cpp中,應該是為了Android和iOS能復用。查看c++代碼,最終走到這里
void Instance::initializeBridge(
std::unique_ptr<InstanceCallback> callback,
std::shared_ptr<JSExecutorFactory> jsef,
std::shared_ptr<MessageQueueThread> jsQueue,
std::unique_ptr<MessageQueueThread> nativeQueue,
std::shared_ptr<ModuleRegistry> moduleRegistry) {
callback_ = std::move(callback);
jsQueue->runOnQueueSync(
[this, &jsef, moduleRegistry, jsQueue,
nativeQueue=folly::makeMoveWrapper(std::move(nativeQueue))] () mutable {
nativeToJsBridge_ = folly::make_unique<NativeToJsBridge>(
jsef.get(), moduleRegistry, jsQueue, nativeQueue.move(), callback_);
});
CHECK(nativeToJsBridge_);
}
其實就是在上面創建的JS線程是創建一個c++層的NativeToJsBridge的對象,來看一下它的構造函數
NativeToJsBridge::NativeToJsBridge(
JSExecutorFactory* jsExecutorFactory,
std::shared_ptr<ModuleRegistry> registry,
std::shared_ptr<MessageQueueThread> jsQueue,
std::unique_ptr<MessageQueueThread> nativeQueue,
std::shared_ptr<InstanceCallback> callback)
: m_destroyed(std::make_shared<bool>(false))
, m_mainExecutorToken(callback->createExecutorToken())
, m_delegate(
std::make_shared<JsToNativeBridge>(
this, registry, std::move(nativeQueue), callback)) {
std::unique_ptr<JSExecutor> mainExecutor =
jsExecutorFactory->createJSExecutor(m_delegate, jsQueue);
// cached to avoid locked map lookup in the common case
m_mainExecutor = mainExecutor.get();
registerExecutor(m_mainExecutorToken, std::move(mainExecutor), jsQueue);
}
在對象初始化時還創建了一個JsToNativeBridge對象,通過代碼里的注釋我們可以知道:
- JsToNativeBridge :管理JS調用Native
- NativeToJsBridge : 管理Native調用JS,也管理相關的線程
先創建一個JSExecutor,來看一下實現
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate, std::shared_ptr<MessageQueueThread> jsQueue) {
return std::unique_ptr<JSExecutor>(
new JSCExecutor(delegate, jsQueue, m_cacheDir, m_jscConfig));
}
創建了一個JSCExecutor對象
JSCExecutor::JSCExecutor(std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> messageQueueThread,
const std::string& cacheDir,
const folly::dynamic& jscConfig) throw(JSException) :
m_delegate(delegate),
m_deviceCacheDir(cacheDir),
m_messageQueueThread(messageQueueThread),
m_jscConfig(jscConfig) {
initOnJSVMThread();
SystraceSection s("setBatchedBridgeConfig");
folly::dynamic nativeModuleConfig = folly::dynamic::array();
{
SystraceSection s("collectNativeModuleNames");
std::vector<std::string> names = delegate->moduleNames();
for (auto& name : delegate->moduleNames()) {
nativeModuleConfig.push_back(folly::dynamic::array(std::move(name)));
}
}
folly::dynamic config =
folly::dynamic::object
("remoteModuleConfig", std::move(nativeModuleConfig));
SystraceSection t("setGlobalVariable");
setGlobalVariable(
"__fbBatchedBridgeConfig",
folly::make_unique<JSBigStdString>(detail::toStdString(folly::toJson(config))));
setGlobalVariable(
"__fbBatchedBridgeSerializeNativeParams",
folly::make_unique<JSBigStdString>(""));
}
這里把之前傳到c++的NativeModule列表遍歷一下,弄到一個數據里,然后生成一個JSON串。
void JSCExecutor::setGlobalVariable(std::string propName, std::unique_ptr<const JSBigString> jsonValue) {
try {
SystraceSection s("JSCExecutor.setGlobalVariable",
"propName", propName);
auto globalObject = JSContextGetGlobalObject(m_context);
String jsPropertyName(propName.c_str());
String jsValueJSON = jsStringFromBigString(*jsonValue);
auto valueToInject = JSValueMakeFromJSONString(m_context, jsValueJSON);
JSObjectSetProperty(m_context, globalObject, jsPropertyName, valueToInject, 0, NULL);
} catch (...) {
std::throw_with_nested(std::runtime_error("Error setting global variable: " + propName));
}
}
先來看一下__fbBatchedBridgeConfig的值是什么
{
"remoteModuleConfig":[
["ImageStoreManager"],
["ImageEditingManager"],
["LocationObserver"],
["Networking"],
["SourceCode"],
["NetInfo"],
["ShareModule"],
["ImageLoader"],
["FrescoModule"],
["ToastAndroid"],
["Timing"],
["Vibration"],
["UIManager"],
["DatePickerAndroid"],
["AsyncSQLiteDBStorage"],
["I18nManager"],
["AppState"],
["NativeAnimatedModule"],
["CameraRollManager"],
["PermissionsAndroid"],
["TimePickerAndroid"],
["AndroidConstants"],
["StatusBarManager"],
["ExceptionsManager"],
["AnimationsDebugModule"],
["DialogManagerAndroid"],
["IntentAndroid"],
["WebSocketModule"],
["Clipboard"],
["DeviceEventManager"]
]
}
這些就是我們上面列出的Module名字.
setGlobalVariable像是扔到一個全局的配置中,至此initializeBridge分析完了。
回到createReactContext中,看一下剩下部分
try {
catalystInstance.getReactQueueConfiguration().getJSQueueThread().callOnQueue(
new Callable<Void>() {
@Override
public Void call() throws Exception {
reactContext.initializeWithInstance(catalystInstance);
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
try {
catalystInstance.runJSBundle();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(RUN_JS_BUNDLE_END);
}
return null;
}
}).get();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException) e.getCause();
} else {
throw new RuntimeException(e);
}
}
這段代碼最主要就是在JS的線種隊列中添加一個Callable,然后執行,最終調用到CatalystInstanceImpl.loadScriptFromFile(這里有三種實現,FromFile, FromAssets, FromOptimizedBundle,本次分析就選擇從File中加載,其原理是一樣的),也就是要加載JS代碼了。這是一個native方法,經過幾次調用會走到下面的方法
void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) throw(JSException) {
...
String jsScript = jsStringFromBigString(*script);
...
String jsSourceURL(sourceURL.c_str());
evaluateScript(m_context, jsScript, jsSourceURL);
bindBridge();
flush();
ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
}
evaluateScript應該是調用了JavaScriptCore中的方法,驗證JS文件是否有效u并解析執行,bindBridge從__fbBatchedBridge中取出幾個屬性:
- callFunctionReturnFlushedQueue
- invokeCallbackAndReturnFlushedQueue
- flushedQueue
這三個都是MessageQueue.js中的方法,把它們當用c++的對象,保存在JSCExecutor中,關于這三個方法是如何設置的請參考React Native通訊原理。
void JSCExecutor::flush() {
auto result = m_flushedQueueJS->callAsFunction({});
try {
auto calls = Value(m_context, result).toJSONString();
m_delegate->callNativeModules(*this, std::move(calls), true);
} catch (...) {
std::string message = "Error in flush()";
try {
message += ":" + Value(m_context, result).toString().str();
} catch (...) {
// ignored
}
std::throw_with_nested(std::runtime_error(message));
}
}
先調用JS的flushedQueue,返回一個JS的一個隊列(本次拿到的隊列為空),對于RN如何把JS對象轉換成Native的對象,之后再寫篇文章分析。
至此createReactContext就完成了。
6. setupReactContext
創建ReactApplicationContext是在AsyncTask的doInBackground中執行的,?而在onPostExecute中只調用了setupReactContext,看一下它的實現
private void setupReactContext(ReactApplicationContext reactContext) {
...
CatalystInstance catalystInstance =
Assertions.assertNotNull(reactContext.getCatalystInstance());
catalystInstance.initialize();
mDevSupportManager.onNewReactContextCreated(reactContext);
mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
moveReactContextToCurrentLifecycleState();
for (ReactRootView rootView : mAttachedRootViews) {
attachMeasuredRootViewToInstance(rootView, catalystInstance);
}
ReactInstanceEventListener[] listeners =
new ReactInstanceEventListener[mReactInstanceEventListeners.size()];
listeners = mReactInstanceEventListeners.toArray(listeners);
for (ReactInstanceEventListener listener : listeners) {
listener.onReactContextInitialized(reactContext);
}
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
- catalystInstance.initialize()會初始化所有Native Module
- 添加內存警告的回調
- 如果是Resumed狀態,回調listener
- 給root view添加內容,重點看一下這個
private void attachMeasuredRootViewToInstance(
ReactRootView rootView,
CatalystInstance catalystInstance) {
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "attachMeasuredRootViewToInstance");
UiThreadUtil.assertOnUiThread();
// Reset view content as it's going to be populated by the application content from JS
rootView.removeAllViews();
rootView.setId(View.NO_ID);
UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
int rootTag = uiManagerModule.addMeasuredRootView(rootView);
rootView.setRootViewTag(rootTag);
@Nullable Bundle launchOptions = rootView.getLaunchOptions();
WritableMap initialProps = Arguments.makeNativeMap(launchOptions);
String jsAppModuleName = rootView.getJSModuleName();
WritableNativeMap appParams = new WritableNativeMap();
appParams.putDouble("rootTag", rootTag);
appParams.putMap("initialProps", initialProps);
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
設置RootView,然后獲取AppRegistry的代理對象,因為所有的JSModule在Android中都是接口,無法直接調用,所以就使用了代理對象。這樣也有一個好處就是,所有Native調用JS都會收斂到JavaScriptModuleInvocationHandler中,有一個統一的入口
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
ExecutorToken executorToken = mExecutorToken.get();
if (executorToken == null) {
FLog.w(ReactConstants.TAG, "Dropping JS call, ExecutorToken went away...");
return null;
}
NativeArray jsArgs = args != null ? Arguments.fromJavaArgs(args) : new WritableNativeArray();
mCatalystInstance.callFunction(
executorToken,
mModuleRegistration.getName(),
method.getName(),
jsArgs
);
return null;
}
從而得知,所有native調用js都是通過CatalystInstance的。在callFunction中會做一些容錯處理,然后調用c++方法callJSFunction。
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) {
....
// 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->callFunction(module, method, arguments);
});
}
------------------------------------------------
void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
try {
auto result = m_callFunctionReturnFlushedQueueJS->callAsFunction({
Value(m_context, String::createExpectingAscii(moduleId)),
Value(m_context, String::createExpectingAscii(methodId)),
Value::fromDynamic(m_context, std::move(arguments))
});
auto calls = Value(m_context, result).toJSONString();
__android_log_print(ANDROID_LOG_DEBUG,"zbljni","JSCExecutor::callFunction moduleId = %s, methodId = %s, result = %s" , moduleId.c_str(), methodId.c_str(), calls.c_str());
m_delegate->callNativeModules(*this, std::move(calls), true);
} catch (...) {
std::throw_with_nested(std::runtime_error("Error calling function: " + moduleId + ":" + methodId));
}
}
m_callFunctionReturnFlushedQueueJS (這個方法的作用參考React Native通訊原理) 就是當作一個函數指針然后調用JS的代碼,也就是調用AppRegistry的runApplication,就是顯示
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);
這里的Component。這樣JS代碼就能顯示到手機上了。UI相關的東西我們之后再單寫文章進行分析。
先看一下執行的結果
D/zbljni (11286): JSCExecutor::callFunction moduleId = AppRegistry, methodId = runApplication, result = [[4,4],[9,9],["[2,[3,15]]","[1,[2]]"],21]
一堆數字,這是什么鬼,看一下是怎么回調native的
void callNativeModules(
JSExecutor& executor, std::string callJSON, bool isEndOfBatch) override {
ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor);
m_nativeQueue->runOnQueue([this, token, callJSON=std::move(callJSON), isEndOfBatch] {
// An exception anywhere in here stops processing of the batch. This
// was the behavior of the Android bridge, and since exception handling
// terminates the whole bridge, there's not much point in continuing.
for (auto& call : react::parseMethodCalls(callJSON)) {
m_registry->callNativeMethod(
token, call.moduleId, call.methodId, std::move(call.arguments), call.callId);
}
if (isEndOfBatch) {
m_callback->onBatchComplete();
m_callback->decrementPendingJSCalls();
}
});
}
前面分析過有三個線程,這里就是在NativeModulesQueue線程中執行,里面有個parseMethodCalls,相信這是解析執行結果的,代碼在MethodCall.cpp中的parseMethodCalls就不粘代碼了, 先來解釋下返回結果的含意:
- [4,4] : Module Id, 是c++層ModuleRegistry的modules_的索引
- [9,9] : Method Id,是Java層JavaModuleWrapper的mMethods的索引
- ["[2,[3,15]]","[1,[2]]"] : Request Params
- 21 : Request Call Id
根據返回結果創建一個c++的 std::vector<MethodCall>,從上面的返回結果,應該是兩次請求,所有會有兩個MethodCall。
void ModuleRegistry::callNativeMethod(ExecutorToken token, unsigned int moduleId, unsigned int methodId,folly::dynamic&& params, int callId) {
...
modules_[moduleId]->invoke(token, methodId, std::move(params));
}
這里modules_是所有Native Module的集合, 至于moduleId是否對應__fbBatchedBridgeConfig中的module還不確定,invoke方法在父類BaseJavaModule中就是執行module中的@ReactMethod的方法,如果有多個應該是由methodid決定的。
通過debug代碼,看到 4:對應的是UIManagerModule, 9 :對應的是setChildren,["[2,[3,15]]","[1,[2]]" :這些對應方法的參數
public void setChildren( int viewTag, ReadableArray childrenTags) {
...
}
還有?個疑問,在調用setChildren之前還調用了很多其他的方法,為什么runApplication的返回結果偏偏是它。由于對JS代碼不熟悉,所以猜測在調用runApplication之后,JS開始調用native創建UI,這些操作都是添加到JS的工作隊列中。由于時序問題,剛好runApplication執行完,隊列中需要執行setChildren,所以就返回回來了。如果有了解的朋友歡迎解惑。
其實,runApplication的調用就是Native與JS通訊的流程。
這篇文章只是從代碼的角度去分析,并未做相關的總結,之后會寫相關的文章去介紹。如果有感興趣的topic,歡迎留言。
已完成topic: