Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to analyze the core path of ReactNative For Android framework startup

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)05/31 Report--

How to analyze the core path of ReactNative For Android framework startup, I believe that many inexperienced people are at a loss about this. Therefore, this paper summarizes the causes and solutions of the problem. Through this article, I hope you can solve this problem.

We have analyzed the communication mechanism of ReactNative For Android (RN4A) before. This time we start from the source code to analyze the startup process of RN4A. The startup process is based on the communication mechanism, which involves the principle of the communication mechanism. You can see the previous article.

The above is the RN startup sequence diagram shared by 2016 React.js Conf FB engineers. The whole process is relatively clear. First, the terminal runtime is started, then the terminal context starts the JS runtime, and then the layout is made. Finally, the terminal renders, and finally the View is added to the RootView. Then, let's first understand a few concepts to facilitate our understanding of the whole startup process.

Module:

A module is a collection of API exposed to the caller, and there are two modules in RN4A.

One is the API collection module that the Native layer exposes to the Js layer, that is, NativeModule, such as ToastModule,DialogModule, or the UIManagerModule that creates the View. The business side can implement the NativeModule custom module, expose the module name to the Js layer by rewriting the getName, and expose the API to the Js layer calls by annotations.

The other is the API collection module that the Js layer exposes to the Java layer, namely JavascriptModule, such as DeviceEventEmitter,AppRegistry and so on. The business side can inherit the custom interface module of the JavaScriptModule interface and declare the method corresponding to the Js layer.

Whether it is NativeModule or JavascriptModule, there is a Module,Js layer in the Js layer that maps to each other with the same name and references Module through require.

Module registry:

The information of each module is collected into the module registry. Similarly, there are two kinds of module registries in RN4A, one is the NativeModuleRegistry that collects all the interface information of the Java layer module, and the other is the JavascriptModuleRegistry that collects all the interface information of the Js layer module. After starting RN4A, the terminal stores the registry information in the global variable _ _ fbBatchedBridgeConfig which is interconnected with the front end, so that the same module registry exists in the Js layer and the Java layer.

As the timing diagram proposed by the FB engineer above, boot from the terminal, the entrance is ReactRootView.startReactApplication, and then create ReactContext through ReactContextInitAsycnTask after constructing JavaScriptExecutor&JSBundleLoader. This part mainly creates the registry of NativeModules,JavaScriptModule and its pair, the high-level interface CatalystInstance responsible for the communication between Js and Java, and so on. After creating the ReactContext, get the AppRegistry through CatalystInstance and call its runApplication to start Js Application. The overall process is as follows:

Next, let's get to the point of analyzing the startup of RN4A from the source code (cut the source code appropriately for convenience)

ReactInstanceManager createReactContextInBackground, initializing the ReactNative context through AysncTask. MJSModuleName is the JS Application Name agreed with the front end to start. MLauncahOptions is an optional incoming parameter for the terminal to start the front-end Application.

