使用React-Native实现app热更新的一次实践
Java Objective-C JavaScript
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
android add readme Nov 24, 2015
image add readme Nov 24, 2015
ios first commit Nov 23, 2015
node_modules/react-native first commit Nov 23, 2015
remote add two test file Feb 28, 2018
.gitignore Initial commit Nov 23, 2015
README.md init Nov 24, 2015
index.android.js first commit Nov 23, 2015
index.ios.js first commit Nov 23, 2015
package.json first commit Nov 23, 2015

README.md

使用React-Native实现app热部署的一次实践

效果

原理

demo中执行热部署的实现流程如下:

最新版本的React Native版本中,加入了如下支持:

	/**
     * Path to the JS bundle file to be loaded from the file system.
     *
     * Example: {@code "assets://index.android.js" or "/sdcard/main.jsbundle}
     */
    public Builder setJSBundleFile(String jsBundleFile) {
      mJSBundleFile = jsBundleFile;
      return this;
    }

这是热部署的突破口,由于React Native加载的js文件都打包在bundle中,通过这个方法,可以设置app加载的bundle来源。若检测到远端存在更新的bundle文件,下载好后重新加载即可。

为了在运行中重新加载bundle文件,查看ReactInstanceManager的源码,找到如下方法:

  private void recreateReactContextInBackground(JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader) {
    UiThreadUtil.assertOnUiThread();

    ReactContextInitParams initParams = new ReactContextInitParams(jsExecutor, 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;
    }
  }

虽然这个方法是private的,不过不要紧,使用反射调用就行啦:

private void onJSBundleLoadedFromServer() {

    try {
            Class<?> RIManagerClazz = mReactInstanceManager.getClass();
            Method method = RIManagerClazz.getDeclaredMethod("recreateReactContextInBackground",
                    JavaScriptExecutor.class, JSBundleLoader.class);
            method.setAccessible(true);
            method.invoke(mReactInstanceManager,
                    new JSCJavaScriptExecutor(),
                    JSBundleLoader.createFileLoader(getApplicationContext(), JS_BUNDLE_LOCAL_PATH));
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
    }

总结

使用React Native进行热部署是可行的,只要远程提供打包好的bundle,app下载后重新加载即可;

但是,这样的方案也具有一定的局限性:

React Native在使用View的时候,这些View是要经过本地定制的,并且将相关方法通过RCT_EXPORT_METHOD暴露给js,js端才能正常使用。

比如,我们在新版本中使用了一个PullToRefreshListView,这个View必须在Native中被定制过,此时,单纯的更新bundle就会报错了,需要重新发包才能解决。