React Native Application和Activity源码分析

基于V0.43.3版本 React Native Android端的ReactApplication和ReactActivity的实现原理.

ReactApplication

在Android端接入RN时, 需要ReactApplication作为接口被Application实现. 从源码中可以看出ReactApplication只是提供了一个获取实现ReactNativeHost的接口. 这个类既然叫Host那么肯定承载了RN的实例和一些配置, 可以通过下面的类图或源码看出它都有什么职责.

点击查看大图

从类图中可以看到RNHost中持有着Application的对象, 这个很好理解, 因为RN初始化一些部件或读取一些资源的时候肯定会需要用到Application. 而另外一个属性就是核心的ReactInstanceManager, RNHost类中的方法都是为InstanceManager服务的. R通过ReactInstanceManager管理配置负责Java和JS通讯的高层API类CatalystInstance, dev support的支持, 并且绑定并同步与ReactRootView所在容器的声明周期. 下面简单看一下Host类对ReactInstanceManager配置和初始化的10个方法.

  1. getReactInstanceManager方法

    public ReactInstanceManager getReactInstanceManager() {
    	if (mReactInstanceManager == null) {
    		mReactInstanceManager = createReactInstanceManager();
    	}
    	return mReactInstanceManager;
    }
    

    获取当前Host对象持有的ReactInstanceManager对象, 如果还没有创建的话就通过create方法初始化ReactInstanceManager对象. 因为都是在同一个线程中调用该方法, 所以这种懒加载的初始化方式不会有线程安全问题.

  2. hasInstance方法

    public boolean hasInstance() {
    	return mReactInstanceManager != null;
    }
    

    判断当前ReactInstanceManager对象有没有实例化.

  3. getRedBoxHandler方法

    protected @Nullable RedBoxHandler getRedBoxHandler() {
    	return null;
    }
    

    该方法用于初始化ReactInstanceManager对象实例, 根据实际情况选择复写该方法. RedBoxHandler提供运行时JS异常之后的交互, 默认情况下会有红色的页面出现并显示出JS异常的堆栈信息, 应该就是这个原因才叫RedBox吧. 复写该方法可以自定义一些JS异常之后的流程来丰富产品的兼容交互.

  4. getUIImplementationProvider方法

    protected UIImplementationProvider getUIImplementationProvider() {
    	return new UIImplementationProvider();
    }
    

    该方法用法同上. 如果想要定制RN UI方面的话可以选择复写该方法, 源码中说这个部件是一个非常高级的定制功能, 使用默认的初始化就可以满足百分之九十九的场景, 如无必要不建议复写该方法.

  5. getJSMainModuleName方法

    protected String getJSMainModuleName() {
    	return "index.android";
    }
    

    该方法用法同上. 设置js中的模块名称, 这样就可以在开启了DevSupport的情况下直接加载 packager server上的js bundle中的模块. 在DevSupport下加载的优先级是比较高的.

  6. getJSBundleFile方法

    protected @Nullable String getJSBundleFile() {
    	return null;
    }
    

    该方法用法同上. 如果有在自定义路径下(sd卡或者app沙盒控件)加载js bundle文件的需求的话就要重写该方法并制定要加载的文件路径. 如果使用默认配置的话RN优先根据下面的方法从assets路径下拿js bundle文件.

  7. getBundleAssetName方法

    protected @Nullable String getBundleAssetName() {
    	return "index.android.bundle";
    }
    

    该方法用法同上. 如果开启了DevSupport, RN会优先通过pack server远程加载js bundle文件. 然而在没有开启DevSupport并且没有复写6方法制定特定路径的情况下, RN会根据该方法配置的bundle文件名在assets下读取并加载该文件.

  8. getUseDeveloperSupport方法

    public abstract boolean getUseDeveloperSupport();

    该方法用法同上. getUseDeveloperSupport是一个虚方法. 通过它可以配置是否开启RN的DevSupport.

  9. getPackages方法

    protected abstract List<ReactPackage> getPackages();

    该方法用法同上. getPackages方法也是一个虚方法, 使用该方法来设置RN中JS和Native之间相互的通信模块, 自定义View的接口和Native本地资源的接口. 列表中最少要包含ReactPackage 的系统实现MainReactPackage对象, 如果项目使用到了自定的native module, js module和view managers 要将其添加到ReactPackage列表中.

  10. createReactInstanceManager方法

    protected ReactInstanceManager createReactInstanceManager() {
    	ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
    	  .setApplication(mApplication)
    	  .setJSMainModuleName(getJSMainModuleName())
    	  .setUseDeveloperSupport(getUseDeveloperSupport())
    	  .setRedBoxHandler(getRedBoxHandler())
    	  .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的形式在createReactInstanceManager方法中将初始化所需的配置信息汇总起来并对ReactInstanceManager进行初始化操作.

ReactActivity

RN源码中有提供两个可以直接使用的Activity, 分别是ReactActivity和ReactFragmentActivity. 他们两个的差别是分别继承自Activity和FragmentActivity. 对RN来说没有太大的区别, 这里就只分析ReactActivity了. 并且可以通过对ReactActivity的分析, 再结合RN的设计就可以自己封装出来类似ReactFragment之类的组件出来.

点击查看大图

从ReactActivity相关的类图可以看到它实现了两个接口, 分别是用来将Android回退键点击事件代理给JS的DefaultHardwareBackBtnHandler 和 处理运行时权限授权申请和回调的PermissionAwareActivity. 除了这两个接口之外, ReactActivity还有一个ReactActivityDelegate的属性. 通过源码可以看到这个对象把ReactActivity的声明周期, 事件和权限管理相关全部都同步并代理到自己内部.

通过getMainComponentName方法指定在JS中注册的主模块名称, 并且在构造方法里对delegate对象进行初始化. 如果有特殊需求的话例如要向JS的主模块中传递一些数据等, 可以选择复写createReactActivityDelegate方法, 使用定制的ReactActivityDelegate子类进行初始化.

protected ReactActivity() {
	mDelegate = createReactActivityDelegate();
}

protected @Nullable String getMainComponentName() {
	return null;
}

protected ReactActivityDelegate createReactActivityDelegate() {
	return new ReactActivityDelegate(this, getMainComponentName());
}

经过上面的分析, ReactActivity其实就是一个代理行为的空壳, 真正的实现都在ReactActivityDelegate中. 接下来就进入类中看这个代理类究竟做了什么.首先看一下构造方法.由于ReactActivity和ReactFragmentActivity都使用了相同的Delegate类, 所以提供了两个不同的构造接受Activity的实例和js端注册的模块名称.

public ReactActivityDelegate(Activity activity, @Nullable String mainComponentName) {
	mActivity = activity;
	mMainComponentName = mainComponentName;
	mFragmentActivity = null;
}

public ReactActivityDelegate(FragmentActivity fragmentActivity,@Nullable String mainComponentName) {
	mFragmentActivity = fragmentActivity;
	mMainComponentName = mainComponentName;
	mActivity = null;
}

接下来看到有两个提供被代理方Context和Activity的方法, 方便内部使用.

private Context getContext() {
	if (mActivity != null) {
	  return mActivity;
	}
	return Assertions.assertNotNull(mFragmentActivity);
}

private Activity getPlainActivity() {
	return ((Activity) getContext());
}

往下看就能发现在上面讲ReactApplication的时候主要涉及到的两个类. Application只是提供了一个getReactNativeHost的方法, 它本身并没有调用该方法对ReactInstanceManager进行初始化, 上面一节有说过ReactInstanceManager是懒加载初始化的, 从这看来ReactInstanceManager初始化的时机就是打开RN页面之后, 在RN页面装载的过程中对其初始化.

protected ReactNativeHost getReactNativeHost() {
	return ((ReactApplication) getPlainActivity().getApplication()).getReactNativeHost();
}

public ReactInstanceManager getReactInstanceManager() {
	return getReactNativeHost().getReactInstanceManager();
}

其他的方法例如 onResume, onPause, onDestroy, onActivityResult, onKeyUp, onBackPressed, onNewIntent, requestPermissions和onRequestPermissionsResult方法将声明周期同步到ReactInstanceManager中, 点击/回退与devSupport事件相关联, 动态权限申请和回调的管理起来, 这里就不一一细说了. 除此之外onDestroy方法触发的时候还会根据情况将rootView从ReactInstanceManager对象中卸载掉.

protected void onDestroy() {
	if (mReactRootView != null) {
		mReactRootView.unmountReactApplication();
		mReactRootView = null;
	}
	if (getReactNativeHost().hasInstance()) {
		getReactNativeHost().getReactInstanceManager().onHostDestroy(getPlainActivity());
	}
}

单独分析一下onCreate方法, 第一个大的if块是对DrawOverlays权限的判断. 因为从Android 6.0开始悬浮窗权限收紧, RN对该权限做了判断, 如果没有DrawOverlays权限的话就跳转到系统设置页面动态申请. 有权限了之后就会调用loadApp方法, 对RootView进行初始化. 最后初始化了一个双击R键的监听器, 服务于Dev Support.

protected void onCreate(Bundle savedInstanceState) {
	boolean needsOverlayPermission = false;
	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();
}

loadApp方法其实就是对RootView的初始化, 并通过rootView的startReactApplication方法将其装载进ReactInstanceManager进行管理, 与onDestroy方法中的unmountReactApplication相对应. 因为RootView就是一个view, 将其装载进acitivty中就可以绘制出来. 后面会单独分析ReactRootView.

protected ReactRootView createRootView() {
	return new ReactRootView(getContext());
}

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);
}

