JS程序的入口,将当前APP对象注册到AppRegistry组件中,AppRegistry组件是js module
import { AppRegistry } from 'react-native' ...省略代码 AppRegistry .registerComponent ('demo' , () => Index )
启动流程
我们新建一个RN的项目,在原生代码中会生成MainActivity和MainApplication两个Java类。顾名思义,MainAcitivity就是我们的Native的入口了,
我们先来看下MainApplication都做了哪些操作
public class MainApplication extends Application implements ReactApplication { 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 .init (this , false ); } } }
我们再来看下MainActivity的代码
public class MainActivity extends ReactActivity { @Override protected String getMainComponentName ( ) { return "demo" ; } }
可以看到其实是继承了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 ) { 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 ); } } 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." ); } mReactRootView = createRootView (); mReactRootView.startReactApplication ( getReactNativeHost ().getReactInstanceManager (), appKey, getLaunchOptions ()); 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 { UiThreadUtil .assertOnUiThread (); Assertions .assertCondition ( mReactInstanceManager == null , "This root view has already been attached to a catalyst instance manager" ); mReactInstanceManager = reactInstanceManager; mJSModuleName = moduleName; mAppProperties = initialProperties; 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 ( ) { UiThreadUtil .assertOnUiThread (); if (mUseDeveloperSupport && mJSMainModuleName != null && !Systrace .isTracing (TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JSC_CALLS )) { return ; } 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 (); final ReactContextInitParams initParams = new ReactContextInitParams ( jsExecutorFactory, jsBundleLoader); if (mCreateReactContextThread == null ) { runCreateReactContextOnNewThread (initParams); } else { mPendingReactContextInitParams = initParams; } }
runCreateReactContextOnNewThread中有一个核心方法createReactContext来创建ReactContext
private ReactApplicationContext createReactContext ( JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader ) { final ReactApplicationContext reactContext = new ReactApplicationContext (mApplicationContext); NativeModuleRegistryBuilder nativeModuleRegistryBuilder = new NativeModuleRegistryBuilder ( reactContext, this , mLazyNativeModulesEnabled); JavaScriptModuleRegistry .Builder jsModulesBuilder = new JavaScriptModuleRegistry .Builder (); if (mUseDeveloperSupport) { reactContext.setNativeModuleCallExceptionHandler (mDevSupportManager); } ...省略代码 try { CoreModulesPackage coreModulesPackage = new CoreModulesPackage ( this , mBackBtnHandler, mUIImplementationProvider, mLazyViewManagersEnabled); processPackage (coreModulesPackage, nativeModuleRegistryBuilder, jsModulesBuilder); } finally { Systrace .endSection (TRACE_TAG_REACT_JAVA_BRIDGE ); } for (ReactPackage reactPackage : mPackages) { ...省略代码 try { processPackage (reactPackage, nativeModuleRegistryBuilder, jsModulesBuilder); } finally { Systrace .endSection (TRACE_TAG_REACT_JAVA_BRIDGE ); } } ...省略代码 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 .Builder catalystInstanceBuilder = new CatalystInstanceImpl .Builder () .setReactQueueConfigurationSpec (mUseSeparateUIBackgroundThread ? ReactQueueConfigurationSpec .createWithSeparateUIBackgroundThread () : ReactQueueConfigurationSpec .createDefault ()) .setJSExecutor (jsExecutor) .setRegistry (nativeModuleRegistry) .setJSModuleRegistry (jsModulesBuilder.build ()) .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 )) { } reactContext.initializeWithInstance (catalystInstance); 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) { mHybridData = initHybrid (); mReactQueueConfiguration = ReactQueueConfigurationImpl .create ( reactQueueConfigurationSpec, new NativeExceptionHandler ()); 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 { 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 { 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 ; auto sourceURL = assetURL.substr (kAssetsLength); auto manager = extractAssetManager (assetManager); auto script = loadScriptFromAssets (manager, sourceURL); if (JniJSModulesUnbundle ::isUnbundle (manager, sourceURL)) { instance_->loadUnbundle ( folly ::make_unique<JniJSModulesUnbundle >(manager, sourceURL), std ::move (script), sourceURL, loadSynchronously); return ; } else { 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_->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 ) { 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->loadApplicationScript (std ::move (*startupScript), std ::move (startupScriptSourceURL)); }); }
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 ) { ... evaluateSourceCode (m_context, bcSourceCode, jsSourceURL); flush (); }
void JSCExecutor ::flush ( ) { ... bindBridge (); callNativeModules (m_flushedQueueJS->callAsFunction ({})); ... }
void JSCExecutor ::callNativeModules (Value&& value ) { ... auto calls = value.toJSONString (); 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 ) { ... catalystInstance.initialize (); mDevSupportManager.onNewReactContextCreated (reactContext); moveReactContextToCurrentLifecycleState (); ReactMarker .logMarker (ATTACH_MEASURED_ROOT_VIEWS_START ); synchronized (mAttachedRootViews) { for (ReactRootView rootView : mAttachedRootViews) { attachRootViewToInstance (rootView, catalystInstance); } } ... } } private void attachMeasuredRootViewToInstance ( final ReactRootView rootView, CatalystInstance catalystInstance) { ... UIManagerModule uiManagerModule = catalystInstance.getNativeModule (UIManagerModule .class ); int rootTag = uiManagerModule.addMeasuredRootView (rootView); rootView.setRootViewTag (rootTag); rootView.runApplication (); ... }
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 (); 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上,最终呈现在用户面前。
系统框架图
启动流程图