最近咱們公司忽然開始涉足react-native開發,老實說,我心裏是拒絕的。其一,是由於目前我對於原生開發還不夠精通,不想忽然轉向,其二是由於react-native目前尚未1.0的正式版本,仍然處於探索期不穩定。其三,我特麼開發react-native用的是windows啊,人家facebook的工程師開發這玩兒用的都是mac。看在工資的份兒上,我開始探索react-native。java
首先咱們要明白如下幾點:react
因此咱們的問題其實只有兩點:那就是集中精力觀察「詞典」是怎麼傳遞到雙方手裏的,以及兩方是怎麼傳遞數據的。android
首先,對於詞典仍是正確解釋一下,它是某種config,某種配置文件,每次Java層收到js層傳來的的信息,都會讀取這個文件,而後才能理解Java層的意思。Java層也是同樣。他們對應RN的代碼的類分別是:NativeModuleRegistry和JavaScriptModuleRegistryc++
初始化的開端源自ReactActivity,這是react-native中的類,它的onCreate()方法中是這麼作的:windows
@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();
}
}
mReactRootView = createRootView();
//這是最重要的一步
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
getMainComponentName(),
getLaunchOptions());
setContentView(mReactRootView);
mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
}複製代碼
mRootView是一個layout,繼承自FrameLayout,一切的js渲染從這個Layout上開始,它的startReactApplication()方法以下:react-native
public void startReactApplication( ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle launchOptions) {
UiThreadUtil.assertOnUiThread();
// TODO(6788889): Use POJO instead of bundle here, apparently we can't just use WritableMap
// here as it may be deallocated in native after passing via JNI bridge, but we want to reuse
// it in the case of re-creating the catalyst instance
Assertions.assertCondition(
mReactInstanceManager == null,
"This root view has already been attached to a catalyst instance manager");
mReactInstanceManager = reactInstanceManager;
mJSModuleName = moduleName;
mLaunchOptions = launchOptions;
//這是關鍵
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
mReactInstanceManager.createReactContextInBackground();
}
// We need to wait for the initial onMeasure, if this view has not yet been measured, we set which
// will make this view startReactApplication itself to instance manager once onMeasure is called.
if (mWasMeasured) {
attachToReactInstanceManager();
}
}複製代碼
這裏有一個ReactInstanceManager,它的做用就是管理CatalystInstance的實例,CatalystInstance是什麼?這是一個上層抽象的調用接口),Java和Js均可以經過這個去調用對方,固然,那兩個類都是抽象的,實際上都是經過它們的XXXXImpl類來實現具體的功能。架構
那麼咱們接着往下,注意咱們的目的:瞭解初始化時如何傳遞那兩本「詞典」的,mReactInstanceManager.createReactContextInBackground();這個方法就直接調用到了它的實現類:XReactInstanceManagerImpl中的createReactContextInBackground而後接下來的流程就是:app
createReactContextInBackground()------> recreateReactContextInBackgroundInner(); -------> recreateReactContextInBackgroundFromBundleLoader(); ---------> recreateReactContextInBackground() ;框架
到了recreateReactContextInBackground()這個方法大概是這樣的:ide
private void recreateReactContextInBackground( JavaScriptExecutor.Factory jsExecutorFactory, JSBundleLoader jsBundleLoader) {
UiThreadUtil.assertOnUiThread();
//構造參數
ReactContextInitParams initParams =
new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
if (mReactContextInitAsyncTask == null) {
// No background task to create react context is currently running, create and execute one.
//執行了一個AsyncTask.......
mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
mReactContextInitAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, initParams);
} else {
// Background task is currently running, queue up most recent init params to recreate context
// once task completes.
mPendingReactContextInitParams = initParams;
}
}複製代碼
好咱們接下來看這個線程內部的細節,重點看doInBackground()這個方法:
@Override
protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {
// TODO(t11687218): Look over all threading
// Default priority is Process.THREAD_PRIORITY_BACKGROUND which means we'll be put in a cgroup
// that only has access to a small fraction of CPU time. The priority will be reset after
// this task finishes: https://android.googlesource.com/platform/frameworks/base/+/
d630f105e8bc0021541aacb4dc6498a49048ecea/core/java/android/os/AsyncTask.java#256
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
Assertions.assertCondition(params != null && params.length > 0 && params[0] != null);
try {
JavaScriptExecutor jsExecutor = params[0].getJsExecutorFactory().create();
//createReactContext()這個方法被執行
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);
}
}複製代碼
接下去看createReactContext(jsExecutor, params[0].getJsBundleLoader())這個方法:
/** * @return instance of {@link ReactContext} configured a {@link CatalystInstance} set */
private ReactApplicationContext createReactContext( JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader) {
FLog.i(ReactConstants.TAG, "Creating react context.");
ReactMarker.logMarker(CREATE_REACT_CONTEXT_START);
mSourceUrl = jsBundleLoader.getSourceUrl();
//你瞧,以前提到的兩本「詞典」,他們的Builder已經露面了。
NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
if (mUseDeveloperSupport) {
reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
}
ReactMarker.logMarker(PROCESS_PACKAGES_START);
Systrace.beginSection(
TRACE_TAG_REACT_JAVA_BRIDGE,
"createAndProcessCoreModulesPackage");
try {
//CoreModulesPackage裏面定義了RN框架核心的一些Java和JS的module
//經過processPackage()方法寫入到兩本「詞典」的Builder中
CoreModulesPackage coreModulesPackage =
new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
// TODO(6818138): Solve use-case of native/js modules overriding
//這裏是開發者本身定義或封裝的一些組件或者事件的package
for (ReactPackage reactPackage : mPackages) {
Systrace.beginSection(
TRACE_TAG_REACT_JAVA_BRIDGE,
"createAndProcessCustomReactPackage");
try {
processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
ReactMarker.logMarker(PROCESS_PACKAGES_END);
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_START);
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "buildNativeModuleRegistry");
NativeModuleRegistry nativeModuleRegistry;
try {
//好了,建立了用於翻譯Java端的「詞典」
nativeModuleRegistry = nativeRegistryBuilder.build();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
}
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
? mNativeModuleCallExceptionHandler
: mDevSupportManager;
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
//建立了用於翻譯JS端的「詞典」
.setJSModuleRegistry(jsModulesBuilder.build())
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
//到目前爲止。兩本「詞典」都已經建立完畢,並且所有都在CatalystInstance這個類的**實現類的Builder中**,此時你能夠回憶一下整個過程,理清一下思路。
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;
try {
//這個Build()很關鍵,它用實現類的Builder建立了一個CatalystInstance類。
catalystInstance = catalystInstanceBuilder.build();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);
}
if (mBridgeIdleDebugListener != null) {
catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
}
ReactMarker.logMarker(RUN_JS_BUNDLE_START);
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 {
//在這裏就運行js代碼說明至少在這個方法以前,「詞典」應該傳過去了
//因而咱們刻印回去聚焦到catalystInstance = catalystInstanceBuilder.build();這段代碼
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);
}
}
return reactContext;
}
//將package中的關於Java和js的東西分別添加到兩本「詞典」的builder中
private void processPackage( ReactPackage reactPackage, ReactApplicationContext reactContext, NativeModuleRegistry.Builder nativeRegistryBuilder, JavaScriptModuleRegistry.Builder jsModulesBuilder) {
for (NativeModule nativeModule : reactPackage.createNativeModules(reactContext)) {
nativeRegistryBuilder.add(nativeModule);
}
for (Class<? extends JavaScriptModule> jsModuleClass : reactPackage.createJSModules()) {
jsModulesBuilder.add(jsModuleClass);
}
}複製代碼
catalystInstanceBuilder.build()這段代碼具體實現以下:
public CatalystInstanceImpl build() {
return new CatalystInstanceImpl(
Assertions.assertNotNull(mReactQueueConfigurationSpec),
Assertions.assertNotNull(mJSExecutor),
Assertions.assertNotNull(mRegistry),
Assertions.assertNotNull(mJSModuleRegistry),
Assertions.assertNotNull(mJSBundleLoader),
Assertions.assertNotNull(mNativeModuleCallExceptionHandler));
}複製代碼
這個方法的做用就是直接new了一個CatalystInstanceImpl類,那麼咱們接下去看CatalystInstanceImpl類的構造方法:
private CatalystInstanceImpl( final ReactQueueConfigurationSpec ReactQueueConfigurationSpec, final JavaScriptExecutor jsExecutor, final NativeModuleRegistry registry, final JavaScriptModuleRegistry jsModuleRegistry, final JSBundleLoader jsBundleLoader, NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
FLog.d(ReactConstants.TAG, "Initializing React Xplat Bridge.");
mHybridData = initHybrid();
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
ReactQueueConfigurationSpec,
new NativeExceptionHandler());
mBridgeIdleListeners = new CopyOnWriteArrayList<>();
//這個就是Java層要傳遞給Js層的「詞典」
mJavaRegistry = registry;
mJSModuleRegistry = jsModuleRegistry;
mJSBundleLoader = jsBundleLoader;
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
mTraceListener = new JSProfilerTraceListener(this);
//在這個方法裏,就把Java暴露給Js的詞典傳了進去
initializeBridge(
new BridgeCallback(this),
jsExecutor,
mReactQueueConfiguration.getJSQueueThread(),
mReactQueueConfiguration.getNativeModulesQueueThread(),
//getModuleRegistryHolder()這個方法不過是一種holder,對「詞典」作了一些封裝。
mJavaRegistry.getModuleRegistryHolder(this));
mMainExecutorToken = getMainExecutorToken();
}
//你瞧,native方法,直接call到C++層,接下來,由C++層經過各類折騰,而後生成某種配置文件,轉發到Js端
private native void initializeBridge(ReactCallback callback, JavaScriptExecutor jsExecutor, MessageQueueThread jsQueue, MessageQueueThread moduleQueue, ModuleRegistryHolder registryHolder);複製代碼
好了,關於「詞典」是如何傳遞過去的,就解釋到這裏,雖然C++層可能有更多操做,可是目的就是一個,把Java類轉化成一個JS讀得懂的格式的文件。 那麼咱們接下來看看C++是如何作好傳聲筒的
初始化完成以後,Java端和Js端都有了「詞典」,就可把本身的意圖翻譯成對方能聽得懂的話了,這個時候交流就會暢通了。
這個問題首先Java層應該作的是找到那本Js的詞典,因此咱們應該尋找Java層是在哪裏調用到了JSModuleRegistry這個類的,
讓咱們退回到ReactContextInitAsyncTask的doInBackground方法中,在詞典傳遞完畢以後,這個方法基本執行完畢,接下來是
@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;
}
}複製代碼
setupReactContext(result.get())這個方法,而後這個方法又會調用attachMeasuredRootViewToInstance()方法:
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);
//在這裏,Java找到了那本Js的「詞典」,而後runApplication
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}複製代碼
其實catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams)這段代碼比較複雜,首先getJSModule方法具體實如今catalystInstanceImpl中,而後會調用到mJSModuleRegistry.getJavaScriptModule(this, executorToken, jsInterface)中,代碼以下:
public synchronized <T extends JavaScriptModule> T getJavaScriptModule( CatalystInstance instance, 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, instance, registration));
instancesForContext.put(moduleInterface, interfaceProxy);
return (T) interfaceProxy;
}複製代碼
什麼動態代理不用理他,看 new JavaScriptModuleInvocationHandler(executorToken, instance, registration)這個類:
private static class JavaScriptModuleInvocationHandler implements InvocationHandler {
private final WeakReference<ExecutorToken> mExecutorToken;
private final CatalystInstance mCatalystInstance;
private final JavaScriptModuleRegistration mModuleRegistration;
public JavaScriptModuleInvocationHandler( ExecutorToken executorToken, CatalystInstance catalystInstance, JavaScriptModuleRegistration moduleRegistration) {
mExecutorToken = new WeakReference<>(executorToken);
mCatalystInstance = catalystInstance;
mModuleRegistration = moduleRegistration;
}
//關鍵
@Override
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;
}
}
}複製代碼
invoke方法經過mCatalystInstance調用了callFunction()方法;不用多想,咱們直接在實現類中去找這個方法:
@Override
public void callFunction( ExecutorToken executorToken, final String module, final String method, final NativeArray arguments) {
if (mDestroyed) {
FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed.");
return;
}
if (!mAcceptCalls) {
throw new RuntimeException("Attempt to call JS function before JS bundle is loaded.");
}
callJSFunction(executorToken, module, method, arguments);
}
private native void callJSFunction(ExecutorToken token,String module, String method,NativeArray arguments);複製代碼
callFunction()方法裏面調用了callJSFunction()這個本地方法,而後由C++作轉發,這個本地方法傳遞的參數有token,包名,方法名,和參數,
至此,從Java端調用到Js端的過程,到這裏能夠宣告結束了。
待續。