其實沒那麼複雜!探究react-native通訊機制

近段時間來Android上最火的框架非react native莫屬了,這裏我不去評價這個框架的好壞,畢竟只有用過的人才會有深入的體會。可是我我的有一個習慣,在使用一個開源庫以前,必定要看過它的源碼,不說百分百搞懂吧,至少得弄清楚它的工做原理,因此在使用RN以前我就看了看它的源碼。不看不知道,一看嚇一跳,它其中最核心的那一部分——java和js的通訊寫的確實是很是的精妙,把整個流程搞懂之後讓我受益無窮。java

這裏插一句題外話,阿里的weex也立刻就要開源了,我身邊的小夥伴也有已經拿到源碼投身於其中的,等到它開源的時候我也會去看看它的源碼,瞭解下它和RN的區別究竟是什麼。react

廢話也不說了,讓咱們好好看看RN的通訊機制是怎樣的吧。git

前言

在看這篇文章以前,你得確保你有必定的RN開發基礎,若是是零基礎的同窗,建議你們先去看看這個系列的文章,裏面很是清楚的介紹瞭如何使用RN來進行開發。固然若是你不知足僅僅是[瞭解]的話,也能夠去網上查閱關於RN的一些資料,其實裏面一些東西是很是值得一看的,好比virtual DOM,diff機制等等。json

通訊方式

咱們所說的[通訊],指的是RN中Java和js的通訊,也就是js部分中的那些jsx代碼是如何轉化成一個java層真實的view和事件的,java層又是如何調用js來找出它所須要的那些view和事件的。react-native

簡單的說,RN的兩端通訊靠的是一張配置表,java端和js端持有同一張表,通訊的時候就是靠這張表的各個條目的對應來進行的。緩存

connection

大體的就是和上面這張圖同樣,兩端各持有一份相同的config,config中有一些已經註冊的模塊,兩端的通訊就是經過傳輸這樣的「A」,「B」或者」C」來實現的。這個config對應到RN的代碼是NativeModuleRegistry和JavaScriptModuleRegistry。若是你們想象不出來的話我能夠給你們打個比喻,java端和js端的通訊就比如一箇中國人和一個美國人在對話,而這個config,也就是註冊表就至關於兩個翻譯,有了翻譯兩個語言不通的人才能正常交流。那這兩張表是如何生成的呢?仍是讓咱們從代碼中尋找答案吧。weex

首先咱們知道在使用RN的時候,咱們對應的activity要繼承自ReactActivity而且重寫一個叫作getPackages的方法。app

1
2
3
4
5
6
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage()
);
}

這個MainReactPackage是RN幫咱們生成好的,其中定義了一些基礎的組件和事件,具體就不說了,你們能夠本身去看一下源碼。若是你想要自定義一些組件或者事件的話必需要本身去寫一個package,至於怎麼寫你們看我前面提到的那一系列文章就知道了。而咱們前面提到的那兩個註冊表——NativeModuleRegistry和JavaScriptModuleRegistry就是經過這樣的package去生成的,具體方法咱們看下去就知道了。框架

既然咱們的activity繼承自了ReactActivity,那咱們就去看看ReactActivity裏面作了什麼。第一個要看的固然是onCreate函數。異步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

if (getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {
// Get permission to show redbox in dev builds.
if (!Settings.canDrawOverlays(this)) {
Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
startActivity(serviceIntent);
FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
}
}

mReactInstanceManager = createReactInstanceManager();
ReactRootView mReactRootView = createRootView();
mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());
setContentView(mReactRootView);
}

能夠看到咱們建立了一個ReactInstanceManager,看看是怎麼建立的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
.setApplication(getApplication())
.setJSMainModuleName(getJSMainModuleName())
.setUseDeveloperSupport(getUseDeveloperSupport())
.setInitialLifecycleState(mLifecycleState);

for (ReactPackage reactPackage : getPackages()) {
builder.addPackage(reactPackage);
}

String jsBundleFile = getJSBundleFile();

if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName(getBundleAssetName());
}

return builder.build();
}

中間有一段這樣的代碼

1
2
3
for (ReactPackage reactPackage : getPackages()) {
builder.addPackage(reactPackage);
}