转载请注明出处:http://blog.csdn.net/l2show/article/details/72859653

### React NativeAndroid 平台上开发的相关信息 #### 一、React Native Android 的基本概念 React Native 是一种用于构建跨平台移动应用的技术框架,它允许开发者通过 JavaScript 原生组件来实现高性能的应用程序。对于 Android 平台而言,React Native 提供了一套完整的工具链 API 来支持其运行环境[^1]。 #### 二、React Native Android 开发的基础流程 创建一个不包含任何特定于 Android 或 iOS 模块的 React Native 项目是一个基础操作。这通常涉及初始化一个新的 React Native 应用并配置必要的依赖项。如果在此过程中遇到了问题,则可以参考更详细的文档或者视频教程来进行排查[^2]。 #### 三、集成到现有 Android 工程中的方法 当需要将 React Native 集成至现有的 Android 原生工程时,可以通过继承 `ReactActivity` 类的方式完成页面加载逻辑。例如,在自定义 Activity 中重写 `getMainComponentName()` 方法指定要渲染的主要 JS 组件名称: ```java public class ReactPageActivity extends ReactActivity { /** * 返回注册来自 JavaScript 的主要组件名。 * 这被用来安排该组件的呈现。 */ @Override protected String getMainComponentName() { return "App1"; } } ``` 上述代码片段展示了如何设置主入口点以便启动 React Native 页面[^4]。 另外还需要注意的是获取全局唯一的 `ReactNativeHost` 实例的方法可能如下所示: ```java protected ReactNativeHost getReactNativeHost() { return ((ReactApplication) getPlainActivity().getApplication()).getReactNativeHost(); } ``` 这段代码说明了在一个复杂场景下访问已有的 Host 对象实例的过程[^5]。 #### 四、学习资源推荐 为了更好地掌握 React NativeAndroid 上的实际运用技巧以及应对可能出现的各种挑战,建议利用官方指南以及其他社区贡献的学习资料作为补充材料。比如有这样一个公开仓库提供了详尽的教学案例:[https://gitcode.com/gh_mirrors/re/react-native-android-lession](https://gitcode.com/gh_mirrors/re/react-native-android-lession)[^3]。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值