React native热更新思路(一)之ReactNativeHost类解析【适用于Android开发者】

时间:2022-07-31 00:22:16

        React Native发展到今天,之所以受到大家的青睐一个最重要的原因就是它支持动态更新,并且这种动态更新的方式较原生的来看要方便的很多。所以今天我将带领大家来看看React native是如何实现动态更新的。

      首先我们一起来看看ReactNativeHost类到底写了些什么?

/**
* Simple class that holds an instance of {@link ReactInstanceManager}. This can be used in your
* {@link Application class} (see {@link ReactApplication}), or as a static field.
*/
public abstract class ReactNativeHost {

private final Application mApplication;
private @Nullable ReactInstanceManager mReactInstanceManager;

protected ReactNativeHost(Application application) {
mApplication = application;
}

/**
* 获取一个react native的核心管理类的的对象
*/
public ReactInstanceManager getReactInstanceManager() {
if (mReactInstanceManager == null) {
mReactInstanceManager = createReactInstanceManager();
}
return mReactInstanceManager;
}

/**
* Get whether this holder contains a {@link ReactInstanceManager} instance, or not. I.e. if
* {@link #getReactInstanceManager()} has been called at least once since this object was created
* or {@link #clear()} was called.
*/
public boolean hasInstance() {
return mReactInstanceManager != null;
}

/**
* Destroy the current instance and release the internal reference to it, allowing it to be GCed.
*/
public void clear() {
if (mReactInstanceManager != null) {
mReactInstanceManager.destroy();
mReactInstanceManager = null;
}
}
 /**
* 构建 ReactInstanceManager 对象,ReactInstanceManager会在下次详细介绍
*/
protected ReactInstanceManager createReactInstanceManager() { ReactInstanceManager.Builder builder = ReactInstanceManager.builder() .setApplication(mApplication) .setJSMainModuleName(getJSMainModuleName()) .setUseDeveloperSupport(getUseDeveloperSupport()) .setRedBoxHandler(getRedBoxHandler()) .setUIImplementationProvider(getUIImplementationProvider()) .setInitialLifecycleState(LifecycleState.BEFORE_CREATE); for (ReactPackage reactPackage : getPackages()) { builder.addPackage(reactPackage); }
 /** * 今天的重点,判断是否存在jsbundle,如果存在则加载,反之则加载assets中的jsbundle
 * 这里是热更新的开始 具体如何判断请看getJSBundleFile方法 */
String jsBundleFile = getJSBundleFile(); if (jsBundleFile != null) { builder.setJSBundleFile(jsBundleFile); } else { builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName())); } return builder.build(); } /** * Get the {@link RedBoxHandler} to send RedBox-related callbacks to. */ protected @Nullable RedBoxHandler getRedBoxHandler() { return null; } protected final Application getApplication() { return mApplication; } /** * Get the {@link UIImplementationProvider} to use. Override this method if you want to use a * custom UI implementation. * * Note: this is very advanced functionality, in 99% of cases you don't need to override this. */ protected UIImplementationProvider getUIImplementationProvider() { return new UIImplementationProvider(); } /** * Returns the name of the main module. Determines the URL used to fetch the JS bundle * from the packager server. It is only used when dev support is enabled. * This is the first file to be executed once the {@link ReactInstanceManager} is created. * e.g. "index.android" */ protected String getJSMainModuleName() { return "index.android"; } /** * 这里可以设置你的最新jsbundle进来 */ protected @Nullable String getJSBundleFile() { return null; } /** * Returns the name of the bundle in assets. If this is null, and no file path is specified for * the bundle, the app will only work with {@code getUseDeveloperSupport} enabled and will * always try to load the JS bundle from the packager server. * e.g. "index.android.bundle" */ protected @Nullable String getBundleAssetName() { return "index.android.bundle"; } /** * Returns whether dev mode should be enabled. This enables e.g. the dev menu. */ protected abstract boolean getUseDeveloperSupport(); /** * Returns a list of {@link ReactPackage} used by the app. * You'll most likely want to return at least the {@code MainReactPackage}. * If your app uses additional views or modules besides the default ones, * you'll want to include more packages here. */ protected abstract List<ReactPackage> getPackages();}

代码中有关热更新的地方我都用红色字体做了中文注释

看完ReactNativeHost类的代码,我们大致可以了解到React native的确为我们提供了热更新的功能,并且我们只需要实现ReactNativeHost类的同时去重写两个方法即可,如下

protected @Nullable String getJSBundleFile() {
return null;
}
protected String getJSMainModuleName() {
return "index.android";
}

最新版的React native为我们提供了一个ReactApplication,我们只需要继承它去实现getReactNativeHost方法就可以将我们的重写好的ReactNativeHost配置进去,这样就初步搭建好了React native的热更新


下面我们一起来找找这里面的坑

 1.这里没有具体介绍jsbundle的校验问题,每个人的校验方式都不同,也有很多方式,不懂得可以百度

 2.有人可能会疑惑,ReactNativeHost是在Application中配置好的,如果说程序运行过程中有新的jsbundle下载到本地,那我应该如何去加载它呢?

       作者一开始也想着手动去调用一下加载的方法,可是怎么都没找到,后来看了codepush源码之后,发现他们利用类反射去调用了一个叫recreateReactContextInBackground的方法,后来又去查看这个方法的源码才明白这样是完全可行的【切记在UI thread 中去调用它】

/**
* Recreate the react application and context. This should be called if configuration has
* changed or the developer has requested the app to be reloaded. It should only be called after
* an initial call to createReactContextInBackground.
*
* Called from UI thread.
*/
public void recreateReactContextInBackground() {
Assertions.assertCondition(
mHasStartedCreatingInitialContext,
"recreateReactContextInBackground should only be called after the initial " +
"createReactContextInBackground call.");
recreateReactContextInBackgroundInner();
}
 3.资源文件该怎么办呢?

       其实React native已经为我们提供好了资源文件的加载,我们只需要将最新的资源文件和你的jsbundle保存在相同的路径下即可,具体的实现大家可以看React native image加载本地图片的实现源码。

 4.是否有源码可以分享出来?

       前段时间写了一个,只是将热更新的流程走通了,还有一些功能要逐步完善,有兴趣的可以一起GitHub

   

   今天大致带大家了解了下ReactNativeHost到底做了哪些事,对于初学RN的伙伴来说,利用今天分享完全可以体验到React native的热更新功能。

   希望对大家有帮助     

   QQ群581621024