經過builder模式把咱們的package注入到了builder中而且最後調用build方法建立出一個ReactInstanceManagerImpl實例。

咱們回過頭來看onCreate函數,在這以後咱們建立一個ReactRootView做爲咱們的根視圖,而且調用它的startReactApplication函數,從函數名字就能夠看出來,這個函數的做用很是重要,從這兒開始,算是啓動了咱們的RN程序。

在startReactApplication函數中咱們調用了ReactInstanceManager的createReactContextInBackground()方法去構造屬於RN程序的上下文。在這個方法中會去判斷是不是開發模式,你們能夠在本身的activity中重寫getUseDeveloperSupport()去更改模式。模式不一樣的主要區別在於獲取JSBundle的方式不一樣。若是你是開發模式的,就會從server端獲取,若是不是,則是從文件中獲取。這裏咱們只關注前者。最終會走到onJSBundleLoadedFromServer方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void onJSBundleLoadedFromServer() {
recreateReactContextInBackground(
new JSCJavaScriptExecutor.Factory(),
JSBundleLoader.createCachedBundleFromNetworkLoader(
mDevSupportManager.getSourceUrl(),
mDevSupportManager.getDownloadedJSBundleFile()));
}

private void recreateReactContextInBackground(
JavaScriptExecutor.Factory jsExecutorFactory,
JSBundleLoader jsBundleLoader) {
..........

mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
mReactContextInitAsyncTask.execute(initParams);
}

在該方法中咱們調用JSBundleLoader的createCachedBundleFromNetworkLoader方法去建立了一個JSBundleLoader。它的主要做用是去加載JSBundle。你們能夠去看看JSBundleLoader這個類,其中還有兩種建立loader的方式,若是咱們不是開發模式,調用的是createFileLoader,也就是說release的狀況下咱們須要用gradle生成了JSBundle以後將其放在assets目錄上或者文件中。

下面讓咱們看看以後的recreateReactContextInBackground方法。

它會調用了一個叫作mReactContextInitAsyncTask的AsyncTask去執行異步任務。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {
Assertions.assertCondition(params != null && params.length > 0 && params[0] != null);
try {
JavaScriptExecutor jsExecutor =
params[0].getJsExecutorFactory().create(
mJSCConfig == null ? new WritableNativeMap() : mJSCConfig.getConfigMap());
return Result.of(createReactContext(jsExecutor, params[0].getJsBundleLoader()));
} catch (Exception e) {
// Pass exception to onPostExecute() so it can be handled on the main thread
return Result.of(e);
}
}

咱們能夠看到它的doInBackground方法調用了createReactContext()方法去建立上下文。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
FLog.i(ReactConstants.TAG, "Creating react context.");
ReactMarker.logMarker(CREATE_REACT_CONTEXT_START);
mSourceUrl = jsBundleLoader.getSourceUrl();
NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
JavaScriptModulesConfig.Builder jsModulesBuilder = new JavaScriptModulesConfig.Builder();

ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
if (mUseDeveloperSupport) {
reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
}

ReactMarker.logMarker(PROCESS_PACKAGES_START);
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"createAndProcessCoreModulesPackage");
try {
CoreModulesPackage coreModulesPackage =
new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}

// TODO(6818138): Solve use-case of native/js modules overriding
for (ReactPackage reactPackage : mPackages) {
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"createAndProcessCustomReactPackage");
try {
processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
ReactMarker.logMarker(PROCESS_PACKAGES_END);

ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_START);
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildNativeModuleRegistry");
NativeModuleRegistry nativeModuleRegistry;
try {
nativeModuleRegistry = nativeRegistryBuilder.build();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
}

ReactMarker.logMarker(BUILD_JS_MODULE_CONFIG_START);
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildJSModuleConfig");
JavaScriptModulesConfig javaScriptModulesConfig;
try {
javaScriptModulesConfig = jsModulesBuilder.build();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(BUILD_JS_MODULE_CONFIG_END);
}

NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
? mNativeModuleCallExceptionHandler
: mDevSupportManager;
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSModulesConfig(javaScriptModulesConfig)
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);

ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START);
// CREATE_CATALYST_INSTANCE_END is in JSCExecutor.cpp
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
CatalystInstance catalystInstance;
try {
catalystInstance = catalystInstanceBuilder.build();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);
}

