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

2,225 阅读4分钟
原文链接: blog.csdn.net
      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 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