/ * ReactRootView.java * / public void startReactApplication (ReactInstanceManager reactInstanceManager, String moduleName, @ Nullable Bundle launchOptions) {UiThreadUtil.assertOnUiThread (); mReactInstanceManager = reactInstanceManager; mJSModuleName = moduleName; mLaunchOptions = launchOptions; if (! mReactInstanceManager.hasStartedCreatingInitialContext ()) {mReactInstanceManager.createReactContextInBackground ();} if (mWasMeasured & & mIsAttachedToWindow) {mReactInstanceManager.attachMeasuredRootView (this); mIsAttachedToInstance = true; getViewTreeObserver (). AddOnGlobalLayoutListener (getKeyboardListener ());} else {mAttachScheduled = true;}} `

CreateReactContextInBackground is finally called to recreateReactContextInBackgroundFromBundleFile. Here you will create two Key Obj: JSCJavaScriptExecutor&JSBundleLoader.

JSCJavaScriptExecutor inherits from JavaScriptExecutor. The SO of ReactNative is loaded when JSCJavaScriptExecutor.class is loaded, and initialze is called during the initial JSCJavaScriptExecutor to initialize the communication framework between C++ layer ReactNative and JSC.

JSBundleLoader caches the information of JsBundle and encapsulates the upper layer loading JsBundle-related interfaces, through which CatalystInstance indirectly calls ReactBridge to load files.

/ * * ReactInstanceManagerImpl.java * / private void recreateReactContextInBackgroundFromBundleFile () {recreateReactContextInBackground (new JSCJavaScriptExecutor.Factory (), JSBundleLoader.createFileLoader (mApplicationContext, mJSBundleFile));}

After the JSCJavaScriptExecutor&JSBundleLoader is created, execute ReactContextInitAsyncTask continues to initialize the ReactContext.

/ * ReactInstanceManagerImpl.java * / private void recreateReactContextInBackground (JavaScriptExecutor.Factory jsExecutorFactory, JSBundleLoader jsBundleLoader) {ReactContextInitParams initParams = new ReactContextInitParams (jsExecutorFactory, jsBundleLoader); if (! mIsContextInitAsyncTaskRunning) {ReactContextInitAsyncTask initTask = new ReactContextInitAsyncTask (); initTask.execute (initParams); mIsContextInitAsyncTaskRunning = true;} else {mPendingReactContextInitParams = initParams;}}

ReactContextInitAsyncTask, the core class that creates ReactContext, destroys the previous context before initialization, ensuring that only one context exists. Then, call createReactContext to further create the ReactContext. After the ReactContext is created, setUpReactContext is called, and then the DevSupportManager is notified to update the context, update the life cycle, pass ReactRootView as RootView to UIManagerModule, call AppRegistry's runApplication to start Js Application, and so on.

/ * ReactInstanceManagerImpl$ReactContextInitAsynTask.java * / private final class ReactContextInitAsyncTask extends AsyncTask {@ Override protected void onPreExecute () {if (mCurrentReactContext! = null) {tearDownReactContext (mCurrentReactContext); mCurrentReactContext = null;}} @ Override protected Result doInBackground (ReactContextInitParams... Params) {Assertions.assertCondition (params! = null & & params.length > 0 & & params [0]! = null); try {JavaScriptExecutor jsExecutor = params [0] .getJsExecutorFactory () .create (); 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) } @ Override protected void onPostExecute (Result result) {try {setupReactContext (result.get ());} catch (Exception e) {mDevSupportManager.handleException (e);} finally {mIsContextInitAsyncTaskRunning = false;} / / Handle enqueued request to re-initialize react context. If (mPendingReactContextInitParams! = null) {recreateReactContextInBackground (mPendingReactContextInitParams.getJsExecutorFactory (), mPendingReactContextInitParams.getJsBundleLoader ()); mPendingReactContextInitParams = null;}}

In CreateReactContext, there are mainly five key path:

Build NativeModuleRegistry and JavaScriptModuleConfig mentioned in the above concepts through Builder

Create a ReactApplicationContext. ReactApplicationContext inherits from ContextWrapper and mainly caches ApplicationContext, the three thread of Activity Context,ReactNative processing messages (described in the second part), and the global control JS call causes the NativeModuleCallExceptionHandler of NativeModule Crash, which is passed in when initializing ReactInstanceManager, and can only be enabled after DeveloperSupport is disabled. If it is not passed, it will be handled by DevSupportManger by default.

Create a ReactPackage. ReactPackage mainly creates local modules, JS modules and view components through API such as createNativeModules, createJSModules and createViewManagers. ReactPackage is divided into framework's CoreModulesPackage and the business side's optional basic MainReactPackage,CoreModulesPackage encapsulates most of the communication and debugs core classes, such as UIManagerModule, which controls the Js layer from Dom to Native View.

Create a CatalystInstance. CatalystInstance is not intended for developers directly. Developers operate CatalystInstance indirectly through ReactInstanceManger. CatalystInstance holds a reference to ReactBridge, mainly through the JNI class ReactBridge to achieve the communication between the Java layer and the Js layer, and ReactBridge is created by the Constructor of CatalystInstance. At the same time, ReactQueueConfigurationSpec.createDefault is called during initialization to create two threads JsQueueThread&NativeModulesQueueThread for ReactNative communication.

Call reactContext.initializeWithInstance to further cache the created CatalystInstance and threads in ReactContext

Call catalystInstance.runJSBundle to load and parse Jsbundle

/ * ReactInstanceManagerImpl.java * @ 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 (); JavaScriptModulesConfig.Builder jsModulesBuilder = new JavaScriptModulesConfig.Builder (); ReactApplicationContext reactContext = new ReactApplicationContext (mApplicationContext); if (mUseDeveloperSupport) {reactContext.setNativeModuleCallExceptionHandler (mDevSupportManager);} CoreModulesPackage coreModulesPackage = new CoreModulesPackage (this, mBackBtnHandler, mUIImplementationProvider) ProcessPackage (coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder); for (ReactPackage reactPackage: mPackages) {processPackage (reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);} nativeModuleRegistry = nativeRegistryBuilder.build (); javaScriptModulesConfig = jsModulesBuilder.build (); NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler! = null? MNativeModuleCallExceptionHandler: mDevSupportManager; CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder () .setReactQueueConfigurationSpec (ReactQueueConfigurationSpec.createDefault ()) .setJSExecutor (jsExecutor) .set Registry (nativeModuleRegistry) .setJSModulesConfig (javaScriptModulesConfig) .setJSBundleLoader (jsBundleLoader) .setNativeModuleCallExceptionHandler (exceptionHandler); CatalystInstance catalystInstance= catalystInstanceBuilder.build (); if (mBridgeIdleDebugListener! = null) {catalystInstance.addBridgeIdleDebugListener (mBridgeIdleDebugListener);} reactContext.initializeWithInstance (catalystInstance); catalystInstance.runJSBundle (); return reactContext;}

ReactBridge is created by Constructor of CatalystInstance.

/ * CatalystInstanceImpl.java * / private CatalystInstanceImpl (final ReactQueueConfigurationSpec ReactQueueConfigurationSpec, final JavaScriptExecutor jsExecutor, final NativeModuleRegistry registry, final JavaScriptModulesConfig jsModulesConfig, final JSBundleLoader jsBundleLoader, NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {mReactQueueConfiguration = ReactQueueConfigurationImpl.create (ReactQueueConfigurationSpec, new NativeExceptionHandler ()); mBridgeIdleListeners = new CopyOnWriteArrayList (); mJavaRegistry = registry; mJSModuleRegistry = new JavaScriptModuleRegistry (CatalystInstanceImpl.this, jsModulesConfig); mJSBundleLoader = jsBundleLoader; mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler; mTraceListener = new JSProfilerTraceListener () Try {mBridge = mReactQueueConfiguration.getJSQueueThread () .callOnQueue (new Callable () {@ Override public ReactBridge call () throws Exception {Systrace.beginSection (Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "initializeBridge"); try {return initializeBridge (jsExecutor, jsModulesConfig);} finally {Systrace.endSection (Systrace.TRACE_TAG_REACT_JAVA_BRIDGE) }. Get ();} catch (Exception t) {throw new RuntimeException ("Failed to initialize bridge", t);}}

ReactBridge stores the registry information in the global variable _ _ fbBatchedBridgeConfig that is interconnected with the front end, so that the Js layer and the Java layer have the same module registry.

/ * CatalystInstanceImpl.java * / private ReactBridge initializeBridge (JavaScriptExecutor jsExecutor, JavaScriptModulesConfig jsModulesConfig) {ReactBridge bridge = new ReactBridge (jsExecutor, new NativeModulesReactCallback (), mReactQueueConfiguration.getNativeModulesQueueThread ()); Systrace.beginSection (Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "setBatchedBridgeConfig"); bridge.setGlobalVariable ("_ fbBatchedBridgeConfig", buildModulesConfigJSONProperty (mJavaRegistry, jsModulesConfig)); bridge.setGlobalVariable ("_ RCTProfileIsProfiling", return bridge;}

Call catalystInstance.runJSBundle to load and parse Jsbundle. If Exception appears in the parsing process, leave it to NativeModuleCallExceptionHandler for processing. It is recommended that developers set their own NativeModuleCallExceptionHandler and avoid some Crash (SyntaxError: Unexpected token').

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report