if (mBridgeIdleDebugListener != null) {
catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
}

reactContext.initializeWithInstance(catalystInstance);

ReactMarker.logMarker(RUN_JS_BUNDLE_START);
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
try {
catalystInstance.runJSBundle();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(RUN_JS_BUNDLE_END);
}

return reactContext;
}

這個方法的代碼就比較多了,可是咱們如今只看咱們所關注的。你們應該還記得咱們的關注點吧?[兩個註冊表NativeModuleRegistry和JavaScriptModuleRegistry是如何生成的]。這裏給出了答案。

1
2
NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
JavaScriptModulesConfig.Builder jsModulesBuilder = new JavaScriptModulesConfig.Builder();

首先建立出兩個builder。

1
2
3
4
5
6
7
try {
CoreModulesPackage coreModulesPackage =
new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}

而後會去new一個CoreModulesPackage而且使用了processPackage方法去處理它,這個CoreModulesPackage裏面定義了RN框架核心的一些Java和JS的module。

下面讓咱們看看processPackage方法。

1
2
3
4
5
6
7
8
9
10
11
12
private void processPackage(
ReactPackage reactPackage,
ReactApplicationContext reactContext,
NativeModuleRegistry.Builder nativeRegistryBuilder,
JavaScriptModulesConfig.Builder jsModulesBuilder) {
for (NativeModule nativeModule : reactPackage.createNativeModules(reactContext)) {
nativeRegistryBuilder.add(nativeModule);
}
for (Class<? extends JavaScriptModule> jsModuleClass : reactPackage.createJSModules()) {
jsModulesBuilder.add(jsModuleClass);
}
}

很簡單,拿到具體的native和JS的module把它們添加到對應的builder中。

再處理完了CoreModulesPackage以後,程序又會去處理咱們在activity中注入的那些package。

1
2
3
4
5
6
7
8
9
10
for (ReactPackage reactPackage : mPackages) {
Systrace.beginSection(
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
"createAndProcessCustomReactPackage");
try {
processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}

接下去就是生成註冊表了。

1
2
3
4
5
6
7
8
9
10
11
12
13
try {
nativeModuleRegistry = nativeRegistryBuilder.build();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
}

try {
javaScriptModulesConfig = jsModulesBuilder.build();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(BUILD_JS_MODULE_CONFIG_END);
}

至此,咱們就把全部的packages,包括RN核心的CoreModulesPackage和咱們activity本身注入的package裏面的各個modules所有寫到了對應Registry的builder中。

如今這兩份註冊表是存在於java端的,那要怎麼傳輸到JS端呢?咱們繼續看下去。

再建立完了對應的註冊表以後,ReactInstanceManagerImpl會經過builder模式去建立一個CatalystInstance的實例CatalystInstanceImpl。在CatalystInstanceImpl的構造函數中會去建立一個ReactBridge。而且會調用initializeBridge(jsExecutor, jsModulesConfig)這個方法。在這個方法中會去經過ReactBridge向C層傳遞一些數據,其中有這麼一段:

1
2
3
bridge.setGlobalVariable(
"__fbBatchedBridgeConfig",
buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));

調用了bridge的setGlobalVariable方法,這是一個native的方法。

1
public native void setGlobalVariable(String propertyName, String jsonEncodedArgument);

這個方法的做用就是先把JavaRegistry格式化成json,而且調用C層的代碼傳輸到js端。因爲本人C層學藝不精,能看懂個大概,怕會有細節講錯,就不帶你們進行分析了。

總結

到這裏,真相就水落石出了,咱們來總結一下吧。

(1) 在程序啓動的時候,也就是ReactActivity的onCreate函數中,咱們會去建立一個ReactInstanceManagerImpl對象

(2) 經過ReactRootView的startReactApplication方法開啓整個RN世界的大門

(3) 在這個方法中,咱們會經過一個AsyncTask去建立ReactContext

(4) 在建立ReactContext過程當中,咱們把咱們本身注入(MainReactPackage)的和系統生成(CoreModulesPackage)的package經過processPackage方法將其中的各個modules注入到了對應的Registry中

