后面会陆续写一些文章分析React Native源码,今天分析下模块加载的过程,包括系统模块和用户自定义模块的加载。源码是基于0.19.1,加载的大概流程和0.54差别不大,所以小伙伴们也不用特别纠结。
首先先看下怎么自定义Java模块给JS调用。直接用的官方的ToastAndroid的例子了。
1.自定义模块
首先创建一个原生模块,原生模块是继承了ReactContextBaseJavaModule的Java类,它可以实现一些JavaScript所需的功能。
public class ToastModule extends ReactContextBaseJavaModule {
private static final String DURATION_SHORT_KEY = "SHORT";
private static final String DURATION_LONG_KEY = "LONG";
public ToastModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "ToastExample";
}
@ReactMethod
public void show(String message, int duration) {
Toast.makeText(getReactApplicationContext(), message, duration).show();
}
}
ReactContextBaseJavaModule要求派生类实现getName方法。这个函数用于返回一个字符串名字,这个名字在JavaScript端标记这个模块。这里我们把这个模块叫做ToastExample,这样就可以在JavaScript中通过React.NativeModules.ToastExample访问到这个模块。要导出一个方法给JavaScript使用,Java方法需要使用注解@ReactMethod。方法的返回类型必须为void。
这样模块就定义好了,接下来就是注册这个模块了。在Java层需要注册这个模块,在应用的Package类的createNativeModules方法中添加这个模块。如果模块没有被注册,它也无法在JavaScript中被访问到。
public class AnExampleReactPackage implements ReactPackage {
@Override
public List createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List createNativeModules(
ReactApplicationContext reactContext) {
List modules = new ArrayList<>();
modules.add(new ToastModule(reactContext));
return modules;
}
这个package需要在MainApplication.java文件的getPackages方法中提供。这个文件位于你的react-native应用文件夹的android目录中。具体路径是: android/app/src/main/java/com/your-app-name/MainApplication.java.
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List getPackages() {
return Arrays.asList(
new MainReactPackage(),
new AnExampleReactPackage()
);
}
@Override
protected String getJSMainModuleName() {
return "index.android";
}
@Nullable
@Override
protected String getBundleAssetName() {
return "index.android.bundle";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
MultiDex.install(this);
...
}
}
这样就完成了模块的注册工作了,可以看出来框架的封装做的很好,可以很好的扩展组件的定义和注册。
不知道大家有没有疑问,反正我在刚做RN的时候做完上面的步骤后是有疑问的,模块怎么被加载的?RN的界面和Android原生的界面比如Activity有什么关系?
首先来看下,RN的界面和Android原生的界面比如Activity有什么关系?
2.ReactActivity
大家都知道,在原生Android中肯定是有一个MainActivity,然后再onCreate中setContentView塞入我们在xml中定义的布局文件,这样界面就能被系统给绘制出来。那么RN的所有界面其实也可以放到一个布局文件中,只不过现在布局文件不是xml了,而是js写的文件了,然后把这个布局文件给到一个Activity中,空口无凭,去扒一扒源码。
首先在开发RN程序的时候要新建一个Activity继承于ReactActivity,比如下面这样:
public class MainActivity extends ReactActivity implements DefaultHardwareBackBtnHandler {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "TEST";
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
}
首先继承ReactActivity,把生命周期交给系统进行管理,其他工作通过Delegate完成。
看下ReactActivity,其中getMainComponentName方法需要进行重写,为什么需要重写?后面在代码里面分析,这里先略过。
/**
* Base Activity for React Native applications.
*/
public abstract class ReactActivity extends Activity
implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {
private final ReactActivityDelegate mDelegate;
protected ReactActivity() {
mDelegate = createReactActivityDelegate();
}
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
* e.g. "MoviesApp"
*/
protected @Nullable String getMainComponentName() {
return null;
}
/**
* Called at construction time, override if you have a custom delegate implementation.
*/
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName());
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDelegate.onCreate(savedInstanceState);
}
...
}
getMainComponentName返回的名称需要和Android入口文件index.android.js里面注册的组件名称一致:
class TEST extends Component {
render() {
return ;
}
}
AppRegistry.registerComponent('TEST', () => TEST);
其实到这里可以知道要渲染的组件就是TEST这个Component(RN中的组件,可以简单对等于View)。
接着返回去看ReactActivity,生命周期里面都调用ReactActivityDelegate:
protected void onCreate(Bundle savedInstanceState) {
boolean needsOverlayPermission = false;
//如果Android版本是M(23)也就是Android 6.0系统,在调式模式下需要弹窗获取权限
if (getReactNativeHost().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Get permission to show redbox in dev builds.
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);
}
1.mMainComponentName就是前面重写方法需要返回的,如果复写了就会进入loadApp,这就是前面需要复写该方法的原因喽
3.loadApp进行三个很重要的工作,
- 创建ReactRootView,这个是RN 的自定义根View,可以监听屏幕尺寸的变化,拦截Touch事件然后进行发送给JS层进行处理。最终会把这个View塞给activity的setContentView。看下官方的定义:
Default root view for catalyst apps. Provides the ability to listen for
size changes so that a UI manager can re-layout its elements. It delegates
handling touch events for itself and child views and sending those events to
JS by using JSTouchDispatcher. This view is overriding {@link
ViewGroup#onInterceptTouchEvent} method in order to be notified about the events
for all of its children and it's also overriding
{@link ViewGroup#requestDisallowInterceptTouchEvent} to make sure that
{@link ViewGroup#onInterceptTouchEvent} will get events even when some child view
start intercepting it. In case when no child view is interested in handling
some particular touch event this view's {@link View#onTouchEvent} will still
return true in order to be notified about all subsequent touch events related to
that gesture (in case when JS code want to handle that gesture
- 创建ReactContext
Schedule rendering of the react component rendered by the JS application from the
given JS* module (@{param moduleName}) using provided
{@param reactInstanceManager} to attach to the* JS context of that manager.
- setContentView(ReactRootView)
到这里就明白主要工作在ReactActivityDelegate中的loadApp,首先构造根View-createRootView,然后构造ReactContext上下文,渲染appKey-- mMainComponentName这里就是在index.android.js中注册的入口模块,之后将该View塞给Activity,最终Android系统将其显示到屏幕上。
3.RN框架加载Package
接下来看看Package怎么加载到RN框架中。
首先回到前面ReactActivityDelegate中的loadApp会拿到MainApplication中的ReactNativeHost:
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);
}
protected ReactNativeHost getReactNativeHost() {
return ((ReactApplication) getPlainActivity().getApplication()).getReactNativeHost();
}
loadApp会调用getReactNativeHost().getReactInstanceManager(),接着看看ReactInstanceManager是什么,跟到ReactNativeHost中,第一次会构建ReactInstanceManager,用来管理CatalystInstance,CatalystInstance是Java/C++/JS三方通信的总管:
/**
* Get the current {@link ReactInstanceManager} instance, or create one.
*/
public ReactInstanceManager getReactInstanceManager() {
if (mReactInstanceManager == null) {
mReactInstanceManager = createReactInstanceManager();
}
return mReactInstanceManager;
}
protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
.setApplication(mApplication)
.setJSMainModulePath(getJSMainModuleName())
.setUseDeveloperSupport(getUseDeveloperSupport())
.setRedBoxHandler(getRedBoxHandler())
.setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
.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()));
}
return builder.build();
}
从上面可以看到用到了建造者模式构造 ReactInstanceManager:
/**
* Builder class for {@link ReactInstanceManager}
*/
public class ReactInstanceManagerBuilder {
private final List mPackages = new ArrayList<>();
public ReactInstanceManagerBuilder addPackage(ReactPackage reactPackage) {
mPackages.add(reactPackage);
return this;
}
public ReactInstanceManagerBuilder addPackages(List reactPackages) {
mPackages.addAll(reactPackages);
return this;
}
/**
* Instantiates a new {@link ReactInstanceManagerImpl}.
* Before calling {@code build}, the following must be called:
*
* - {@link #setApplication}
*
- {@link #setJSBundleFile} or {@link #setJSMainModuleName}
*
*/
public ReactInstanceManager build() {
return new ReactInstanceManagerImpl(
Assertions.assertNotNull(
mApplication,
"Application property has not been set with this builder"),
mJSBundleFile,
mJSMainModuleName,
mPackages,
mUseDeveloperSupport,
mBridgeIdleDebugListener,
Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"),
mUIImplementationProvider,
mNativeModuleCallExceptionHandler);
}
}
到这里系统定义的和我们自定义的ReactPackage都加载到ReactInstanceManageImpl中的mPackages中。
再返回前面ReactRootView startReactApplication,会调用mReactInstanceManager.createReactContextInBackground
//com/facebook/react/ReactRootView
public void startReactApplication(
ReactInstanceManager reactInstanceManager,
String moduleName,
@Nullable Bundle initialProperties) {
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "startReactApplication");
try {
UiThreadUtil.assertOnUiThread();
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
mReactInstanceManager.createReactContextInBackground();
}
attachToReactInstanceManager();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
接着到ReactInstanceManagerImpl中:
//com/facebool/react/ReactInstanceManagerImpl
@ThreadConfined(UI)
public void createReactContextInBackground() {
......
mHasStartedCreatingInitialContext = true;
recreateReactContextInBackgroundInner();
}
@ThreadConfined(UI)
private void recreateReactContextInBackgroundInner() {
......
UiThreadUtil.assertOnUiThread();
if (mUseDeveloperSupport && mJSMainModulePath != null &&
!Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JSC_CALLS)) {
final DeveloperSettings devSettings = mDevSupportManager.getDevSettings();
// If remote JS debugging is enabled, load from dev server.
if (mDevSupportManager.hasUpToDateJSBundleInCache() &&
!devSettings.isRemoteJSDebugEnabled()) {
// If there is a up-to-date bundle downloaded from server,
// with remote JS debugging disabled, always use that.
onJSBundleLoadedFromServer();
} else if (mBundleLoader == null) {
mDevSupportManager.handleReloadJS();
} else {
mDevSupportManager.isPackagerRunning(
new PackagerStatusCallback() {
@Override
public void onPackagerStatusFetched(final boolean packagerIsRunning) {
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
if (packagerIsRunning) {
mDevSupportManager.handleReloadJS();
} else {
// If dev server is down, disable the remote JS debugging.
devSettings.setRemoteJSDebugEnabled(false);
recreateReactContextInBackgroundFromBundleLoader();
}
}
});
}
});
}
return;
}
recreateReactContextInBackgroundFromBundleLoader();
}
最后都会回调:
//com/facebool/react/ReactInstanceManagerImpl
private void recreateReactContextInBackground(
JavaScriptExecutor.Factory jsExecutorFactory,
JSBundleLoader jsBundleLoader) {
UiThreadUtil.assertOnUiThread();
ReactContextInitParams initParams =
new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
if (!mIsContextInitAsyncTaskRunning) {
// No background task to create react context is currently running, create and execute one.
ReactContextInitAsyncTask initTask = new ReactContextInitAsyncTask();
initTask.execute(initParams);
mIsContextInitAsyncTaskRunning = true;
} else {
// Background task is currently running, queue up most recent init params to recreate context
// once task completes.
mPendingReactContextInitParams = initParams;
}
}
会启动一个AsyncTask来构造ReactContext:
//com/facebool/react/ReactInstanceManagerImpl
/*
* Task class responsible for (re)creating react context in the background. These tasks can only
* be executing one at time, see {@link #recreateReactContextInBackground()}.
*/
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;
}
......
}
}
在doInBackground中构造ReactContext:
- 构造ReactContext
- 加载CoreModulesPackage,系统的核心模块
- 加载用户自定义Packages
- 构造NativeModuleRegistry,管理暴露给JS层的Java本地模块
- 构造JavaScriptModulesConfig,存储JS层暴露给Java调用的JS模块
- 构造CatalystInstanceImpl,三方总管
- runJSBundle
/**
* @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();
//构造ReactContext
ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
if (mUseDeveloperSupport) {
reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
}
//加载CoreModulesPackage
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);
}
//加载用户自定义Packages
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);
}
}
//构造NativeModuleRegistry
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildNativeModuleRegistry");
NativeModuleRegistry nativeModuleRegistry;
try {
nativeModuleRegistry = nativeRegistryBuilder.build();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
//构造JavaScriptModulesConfig
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildJSModuleConfig");
JavaScriptModulesConfig javaScriptModulesConfig;
try {
javaScriptModulesConfig = jsModulesBuilder.build();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
...
//构造CatalystInstanceImpl
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setCatalystQueueConfigurationSpec(CatalystQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSModulesConfig(javaScriptModulesConfig)
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
CatalystInstance catalystInstance;
try {
catalystInstance = catalystInstanceBuilder.build();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
......
reactContext.initializeWithInstance(catalystInstance);
//runJSBundle
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
try {
catalystInstance.runJSBundle();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
ReactMarker.logMarker("CREATE_REACT_CONTEXT_END");
return reactContext;
}
//加载模块
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);
}
}
CoreModulesPackage
封装了一些系统的核心模块,有NativeModules和JSModules
class CoreModulesPackage implements ReactPackage{
@Override
public List createNativeModules(
ReactApplicationContext catalystApplicationContext) {
......
return Arrays.asList(
new AnimationsDebugModule(
catalystApplicationContext,
mReactInstanceManager.getDevSupportManager().getDevSettings()),
new AndroidInfoModule(),
new DeviceEventManagerModule(catalystApplicationContext, mHardwareBackBtnHandler),
new ExceptionsManagerModule(mReactInstanceManager.getDevSupportManager()),
new Timing(catalystApplicationContext),
new SourceCodeModule(
mReactInstanceManager.getSourceUrl(),
mReactInstanceManager.getDevSupportManager().getSourceMapUrl()),
uiManagerModule,
new DebugComponentOwnershipModule(catalystApplicationContext));
}
@Override
public List> createJSModules() {
return Arrays.asList(
DeviceEventManagerModule.RCTDeviceEventEmitter.class,
JSTimersExecution.class,
RCTEventEmitter.class,
RCTNativeAppEventEmitter.class,
AppRegistry.class,
com.facebook.react.bridge.Systrace.class,
DebugComponentOwnershipModule.RCTDebugComponentOwnership.class);
}
}
public interface ReactPackage {
/**
* @param reactContext react application context that can be used to create modules
* @return list of native modules to register with the newly created catalyst instance
*/
List createNativeModules(ReactApplicationContext reactContext);
/**
* @return list of JS modules to register with the newly created catalyst instance.
*
* IMPORTANT: Note that only modules that needs to be accessible from the native code should be
* listed here. Also listing a native module here doesn't imply that the JS implementation of it
* will be automatically included in the JS bundle.
*/
List> createJSModules();
/**
* @return a list of view managers that should be registered with {@link UIManagerModule}
*/
List createViewManagers(ReactApplicationContext reactContext);
}
到这里就把所有的Java模块和JS模块传递给了CatalystInstanceImpl,这个是三方的中转模块,调用逻辑在这里中转。再之后就是Java<-->JS双方通信的原理了,后面专门的文章进行分析。
4.总结
整个流程其实是:
ReactActivity--->
ReactActivityDelegate--->
createRootView->
getReactNativeHost->
createReactInstanceManager->ReactInstanceManagerImpl
recreateReactContextInBackground->
1.构造ReactContext->
2.加载CoreModulesPackage->
3.加载用户自定义Packages->
4.构造CatalystInstanceImpl->
5.runJSBundle
setContentView(mReactRootView)
React Native框架就是套一个自定义的根View-ReactRootView,在这个自定义View中创建ReactInstanceManager,ReactInstanceManager会构造ReactContext(包括JS运行环境和JSBundleLoader),同时加载系统和用户定义的Package,构造三方通信的中转站CatalystInstanceImpl,然后解释执行JSBundle,最后getPlainActivity().setContentView(mReactRootView)塞给Activity这样RN界面就显示到屏幕上了,而这个Activity的生命周期其实还是交给Android进行管理的。
后面会接着分析RN中Java与JS的通信原理,感兴趣的小伙伴们欢迎关注。
如果文章内容能够帮到你,帮忙点赞哈。
欢迎关注公众号:JueCode