(5) 最後經過CatalystInstanceImpl中的ReactBridge將java的註冊表經過jni傳輸到了JS層。

這樣,js層就獲取到了java層的全部接口和方法,至關於一個美國人身邊有了覺得中文翻譯。而js層的註冊表原本就是由java層生成的,因此就至關於一箇中國人身邊有了一個英文翻譯,今後他們就能夠愉快的交流了。

涉及到的重要的類:

ReactInstanceManagerImpl,ReactContext,CatalystInstanceImpl,ReactBridge。

Java->js

前面咱們講了兩端通訊的方式和註冊表是如何從Java端發送到js端的,下面讓咱們講講這樣的準備工做完成之後,java是如何調用js的方法的。

首先,通過上面的學習,其實這個問題已經有了一個初步的答案了,由於[將JavaRegistry從java端傳輸到js端]這個過程就是一個java向js通訊的過程,具體的過程在上一節的總結中已經說了,讓咱們回想一下,以前全部的事情都是在ReactInstanceManagerImpl中ReactContextInitAsyncTask的doInBackground方法中完成的,那這個方法完成以後又作了什麼呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
protected void onPostExecute(Result<ReactApplicationContext> result) {
try {
setupReactContext(result.get());
} catch (Exception e) {
mDevSupportManager.handleException(e);
} finally {
mReactContextInitAsyncTask = null;
}

// Handle enqueued request to re-initialize react context.
if (mPendingReactContextInitParams != null) {
recreateReactContextInBackground(
mPendingReactContextInitParams.getJsExecutorFactory(),
mPendingReactContextInitParams.getJsBundleLoader());
mPendingReactContextInitParams = null;
}
}

能夠看到,在onPostExecute方法中調用了setupReactContext方法,在這個方法中會去調用attachMeasuredRootViewToInstance方法。

1
2
3
4
5
6
7
8
private void attachMeasuredRootViewToInstance(
ReactRootView rootView,
CatalystInstance catalystInstance) {
.......
.......

catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
}

這個方法的最後用了咱們的CatalystInstanceImpl的getJSModule方法,它會去調用JavaScriptModuleRegistry的getJSModule方法,獲取對應的JavaScriptModule,也就是從註冊表中獲取對應的模塊。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public synchronized <T extends JavaScriptModule> T getJavaScriptModule(ExecutorToken executorToken, Class<T> moduleInterface) {
HashMap<Class<? extends JavaScriptModule>, 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, mCatalystInstance, registration));
instancesForContext.put(moduleInterface, interfaceProxy);
return (T) interfaceProxy;
}

這個方法就比較神奇了,首先去緩存中找,若是找到就返回,沒找到就去建立,怎麼建立的呢,用的是動態代理!

1
2
3
4
JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
moduleInterface.getClassLoader(),
new Class[]{moduleInterface},
new JavaScriptModuleInvocationHandler(executorToken, mCatalystInstance, registration));

這裏你們必需要對動態代理有所瞭解,能夠本身去找相關的知識。讓咱們看看JavaScriptModuleInvocationHandler。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ExecutorToken executorToken = mExecutorToken.get();
if (executorToken == null) {
FLog.w(ReactConstants.TAG, "Dropping JS call, ExecutorToken went away...");
return null;
}
String tracingName = mModuleRegistration.getTracingName(method);
mCatalystInstance.callFunction(
executorToken,
mModuleRegistration.getModuleId(),
mModuleRegistration.getMethodId(method),
Arguments.fromJavaArgs(args),
tracingName);
return null;
}

咱們看它最核心的invoke方法。裏面獲取了調用方法的moduleId,methodId和參數,而後調用了CatalystInstanceImpl的callFunction去執行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public void callFunction(
ExecutorToken executorToken,
int moduleId,
int methodId,
NativeArray arguments,
String tracingName) {
synchronized (mJavaToJSCallsTeardownLock) {
if (mDestroyed) {
FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed.");
return;
}

incrementPendingJSCalls();

Assertions.assertNotNull(mBridge).callFunction(executorToken, moduleId, methodId, arguments, tracingName);
}
}

直接調用了ReactBridge的同名函數。

1
public native void callFunction(ExecutorToken executorToken, int moduleId, int methodId, NativeArray arguments, String tracingName);

能夠看到又是一個native函數,具體做用就是將想用調用的方法對應的moduleId,methodId和arguments經過jni傳遞到js端進行調用。而咱們這邊調用的是AppRegistry的runApplication方法,這個方法在js端的做用就是開始運行整個js程序,從而將咱們的RN程序真正的跑起來。

總結

首先,對於咱們java端要調用的js端的類和方法,咱們都必需要註冊到js的註冊表中,這個過程在上一部分的分析中已經帶你們走過了。當真正要調用的時候,步驟是這樣的:

(1) 調用CatalystInstanceImpl這個類的getJSModule方法去獲得對應的JSModule

(2) CatalysInstanceImpl其實是調用JavaScriptModuleRegistry的getJSModule方法去獲取註冊在其中的module

(3) 而後經過動態代理拿到方法的各類參數,包括moduleId,methodId和params

(4) 經過ReactBridge調用jni傳遞到C層

(5) 經過C層再傳遞到js層。

涉及到的重要的類:

CatalystInstanceImpl,JavaScriptModuleRegistry,JavaScriptModuleInvocationHandler,ReactBridge。

JavaCallJS

經過這個圖配合上代碼應該能比較好的理解了。

js->Java

RN的js調java的流程能夠說是讓我以爲最新穎的地方,具體就是js不是直接經過註冊接口去調用java方法的,而是將對應的的參數(moduleId和methodId)push到一個messageQueue中,等待java層的事件來驅動它,當java層的事件傳遞過來之後,js層把messageQueue中全部的數據返回給java層,再經過註冊表JavaRegistry去調用方法。

首先,咱們說了js層是把對應的參數push到messageQueue中,具體的方法是MessageQueue.js的__nativeCall方法。

1
2
3
4
5
6
7
8
9
10
__nativeCall(module, method, params, onFail, onSucc) {

.........

this._queue[MODULE_IDS].push(module);
this._queue[METHOD_IDS].push(method);
this._queue[PARAMS].push(params);

...........
}

能夠看到它把對應的module,method和params push到了隊列裏面,而後就是等待java層的事件驅動。

java層的事件驅動其實也能夠當作java層向js層的通訊,最終會走到MessageQueue.js的callFunctionReturnFlushedQueue方法和invokeCallbackAndReturnFlushedQueue方法。

1
2
3
4
5
6
7
8
callFunctionReturnFlushedQueue(module, method, args) {
guard(() => {
this.__callFunction(module, method, args);
this.__callImmediates();
});

return this.flushedQueue();
}

調用了flushedQueue將MessageQueue中的全部數據經過C層發往java層。

到了java層之後,會回調到NativeModulesReactCallback類執行call方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private class NativeModulesReactCallback implements ReactCallback {

@Override
public void call(ExecutorToken executorToken, int moduleId, int methodId, ReadableNativeArray parameters) {
mReactQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();

synchronized (mJSToJavaCallsTeardownLock) {
// Suppress any callbacks if destroyed - will only lead to sadness.
if (mDestroyed) {
return;
}
mJavaRegistry.call(CatalystInstanceImpl.this, executorToken, moduleId, methodId, parameters);
}
}
.....
}

能夠看到裏面經過JavaRegistry調用了它的call方法。

1
2
3
4
5
6
7
8
9
10
11
12
/* package */ void call(
CatalystInstance catalystInstance,
ExecutorToken executorToken,
int moduleId,
int methodId,
ReadableNativeArray parameters) {
ModuleDefinition definition = mModuleTable.get(moduleId);
if (definition == null) {
throw new RuntimeException("Call to unknown module: " + moduleId);
}
definition.call(catalystInstance, executorToken, methodId, parameters);
}

拿到對應的module,調用call方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void call(
CatalystInstance catalystInstance,
ExecutorToken executorToken,
int methodId,
ReadableNativeArray parameters) {
MethodRegistration method = this.methods.get(methodId);
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, method.tracingName);
try {
this.methods.get(methodId).method.invoke(catalystInstance, executorToken, parameters);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
}

其中根據methodId拿到對應module的方法,執行invoke。

最終執行的是BaseJavaModule的invoke方法。

1
2
3
4
5
6
7
@Override
public void invoke(CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray parameters) {

.........
mMethod.invoke(BaseJavaModule.this, mArguments);
.........
}

經過反射調用最終的方法。

總結

這裏咱們重點要了解的就是js向java的通訊靠的是事件驅動模式。

(1) JS將方法的對應參數push到MessageQueue中,等java端事件傳遞

(2) Java端事件觸發之後,JS層將MessageQueue中的數據經過C層傳遞給java層

(3) C層調用一開始註冊在其中的NativeModulesReactCallback

(4 而後經過JavaRegistry這個Java註冊表拿到對應的module和method

(5) 經過反射執行方法。

涉及到的重要的類:

MessageQueue(JS層),NativeModulesReactCallback,JavaRegistry。

JSCallJava

老規矩,經過圖配合代碼來理解流程。

重要類的做用

最後咱們經過總結一下前面提到的幾個重要的類的做用來加深印象。

ReactInstanceManager:它的做用是建立出ReactContext,CatalystInstance等類,解析package生成註冊表,而且配合ReactRootView管理View的建立,生命週期等功能。

ReactContext:繼承自ContextWrapper,是RN程序本身的上下文,咱們能夠經過getContext()去得到,裏面有CatalystInstance實例,能夠去得到Java和JS的module。

ReactRootView:RN程序的根視圖,startReactApplication方法開啓RN世界的大門。

CatalystInstance:Java端通訊的管理類,提供通訊的環境,方法和回調方法,內部經過ReactBridge進行通訊。

ReactBridge:通訊的核心類,經過jni的方式進行通訊。

NativeModuleRegistry:Java接口的註冊表。

JavascriptModuleRegistry:JS接口的註冊表。

CoreModulePackage:RN核心框架的package,包括Java接口和js接口,前文提到的AppResgitry就是在這裏面註冊的。

MainReactPackage:RN幫咱們封裝的一些通用的Java組件和事件。

JsBundleLoader:用於加載JSBundle的類,其中會根據應用的狀況建立不一樣的loader。

JSBundle:存有js核心邏輯,在release環境下要經過gradle任務去建立而且放在對應的目錄下。

勘誤

很差意思各位!!這裏因爲本人粗心的緣由,代碼沒看全,有一個地方出錯了,這裏寫一個勘誤,若是形成了你們的困擾我再次說一聲很差意思!

前面我說過,JS調Java的機制是[JS把對應的moduleId,methodId和params push到queue中,等待native調用js,而後把MessageQueue中的數據發送到C層再經過jni轉到java層]。可是今天公司裏的大神在和咱們討論RN的時候糾正了個人這個錯誤。

咱們一塊兒看一下MessageQueue.js的__nativeCall方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
__nativeCall(module, method, params, onFail, onSucc) {

..............

this._queue[MODULE_IDS].push(module);
this._queue[METHOD_IDS].push(method);
this._queue[PARAMS].push(params);

var 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;
}
Systrace.counterEvent('pending_js_to_native_queue', this._queue[0].length);
if (__DEV__ && SPY_MODE && isFinite(module)) {
console.log('JS->N : ' + this._remoteModuleTable[module] + '.' +
this._remoteMethodTable[module][method] + '(' + JSON.stringify(params) + ')');
}
}

以前的文章只分析了前面的三個queue.push,可是你們能夠看一下後面的一段:

1
2
3
4
5
6
7
var 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;
}

它會去判斷兩次調用的時差,若是大於MIN_TIME_BETWEEN_FLUSHES_MS(5ms)的話,會調用global.nativeFlushQueueImmediate(this._queue);這個方法去主動把數據傳遞到C層的。也就是說,若是你兩次的通訊時間很短,小於5ms了,那就和我以前講的同樣,可是若是大於5ms,RN在JS端會主動傳遞數據的,這裏必定要注意了!!

 

官方文檔:

    http://wiki.jikexueyuan.com/project/react-native/

相關文章
相關標籤/搜索