react native 实战系列教程之热更新原理分析与实现

前端之家收集整理的这篇文章主要介绍了react native 实战系列教程之热更新原理分析与实现前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

很多人在技术选型的时候,会选择RN是因为它具有热更新,而且这是它的一个特性,所以实现起来会相对比较简单,不像原生那样,原生的热更新是一个大工程。那就目前来看,RN的热更新方案已有的,有微软的CodePush和reactnative中文网的pushy。实话说,这两个我还没有体验过。一来是当初选择RN是因为它不但拥有接近原生的体验感还具有热更新特性,那么就想自己来实现一下热更新,研究一下它的原理;二来,把自己的东西放在别人的服务器上总是觉得不是最好的办法,为什么不自己实现呢?因此,这篇文章便是记录自己的一些研究。

react native加载bundle过程

这篇文章是基于RNAndroid0.38.1

当我们创建完RN的基础项目后,打开android项目,项目只有MainActivity和MainApplication。

打开MainActivity,只有一个重写方法getMainComponentName,返回主组件名称,它继承于ReactActivity。

我们打开ReactActivity,它使用了代理模式,通过ReactActivityDelegate mDelegate对象将Activity需要处理的逻辑放在了代理对象内部,并通过getMainComponentName方法来设置(匹配)JS端AppRegistry.registerComponent端启动的入口组件。

Activity渲染出界面前,先是调用onCreate,所以我们进入代理对象的onCreate方法

      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        //ReactActivityDelegate.java
      •  
      •  
      • protected void onCreate(Bundle savedInstanceState) {
      • //判断是否支持dev模式,也就是RN常见的那个红色弹窗
      • if (getReactNativeHost().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) {
      • // Get permission to show redBox in dev builds.
      • if (!Settings.canDrawOverlays(getContext())) {
      • Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
      • getContext().startActivity(serviceIntent);
      • FLog.w(ReactConstants.TAG,REDBox_PERMISSION_MESSAGE);
      • Toast.makeText(getContext(),REDBox_PERMISSION_MESSAGE,Toast.LENGTH_LONG).show();
      • }
      • }
      •  
      • if (mMainComponentName != null) {
      • //加载app
      • loadApp(mMainComponentName);
      • }
      • //android模拟器dev 模式下,双击R重新加载
      • mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
      • }

      上面的代码并没什么实质的东西,主要是调用了loadApp,我们跟进看下

      1. 1
      2. 2
      3. 3
      4. 4
      5. 5
      6. 6
      7. 7
      8. 8
      9. 9
      10. 10
      11. 11
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          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);
        • }

        生成了一个ReactRootView对象,然后调用它的startReactApplication方法,最后setContentView将它设置为内容视图。再跟进startReactApplication里

        1. 1
        2. 2
        3. 3
        4. 4
        5. 5
        6. 6
        7. 7
        8. 8
        9. 9
        10. 10
        11. 11
        12. 12
        13. 13
        14. 14
        15. 15
        16. 16
        17. 17
        18. 18
        19. 19
        20. 20
        21. 21
        22. 22
        23. 23
        24. 24
        25. 25
        26. 26
        27. 27
        28. 28
        29. 29
        30. 30
        31. 31
        32. 32
        33. 33
            • 1
            • 2
            • 3
            • 4
            • 5
            • 6
            • 7
            • 8
            • 9
            • 10
            • 11
            • 12
            • 13
            • 14
            • 15
            • 16
            • 17
            • 18
            • 19
            • 20
            • 21
            • 22
            • 23
            • 24
            • 25
            • 26
            • 27
            • 28
            • 29
            • 30
            • 31
            • 32
            • 33
            //ReactRootView.java
          •  
          • public void startReactApplication(
          • ReactInstanceManager reactInstanceManager,String moduleName,@Nullable Bundle launchOptions) {
          • UiThreadUtil.assertOnUiThread();
          •  
          • // TODO(6788889): Use POJO instead of bundle here,apparently we can't just use WritableMap
          • // here as it may be deallocated in native after passing via JNI bridge,but we want to reuse
          • // it in the case of re-creating the catalyst instance
          • Assertions.assertCondition(
          • mReactInstanceManager == null,"This root view has already been attached to a catalyst instance manager");
          • //配置项管理
          • mReactInstanceManager = reactInstanceManager;
          • //入口组件名称
          • mJSModuleName = moduleName;
          • //用于传递给JS端初始组件props参数
          • mLaunchOptions = launchOptions;
          • //判断是否已经加载过
          • if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
          • //去加载bundle文件
          • mReactInstanceManager.createReactContextInBackground();
          • }
          •  
          • // We need to wait for the initial onMeasure,if this view has not yet been measured,we set which
          • // will make this view startReactApplication itself to instance manager once onMeasure is called.
          • if (mWasMeasured) {
          • //去渲染ReactRootView
          • attachToReactInstanceManager();
          • }
          • }

          startReactApplication传入三个参数,第一个ReactInstanceManager配置项管理类(非常重要);第二个是MainComponentName入口组件名称;第三个是Android Bundle类型,用于传递给JS端初始组件的props参数。首先,会根据ReactInstanceManager的配置去加载bundle过程,然后去渲染ReactRootView,将UI展示出来。现在我们不用去管attachToReactInstanceManager是如何去渲染ReactRootView,我们主要是研究如何加载bundle的,所以,我们跟进createReactContextInBackground,发现它是抽象类ReactInstanceManager的一个抽象方法。那它具体实现逻辑是什么呢?那我们就需要知道ReactInstanceManager的具体类的实例对象是谁了【1】。

          好了,现在我们回到ReacActivityDelegate.Java的loadApp,在ReactRootView的startReactApplication传入的ReactInstanceManager对象是getReactNativeHost().getReactInstanceManager()

          1. 1
          2. 2
          3. 3
          4. 4
          5. 5
          6. 6
              • 1
              • 2
              • 3
              • 4
              • 5
              • 6
              //ReacActivityDelegate.java
            •  
            • mReactRootView.startReactApplication(
            • getReactNativeHost().getReactInstanceManager(),getLaunchOptions());

            getReactNativeHost(),又是什么呢?

            1. 1
            2. 2
            3. 3
            4. 4
            5. 5
                • 1
                • 2
                • 3
                • 4
                • 5
                //从Application获取ReactNativeHost
              • protected ReactNativeHost getReactNativeHost() {
              • return ((ReactApplication) getPlainActivity().getApplication()).getReactNativeHost();
              • }

              所以我们在打开MainApplication类

              1. 1
              2. 2
              3. 3
              4. 4
              5. 5
              6. 6
              7. 7
              8. 8
              9. 9
              10. 10
              11. 11
              12. 12
              13. 13
              14. 14
              15. 15
              16. 16
              17. 17
              18. 18
              19. 19
              20. 20
              21. 21
                  • 1
                  • 2
                  • 3
                  • 4
                  • 5
                  • 6
                  • 7
                  • 8
                  • 9
                  • 10
                  • 11
                  • 12
                  • 13
                  • 14
                  • 15
                  • 16
                  • 17
                  • 18
                  • 19
                  • 20
                  • 21
                  public class MainApplication extends Application implements ReactApplication {
                •  
                • private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
                • @Override
                • boolean getUseDeveloperSupport() {
                • return BuildConfig.DEBUG;
                • }
                •  
                • protected List<ReactPackage> getPackages() {
                • return Arrays.<ReactPackage>asList(
                • new MainReactPackage()
                • );
                • }
                • };
                •  
                • @Override
                • public ReactNativeHost getReactNativeHost() {
                • return mReactNativeHost;
                • }
                • }

                MainApplication实现了ReactApplication接口,在getReactNativeHost()方法返回配置好的ReactNativeHost对象。由于我们把项目的Application配置成了MainApplication,所以ReacActivityDelegate的getReactNativeHost方法,返回的就是MainApplication mReactNativeHost对象。接着我们看下ReactNativeHost的getReactInstanceManager()方法,里面直接调用了createReactInstanceManager()方法,所以我们直接看createReactInstanceManager()

                1. 1
                2. 2
                3. 3
                4. 4
                5. 5
                6. 6
                7. 7
                8. 8
                9. 9
                10. 10
                11. 11
                12. 12
                13. 13
                14. 14
                15. 15
                16. 16
                17. 17
                18. 18
                19. 19
                20. 20
                21. 21
                22. 22
                23. 23
                    • 1
                    • 2
                    • 3
                    • 4
                    • 5
                    • 6
                    • 7
                    • 8
                    • 9
                    • 10
                    • 11
                    • 12
                    • 13
                    • 14
                    • 15
                    • 16
                    • 17
                    • 18
                    • 19
                    • 20
                    • 21
                    • 22
                    • 23
                    //ReactNativeHost.java
                  •  
                  • 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);
                  • }
                  •  
                  • String jsBundleFile = getJSBundleFile();
                  • if (jsBundleFile != null) {
                  • builder.setJSBundleFile(jsBundleFile);
                  • } else {
                  • builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
                  • }
                  • return builder.build();
                  • }

                  createReactInstanceManager()通过使用ReactInstanceManager.Builder构造器来设置一些配置并生成对象。从这里看,我们可以从MainApplication的mReactNativeHost对象来配置ReactInstanceManager,比如JSMainModuleName、UseDeveloperSupport、Packages、JSBundleFile、BundleAssetName等,也可以重写createReactInstanceManager方法,自己手动生成ReactInstanceManager对象。

                  这里看下jsBundleFile的设置,先判断了getJSBundleFile()是否为null,项目默认是没有重写的,所以默认就是null,那么走builder.setBundleAssetName分支,看下getBundleAssetName(),默认是返回”index.android.bundle”

                  1. 1
                  2. 2
                  3. 3
                  4. 4
                  5. 5
                  6. 6
                  7. 7
                      • 1
                      • 2
                      • 3
                      • 4
                      • 5
                      • 6
                      • 7
                      //builder.setBundleAssetName
                    •  
                    • public Builder setBundleAssetName(String bundleAssetName) {
                    • mJSBundleAssetUrl = (bundleAssetName == null ? null : "assets://" + bundleAssetName);
                    • mJSBundleLoader = null;
                    • return this;
                    • }

                    所以,默认情况下,mJSBundleAssetUrl=”assets://index.android.bundle”,mJSBundleLoader = null。

                    接着往下看,builder最后调用build()来生成ReactInstanceManager实例对象。我们进去build()方法看下。

                    1. 1
                    2. 2
                    3. 3
                    4. 4
                    5. 5
                    6. 6
                    7. 7
                    8. 8
                    9. 9
                    10. 10
                    11. 11
                    12. 12
                    13. 13
                    14. 14
                    15. 15
                    16. 16
                    17. 17
                    18. 18
                    19. 19
                    20. 20
                    21. 21
                    22. 22
                    23. 23
                    24. 24
                    25. 25
                    26. 26
                    27. 27
                    28. 28
                    29. 29
                    30. 30
                    31. 31
                    32. 32
                    33. 33
                    34. 34
                    35. 35
                    36. 36
                    37. 37
                    38. 38
                        • 1
                        • 2
                        • 3
                        • 4
                        • 5
                        • 6
                        • 7
                        • 8
                        • 9
                        • 10
                        • 11
                        • 12
                        • 13
                        • 14
                        • 15
                        • 16
                        • 17
                        • 18
                        • 19
                        • 20
                        • 21
                        • 22
                        • 23
                        • 24
                        • 25
                        • 26
                        • 27
                        • 28
                        • 29
                        • 30
                        • 31
                        • 32
                        • 33
                        • 34
                        • 35
                        • 36
                        • 37
                        • 38
                        //ReactInstanceManager.Builder
                      •  
                      • public ReactInstanceManager build() {
                      • Assertions.assertNotNull(
                      • mApplication,0); Box-sizing: border-Box;">"Application property has not been set with this builder");
                      •  
                      • Assertions.assertCondition(
                      • mUseDeveloperSupport || mJSBundleAssetUrl != null || mJSBundleLoader != "JS Bundle File or Asset URL has to be provided when dev support is disabled");
                      •  
                      • Assertions.assertCondition(
                      • mJSMainModuleName != null || mJSBundleAssetUrl != "Either MainModuleName or JS Bundle File needs to be provided");
                      •  
                      • if (mUIImplementationProvider == null) {
                      • // create default UIImplementationProvider if the provided one is null.
                      • mUIImplementationProvider = new UIImplementationProvider();
                      • }
                      •  
                      • new XReactInstanceManagerImpl(
                      • mApplication,mCurrentActivity,mDefaultHardwareBackBtnHandler,(mJSBundleLoader == null && mJSBundleAssetUrl != null) ?
                      • JSBundleLoader.createAssetLoader(mApplication,mJSBundleAssetUrl) : mJSBundleLoader,mJSMainModuleName,mPackages,mUseDeveloperSupport,mBridgeIdleDebugListener,Assertions.assertNotNull(mInitialLifecycleState,0); Box-sizing: border-Box;">"Initial lifecycle state was not set"),mUIImplementationProvider,mNativeModuleCallExceptionHandler,mJSCConfig,mRedBoxHandler,mLazyNativeModulesEnabled,mLazyViewManagersEnabled);
                      • }

                      从上面看来,XReactInstanceManagerImpl的第四个参数,传入的是一个JSBundleLoader,并且默认是JSBundleLoader.createAssetLoader。

                      new的是XReactInstanceManagerImpl对象,也就是说,XReactInstanceManagerImpl是抽象类ReactInstanceManager的具体实现类。

                      好了,在【1】处留下的疑问,我们现在就解决了。也就是,说调用ReactInstanceManager的createReactContextInBackground方法,是去执行XReactInstanceManagerImpl的reateReactContextInBackground方法

                      进去reateReactContextInBackground方法后,它调用了recreateReactContextInBackgroundInner()一个内部方法,直接看下recreateReactContextInBackgroundInner的实现代码

                      1. 1
                      2. 2
                      3. 3
                      4. 4
                      5. 5
                      6. 6
                      7. 7
                      8. 8
                      9. 9
                      10. 10
                      11. 11
                      12. 12
                      13. 13
                      14. 14
                      15. 15
                      16. 16
                      17. 17
                      18. 18
                      19. 19
                      20. 20
                      21. 21
                      22. 22
                      23. 23
                      24. 24
                      25. 25
                      26. 26
                      27. 27
                      28. 28
                      29. 29
                      30. 30
                      31. 31
                      32. 32
                      33. 33
                      34. 34
                      35. 35
                      36. 36
                      37. 37
                      38. 38
                      39. 39
                      40. 40
                      41. 41
                          • 1
                          • 2
                          • 3
                          • 4
                          • 5
                          • 6
                          • 7
                          • 8
                          • 9
                          • 10
                          • 11
                          • 12
                          • 13
                          • 14
                          • 15
                          • 16
                          • 17
                          • 18
                          • 19
                          • 20
                          • 21
                          • 22
                          • 23
                          • 24
                          • 25
                          • 26
                          • 27
                          • 28
                          • 29
                          • 30
                          • 31
                          • 32
                          • 33
                          • 34
                          • 35
                          • 36
                          • 37
                          • 38
                          • 39
                          • 40
                          • 41
                          //XReactInstanceManagerImpl.java
                        •  
                        • void recreateReactContextInBackgroundInner() {
                        • UiThreadUtil.assertOnUiThread();
                        • //判断是否是dev模式
                        • if (mUseDeveloperSupport && mJSMainModuleName != 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 DevServerHelper.PackagerStatusCallback() {
                        • @Override
                        • void onPackagerStatusFetched(final boolean packagerIsRunning) {
                        • UiThreadUtil.runOnUiThread(
                        • new Runnable() {
                        • @Override
                        • void run() {
                        • if (packagerIsRunning) {
                        • mDevSupportManager.handleReloadJS();
                        • } else {
                        • // If dev server is down,disable the remote JS debugging.
                        • devSettings.setRemoteJSDebugEnabled(false);
                        • recreateReactContextInBackgroundFromBundleLoader();
                        • }
                        • }
                        • });
                        • }
                        • });
                        • }
                        • return;
                        • }
                        •  
                        • recreateReactContextInBackgroundFromBundleLoader();
                        • }

                        由于我们发布出去的apk包,最后都是关闭了dev模式的,所以dev模式下的bundle加载流程我们先不需要太多的关注,那么mUseDeveloperSupport就是false,它就不会走进if里面,而是调用了recreateReactContextInBackgroundFromBundleLoader()方法。其实,你简单看下if里面的判断和方法调用也能知道,其实它就是去拉取通过React-native start启动起来的packages服务器窗口,再者如果打开了远程调试,那么它就走浏览器代理去拉取bundle。

                        recreateReactContextInBackgroundFromBundleLoader又调用了recreateReactContextInBackground

                        1. 1
                        2. 2
                        3. 3
                        4. 4
                        5. 5
                        6. 6
                        7. 7
                        8. 8
                        9. 9
                        10. 10
                        11. 11
                        12. 12
                        13. 13
                        14. 14
                        15. 15
                        16. 16
                        17. 17
                            • 1
                            • 2
                            • 3
                            • 4
                            • 5
                            • 6
                            • 7
                            • 8
                            • 9
                            • 10
                            • 11
                            • 12
                            • 13
                            • 14
                            • 15
                            • 16
                            • 17
                            void recreateReactContextInBackground(
                          • JavaScriptExecutor.Factory jsExecutorFactory,JSBundleLoader jsBundleLoader) {
                          • UiThreadUtil.assertOnUiThread();
                          •  
                          • ReactContextInitParams initParams =
                          • new ReactContextInitParams(jsExecutorFactory,jsBundleLoader);
                          • if (mReactContextInitAsyncTask == // No background task to create react context is currently running,create and execute one.
                          • mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
                          • mReactContextInitAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,initParams);
                          • } else {
                          • // Background task is currently running,queue up most recent init params to recreate context
                          • // once task completes.
                          • mPendingReactContextInitParams = initParams;
                          • }
                          • }

                          到这里,recreateReactContextInBackground使用了ReactContextInitAsyncTask(继承AsyncTask)开启线程去执行,并且将ReactContextInitParams当作参数,传递到了AsyncTask的doInBackground。ReactContextInitParams只是将jsExecutorFactory、jsBundleLoader两个参数封装成一个内部类,方便传递参数。

                          那么ReactContextInitAsyncTask开启线程去执行了什么?该类也是个内部类,我们直接看它的doInBackground方法

                          1. 1
                          2. 2
                          3. 3
                          4. 4
                          5. 5
                          6. 6
                          7. 7
                          8. 8
                          9. 9
                          10. 10
                          11. 11
                          12. 12
                          13. 13
                          14. 14
                              • 1
                              • 2
                              • 3
                              • 4
                              • 5
                              • 6
                              • 7
                              • 8
                              • 9
                              • 10
                              • 11
                              • 12
                              • 13
                              • 14
                              protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {
                            •  
                            • Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
                            •  
                            • 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);
                            • }
                            • }

                            好像也没处理什么,就是使用ReactContextInitParams传递进来的两个参数,去调用了createReactContext

                            1. 1
                            2. 2
                            3. 3
                            4. 4
                            5. 5
                            6. 6
                            7. 7
                            8. 8
                            9. 9
                            10. 10
                            11. 11
                            12. 12
                            13. 13
                            14. 14
                            15. 15
                            16. 16
                            17. 17
                            18. 18
                            19. 19
                            20. 20
                            21. 21
                            22. 22
                            23. 23
                            24. 24
                            25. 25
                            26. 26
                            27. 27
                            28. 28
                            29. 29
                            30. 30
                            31. 31
                            32. 32
                            33. 33
                            34. 34
                            35. 35
                            36. 36
                            37. 37
                            38. 38
                            39. 39
                            40. 40
                            41. 41
                            42. 42
                            43. 43
                            44. 44
                            45. 45
                            46. 46
                            47. 47
                            48. 48
                            49. 49
                            50. 50
                            51. 51
                            52. 52
                            53. 53
                            54. 54
                            55. 55
                            56. 56
                            57. 57
                            58. 58
                            59. 59
                            60. 60
                            61. 61
                            62. 62
                            63. 63
                            64. 64
                            65. 65
                            66. 66
                            67. 67
                            68. 68
                            69. 69
                            70. 70
                            71. 71
                            72. 72
                            73. 73
                            74. 74
                            75. 75
                            76. 76
                            77. 77
                            78. 78
                            79. 79
                            80. 80
                            81. 81
                            82. 82
                            83. 83
                            84. 84
                            85. 85
                            86. 86
                            87. 87
                            88. 88
                            89. 89
                            90. 90
                            91. 91
                                • 1
                                • 2
                                • 3
                                • 4
                                • 5
                                • 6
                                • 7
                                • 8
                                • 9
                                • 10
                                • 11
                                • 12
                                • 13
                                • 14
                                • 15
                                • 16
                                • 17
                                • 18
                                • 19
                                • 20
                                • 21
                                • 22
                                • 23
                                • 24
                                • 25
                                • 26
                                • 27
                                • 28
                                • 29
                                • 30
                                • 31
                                • 32
                                • 33
                                • 34
                                • 35
                                • 36
                                • 37
                                • 38
                                • 39
                                • 40
                                • 41
                                • 42
                                • 43
                                • 44
                                • 45
                                • 46
                                • 47
                                • 48
                                • 49
                                • 50
                                • 51
                                • 52
                                • 53
                                • 54
                                • 55
                                • 56
                                • 57
                                • 58
                                • 59
                                • 60
                                • 61
                                • 62
                                • 63
                                • 64
                                • 65
                                • 66
                                • 67
                                • 68
                                • 69
                                • 70
                                • 71
                                • 72
                                • 73
                                • 74
                                • 75
                                • 76
                                • 77
                                • 78
                                • 79
                                • 80
                                • 81
                                • 82
                                • 83
                                • 84
                                • 85
                                • 86
                                • 87
                                • 88
                                • 89
                                • 90
                                • 91
                                private ReactApplicationContext createReactContext(
                              • JavaScriptExecutor jsExecutor,JSBundleLoader jsBundleLoader) {
                              • FLog.i(ReactConstants.TAG,0); Box-sizing: border-Box;">"Creating react context.");
                              • ReactMarker.logMarker(CREATE_REACT_CONTEXT_START);
                              • mSourceUrl = jsBundleLoader.getSourceUrl();
                              • List<ModuleSpec> moduleSpecs = new ArrayList<>();
                              • Map<Class,ReactModuleInfo> reactModuleInfoMap = new HashMap<>();
                              • JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();
                              •  
                              • final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
                              • if (mUseDeveloperSupport) {
                              • reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
                              • }
                              •  
                              • ReactMarker.logMarker(PROCESS_PACKAGES_START);
                              • Systrace.beginSection(
                              • TRACE_TAG_REACT_JAVA_BRIDGE,0); Box-sizing: border-Box;">"createAndProcesscoreModulesPackage");
                              • try {
                              • CoreModulesPackage coreModulesPackage =
                              • new CoreModulesPackage(this,mBackBtnHandler,mUIImplementationProvider);
                              • processPackage(
                              • coreModulesPackage,reactContext,moduleSpecs,reactModuleInfoMap,jsModulesBuilder);
                              • } finally {
                              • Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
                              • }
                              •  
                              • // TODO(6818138): Solve use-case of native/js modules overriding
                              • for (ReactPackage reactPackage : mPackages) {
                              • Systrace.beginSection(
                              • TRACE_TAG_REACT_JAVA_BRIDGE,0); Box-sizing: border-Box;">"createAndProcessCustomReactPackage");
                              • try {
                              • processPackage(
                              • reactPackage,jsModulesBuilder);
                              • } finally {
                              • Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
                              • }
                              • }
                              • ReactMarker.logMarker(PROCESS_PACKAGES_END);
                              •  
                              • ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_START);
                              • Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE,0); Box-sizing: border-Box;">"buildNativeModuleRegistry");
                              • NativeModuleRegistry nativeModuleRegistry;
                              • try {
                              • nativeModuleRegistry = new NativeModuleRegistry(moduleSpecs,reactModuleInfoMap);
                              • } finally {
                              • Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
                              • ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
                              • }
                              •  
                              • NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
                              • ? mNativeModuleCallExceptionHandler
                              • : mDevSupportManager;
                              • CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
                              • .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
                              • .setJSExecutor(jsExecutor)
                              • .setRegistry(nativeModuleRegistry)
                              • .setJSModuleRegistry(jsModulesBuilder.build())
                              • .setJSBundleLoader(jsBundleLoader)
                              • .setNativeModuleCallExceptionHandler(exceptionHandler);
                              •  
                              • ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START);
                              • // CREATE_CATALYST_INSTANCE_END is in JSCExecutor.cpp
                              • Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE,0); Box-sizing: border-Box;">"createCatalystInstance");
                              • final CatalystInstance catalystInstance;
                              • try {
                              • catalystInstance = catalystInstanceBuilder.build();
                              • } finally {
                              • Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
                              • ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);
                              • }
                              •  
                              • if (mBridgeIdleDebugListener != null) {
                              • catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
                              • }
                              •  
                              • reactContext.initializeWithInstance(catalystInstance);
                              • catalystInstance.runJSBundle();
                              •  
                              • return reactContext;
                              • }

                              这个方法代码有点多,首先它执行设置了RN自带的和开发者自定义的模块组件(Package\Module),然后同样使用了构造器CatalystInstanceImpl.Builder生成了catalystInstance对象,最后调用了catalystInstance.runJSBundle()。跟进去是一个接口类CatalystInstance,那么我们又要去看它的实现类CatalystInstanceImpl

                              1. 1
                              2. 2
                              3. 3
                              4. 4
                              5. 5
                              6. 6
                              7. 7
                              8. 8
                              9. 9
                              10. 10
                              11. 11
                              12. 12
                              13. 13
                              14. 14
                              15. 15
                              16. 16
                              17. 17
                              18. 18
                              19. 19
                              20. 20
                              21. 21
                              22. 22
                              23. 23
                              24. 24
                              25. 25
                                  • 1
                                  • 2
                                  • 3
                                  • 4
                                  • 5
                                  • 6
                                  • 7
                                  • 8
                                  • 9
                                  • 10
                                  • 11
                                  • 12
                                  • 13
                                  • 14
                                  • 15
                                  • 16
                                  • 17
                                  • 18
                                  • 19
                                  • 20
                                  • 21
                                  • 22
                                  • 23
                                  • 24
                                  • 25
                                  //CatalystInstanceImpl.java
                                •  
                                • void runJSBundle() {
                                • Assertions.assertCondition(!mJSBundleHasLoaded,0); Box-sizing: border-Box;">"JS bundle was already loaded!");
                                • mJSBundleHasLoaded = true;
                                • // incrementPendingJSCalls();
                                • mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
                                •  
                                • synchronized (mJSCallsPendingInitLock) {
                                • // Loading the bundle is queued on the JS thread,but may not have
                                • // run yet. It's save to set this here,though,since any work it
                                • // gates will be queued on the JS thread behind the load.
                                • mAcceptCalls = true;
                                •  
                                • for (PendingJSCall call : mJSCallsPendingInit) {
                                • callJSFunction(call.mExecutorToken,call.mModule,call.mMethod,call.mArguments);
                                • }
                                • mJSCallsPendingInit.clear();
                                • }
                                •  
                                •  
                                • // This is registered after JS starts since it makes a JS call
                                • Systrace.registerListener(mTraceListener);
                                • }

                                到这里,可以看到mJSBundleLoader调用了loadScript去加载bundle。进去方法看下,发现它又是个抽象类,有两个抽象方法,一个是loadScript加载bundle,一个是getSourceUrl返回bundle的地址,并且提供了4个静态工厂方法

                                由之前分析知道,JSBundleLoader默认是使用了JSBundleLoader.createAssetLoader来创建的实例

                                1. 1
                                2. 2
                                3. 3
                                4. 4
                                5. 5
                                6. 6
                                7. 7
                                8. 8
                                9. 9
                                10. 10
                                11. 11
                                12. 12
                                13. 13
                                14. 14
                                15. 15
                                16. 16
                                17. 17
                                18. //JSBundleLoader.java
                                19.  
                                20. static JSBundleLoader createAssetLoader(
                                21. final Context context,final String assetUrl) {
                                22. new JSBundleLoader() {
                                23. @Override
                                24. void loadScript(CatalystInstanceImpl instance) {
                                25. instance.loadScriptFromAssets(context.getAssets(),assetUrl);
                                26. }
                                27.  
                                28. public String getSourceUrl() {
                                29. return assetUrl;
                                30. }
                                31. };
                                32. }

                                我们看到loadScript最后是调用了CatalystInstanceImpl的loadScriptFromAssets。跟进去之后发现,它是一个native方法,也就是最后的实现RN把它放在了jni层来完成最后加载bundle的过程。

                                并且CatalystInstanceImpl不止loadScriptFromAssets一个native方法,它还提供了loadScriptFromFile和loadScriptFromOptimizedBundle。其中前面两个,分别是从android assets目录下加载bundle,另一个是从android SD卡文件夹目录下加载bundle。而loadScriptFromOptimizedBundle是在UnpackingJSBundleLoader类里调用,但是UnpackingJSBundleLoader目前好像是没有用到,有知道它的作用的朋友们可以告知一下。

                                至此,bundle的加载流程我们已经走一遍了,下面用一张流程图来总结下

                                加载bundle文件的几个途径

                                从上面的分析过程,我们可以得出,bundle的加载路径来源取决于JSBundleLoader的loadScript,而loadScript又调用了CatalystInstanceImpl的loadScriptFromAssets或者loadScriptFromFile,所以,加载bundle文件的途径本质上有两种方式

                                • loadScriptFromAssets

                                从android项目下的assets文件夹下去加载,这也是RN发布版的默认加载方式,也就是在cmd命令行下使用gradlew assembleRelease 命令打包签名后的apk里面的assets就包含有bundle文件

                                如果你打包后发现里面没有bundle文件,那么你将它安装到系统里,运行也是会报错的

                                react native gradle assembleRelease打包运行失败,没有生成bundle文件

                                • loadScriptFromFile

                                第二种方式是从android文件系统也就是sd卡下去加载bundle。

                                我们只要事先在sd卡下存放bundle文件,然后在ReactNativeHost的getJSBundleFile返回文件路径即可。

                                1. 1
                                2. 2
                                3. 3
                                4. 4
                                5. 5
                                6. 6
                                7. 7
                                8. 8
                                9. 9
                                10. 10
                                11. 11
                                12. 12
                                13. 13
                                14. 14
                                15. 15
                                16. 16
                                17. 17
                                18. 18
                                19. 19
                                20. 20
                                21. 21
                                22. 22
                                23. 23
                                24. 24
                                25. 25
                                26. 26
                                27. 27
                                28. 28
                                29. 29
                                30. 30
                                31. 31
                                32. 32
                                    • 1
                                    • 2
                                    • 3
                                    • 4
                                    • 5
                                    • 6
                                    • 7
                                    • 8
                                    • 9
                                    • 10
                                    • 11
                                    • 12
                                    • 13
                                    • 14
                                    • 15
                                    • 16
                                    • 17
                                    • 18
                                    • 19
                                    • 20
                                    • 21
                                    • 22
                                    • 23
                                    • 24
                                    • 25
                                    • 26
                                    • 27
                                    • 28
                                    • 29
                                    • 30
                                    • 31
                                    • 32
                                    //MainApplication.java
                                  •  
                                  • return Arrays.<ReactPackage>asList(
                                  • new MainReactPackage()
                                  • );
                                  • }
                                  •  
                                  • @Nullable
                                  • protected String getJSBundleFile() {
                                  • File bundleFile = new File(getCacheDir()+"/react_native",0); Box-sizing: border-Box;">"index.android.bundle");
                                  • if(bundleFile.exists()){
                                  • return bundleFile.getAbsolutePath();
                                  • }
                                  • super.getJSBundleFile();
                                  • }
                                  •  
                                  • protected String getBundleAssetName() {
                                  • super.getBundleAssetName();
                                  • }
                                  • };

                                  getJSBundleFile首先会尝试在sd卡目录下

                                  1. 1
                                      • 1
                                      data/data/<package-name>/cache/react_native/

                                    看是否存在index.android.bundle文件,如果有,那么就会使用该bundle,如果没有,那么就会返回null,这时候就是去加载assets下的bundle了。

                                    热更新的实现

                                    如果你了解React Nativebundle命令,那么就会知道,其实该命令分两部分,一部分是生成bundle文件,一部分是生成图片资源。对android的react.gardle来说,也就是app/build.gradle中下面这句

                                    1. 1
                                    2. apply from: "../../node_modules/react-native/react.gradle"

                                    该脚本就是去执行react native bundle命令,它将生成的bundle文件放在assets下,且将生成图片资源放在drawable下。

                                    但是当我们自定义getJSBundleFile路径之后,bundle的所有加载过程都是在该目录下,包括图片资源,所以我们服务器上存放的应该是个bundle patch,包括bundle文件图片资源。关于RN的图片热更新问题,可以看这个React-Native 图片热更新初探

                                    有了前面的分析和了解后,那么就可以自己动手来实现bundle的热更新了。

                                    那么热更新主要包括
                                    - bundle patch从服务器下载到sd卡
                                    - 程序中加载bundle

                                    接下来,进行模拟版本更新:将旧版本中‘我的’tab的列表中‘观看历史’item去掉,也就是新版本中不再有‘观看历史’功能效果如下

                                    更新之前如下:

                                    更新并加载bundle之后如下:

                                    bundle patch的下载

                                    我这里服务器使用的bmob后台,将要更新的bundle文件存放在服务器上。

                                    先将去掉‘观看历史’后的新版本bundle patchs打包出来,上传到服务器上(bmob)。

                                    通过react-native bundle命令手动将patchs包打包出来

                                    1. 1
                                    2. 2
                                    3. 3
                                        • 1
                                        • 2
                                        • 3
                                        react-native bundle --platform android --dev false --r
                                      • eset-cache --entry-file index.android.js --bundle-output F:\Gray\ReactNative\XiF
                                      • an\bundle\index.android.bundle --assets-dest F:\XiFan\bundle

                                      上传到服务器

                                      然后,在客户端定义一个实体类来存放更新对象

                                      1. 1
                                      2. 2
                                      3. 3
                                      4. 4
                                      5. 5
                                      6. AppInfo BmobObject{
                                      7. private String version;//bundle版本
                                      8. private String updateContent;//更新内容
                                      9. private BmobFile bundle;//要下载的bundle patch文件
                                      10. }

                                      然后,程序启动的时候去检测更新

                                      1. 1
                                      2. 2
                                      3. 3
                                      4. 4
                                      5. 5
                                      6. 6
                                      7. 7
                                      8. 8
                                      9. 9
                                      10. 10
                                      11. 11
                                      12. 12
                                      13. 13
                                      14. 14
                                      15. 15
                                      16. 16
                                      17. 17
                                      18. 18
                                      19. 19
                                      20. 20
                                      21. 21
                                      22. 22
                                      23. 23
                                      24. 24
                                      25. 25
                                      26. 26
                                      27. 27
                                      28. 28
                                      29. 29
                                      30. 30
                                      31. 31
                                      32. 32
                                      33. 33
                                      34. 34
                                      35. 35
                                      36. 36
                                      37. 37
                                      38. 38
                                      39. 39
                                      40. 40
                                      41. 41
                                      42. 42
                                      43. 43
                                      44. 44
                                      45. 45
                                      46. 46
                                      47. 47
                                      48. 48
                                      49. 49
                                      50. 50
                                          • 1
                                          • 2
                                          • 3
                                          • 4
                                          • 5
                                          • 6
                                          • 7
                                          • 8
                                          • 9
                                          • 10
                                          • 11
                                          • 12
                                          • 13
                                          • 14
                                          • 15
                                          • 16
                                          • 17
                                          • 18
                                          • 19
                                          • 20
                                          • 21
                                          • 22
                                          • 23
                                          • 24
                                          • 25
                                          • 26
                                          • 27
                                          • 28
                                          • 29
                                          • 30
                                          • 31
                                          • 32
                                          • 33
                                          • 34
                                          • 35
                                          • 36
                                          • 37
                                          • 38
                                          • 39
                                          • 40
                                          • 41
                                          • 42
                                          • 43
                                          • 44
                                          • 45
                                          • 46
                                          • 47
                                          • 48
                                          • 49
                                          • 50
                                          //MainActivity.java
                                        •  
                                        • @Override
                                        • void onCreate(Bundle savedInstanceState) {
                                        • super.onCreate(savedInstanceState);
                                        • BmobQuery<AppInfo> query = new BmobQuery<>();
                                        • query.setLimit(1);
                                        • query.addWhereGreaterThan("version",0); Box-sizing: border-Box;">"1.0.0");
                                        • query.findObjects(new FindListener<AppInfo>() {
                                        • @Override
                                        • void done(List<AppInfo> list,BmobException e) {
                                        • if(e == null){
                                        • if(list!=null && !list.isEmpty()){
                                        • AppInfo info = list.get(0);
                                        • File reactDir = new File(getCacheDir(),0); Box-sizing: border-Box;">"react_native");
                                        • if(!reactDir.exists()){
                                        • reactDir.mkdirs();
                                        • }
                                        •  
                                        • BmobFile patchFile = info.getBundle();
                                        • final File saveFile = new File(reactDir,0); Box-sizing: border-Box;">"bundle-patch.zip");
                                        • if(saveFile.exists()){
                                        • return;
                                        • }
                                        • //下载bundle-patch.zip文件
                                        • patchFile.download(saveFile,136); Box-sizing: border-Box;">new DownloadFileListener() {
                                        • @Override
                                        • void done(String s,BmobException e) {
                                        • if (e == null) {
                                        • System.out.println("下载完成");
                                        • //解压patch文件到react_native文件夹下
                                        • unzip(saveFile);
                                        • } else {
                                        • Log.e("bmob",e.toString());
                                        • }
                                        •  
                                        • }
                                        •  
                                        • void onProgress(Integer integer,136); Box-sizing: border-Box;">long l) {
                                        • System.out.println("下载中...." + integer);
                                        • }
                                        • });
                                        • }
                                        • }else{
                                        • Log.e( 在MainActivity的onCreate,将当前版本当作是1.0.0,发起检测更新。
                                          当进入应用后,就会从服务端获取到更新对象

                                        • 然后将bundle-patch文件保存到data/data/com.xifan/cache/react_native sd卡路径下

                                        • 当将bundle-patch保存完并解压之后,接下去就是加载bundle了。

                                        • 加载bundle

                                        • 根据bug的紧急/重要程度,可以把加载bundle的时机分为:立马加载和下次启动加载,我这里将它们分别称为热加载和冷加载。

                                        • 冷加载

                                        • 冷加载方式比较简单,不用做任何特殊处理,下载并解压完patch.zip包之后,当应用完全退出之后(应用在后台不算完全退出,应用被杀死才算),用户再次启动应用,就会去加载新的bundle了。

                                        • 热加载

                                        • 热加载需要特殊处理一下,处理也很简单,只要在解压unzip之后,调用以下代码即可

                                        • //MainActivity.java
                                        • //清空ReactInstanceManager配置
                                        • getReactNativeHost().clear();
                                        • //重启activity
                                        • recreate();
                                        • 结合JS端,实现完整热更新流程

                                        • 热更新的总体思路是,JS端通过Module发起版本检测请求,如果检测到有新版本bundle,就去下载bundle,下载完成后根据更新的紧急程度来决定是冷加载还是热加载。

                                        • 那么首先我们需要定义一个UpdateCheckModule来建立起JS端和android端之间的检测更新通信。

                                        • UpdateCheckModule.java

                                        •   
                                        •   
                                        • 1
                                        •   
                                        •   
                                        • 2
                                        •   
                                        •   
                                        • 3
                                        •   
                                        •   
                                        • 4
                                        •   
                                        •   
                                        • 5
                                        •   
                                        •   
                                        • 6
                                        •   
                                        •   
                                        • 7
                                        •   
                                        •   
                                        • 8
                                        •   
                                        •   
                                        • 9
                                        •   
                                        •   
                                        • 10
                                        •   
                                        •   
                                        • 11
                                        •   
                                        •   
                                        • 12
                                        •   
                                        •   
                                        • 13
                                        •   
                                        •   
                                        • 14
                                        •   
                                        •   
                                        • 15
                                        •   
                                        •   
                                        • 16
                                        •   
                                        •   
                                        • 17
                                        •   
                                        •   
                                        • 18
                                        •   
                                        •   
                                        • 19
                                        •   
                                        •   
                                        • 20
                                        •   
                                        •   
                                        • 21
                                        •   
                                        •   
                                        • 22
                                        •   
                                        •   
                                        • 23
                                        •   
                                        •   
                                        • 24
                                        •   
                                        •   
                                        • 25
                                        •   
                                        •   
                                        • 26
                                        •   
                                        •   
                                        • 27
                                        •   
                                        •   
                                        • 28
                                        •   
                                        •   
                                        • 29
                                        •   
                                        •   
                                        • 30
                                        •   
                                        •   
                                        • 31
                                        •   
                                        •   
                                        • 32
                                        •   
                                        •   
                                        • 33
                                        •   
                                        •   
                                        • 34
                                        •   
                                        •   
                                        • 35
                                        •   
                                        •   
                                        • 36
                                        •   
                                        •   
                                        • 37
                                        •   
                                        •   
                                        • 38
                                        •   
                                        •   
                                        • 39
                                        •   
                                        •   
                                        • 40
                                        •   
                                        •   
                                        • 41
                                        •   
                                        •   
                                        • 42
                                        •   
                                        •   
                                        • 43
                                        •   
                                        •   
                                        • 44
                                        •   
                                        •   
                                        • 45
                                        •   
                                        •   
                                        • 46
                                        •   
                                        •   
                                        • 47
                                        •   
                                        •   
                                        • 48
                                        •   
                                        •   
                                        • 49
                                        •   
                                        •   
                                        • 50
                                        •   
                                        •   
                                        • 51
                                        •   
                                        •   
                                        • 52
                                        •   
                                        •   
                                        • 53
                                        •   
                                        •   
                                        • 54
                                        •   
                                        •   
                                        • 55
                                        •   
                                        •   
                                        • 56
                                        •   
                                        •   
                                        • 57
                                        •   
                                        •   
                                        • 58
                                        •   
                                        •   
                                        • 59
                                        •   
                                        •   
                                        • 60
                                        •   
                                        •   
                                        • 61
                                        •   
                                        •   
                                        • 62
                                        •   
                                        •   
                                        • 63
                                        •   
                                        •   
                                        • 64
                                        •   
                                        •   
                                        • 65
                                        •   
                                        •   
                                        • 66
                                        •   
                                        •   
                                        • 67
                                        •   
                                        •   
                                        • 68
                                        •   
                                        •   
                                        • 69
                                        •   
                                        •   
                                        • 70
                                        •   
                                        •   
                                        • 71
                                        •   
                                        •   
                                        • 72
                                        •   
                                        •   
                                        • 73
                                        •   
                                        •   
                                        • 74
                                        •   
                                        •   
                                        • 75
                                        •   
                                        •   
                                        • 76
                                        •   
                                        •   
                                        • 77
                                        •   
                                        •   
                                        • 78
                                        •   
                                        •   
                                        • 79
                                        •   
                                        •   
                                        • 80
                                        •   
                                        •   
                                        • 81
                                        •   
                                        •   
                                        • 82
                                        •   
                                        •   
                                        • 83
                                        •   
                                        •   
                                        • 84
                                        •   
                                        •   
                                        • 85
                                        •   
                                        •   
                                        • 86
                                        •   
                                        •   
                                        • 87
                                        •   
                                        •   
                                        • 88
                                        •  
                                        •  
                                        •  
                                        •  
                                            • 1
                                            • 2
                                            • 3
                                            • 4
                                            • 5
                                            • 6
                                            • 7
                                            • 8
                                            • 9
                                            • 10
                                            • 11
                                            • 12
                                            • 13
                                            • 14
                                            • 15
                                            • 16
                                            • 17
                                            • 18
                                            • 19
                                            • 20
                                            • 21
                                            • 22
                                            • 23
                                            • 24
                                            • 25
                                            • 26
                                            • 27
                                            • 28
                                            • 29
                                            • 30
                                            • 31
                                            • 32
                                            • 33
                                            • 34
                                            • 35
                                            • 36
                                            • 37
                                            • 38
                                            • 39
                                            • 40
                                            • 41
                                            • 42
                                            • 43
                                            • 44
                                            • 45
                                            • 46
                                            • 47
                                            • 48
                                            • 49
                                            • 50
                                            • 51
                                            • 52
                                            • 53
                                            • 54
                                            • 55
                                            • 56
                                            • 57
                                            • 58
                                            • 59
                                            • 60
                                            • 61
                                            • 62
                                            • 63
                                            • 64
                                            • 65
                                            • 66
                                            • 67
                                            • 68
                                            • 69
                                            • 70
                                            • 71
                                            • 72
                                            • 73
                                            • 74
                                            • 75
                                            • 76
                                            • 77
                                            • 78
                                            • 79
                                            • 80
                                            • 81
                                            • 82
                                            • 83
                                            • 84
                                            • 85
                                            • 86
                                            • 87
                                            • 88
                                            class UpdateCheckModule extends ReactContextBaseJavaModule {
                                          •     static final String TAG = "UpdateCheckModule";
                                          •     final String BUNDLE_VERSION = "CurrentBundleVersion";
                                          •     private SharedPreferences mSP;
                                          •     UpdateCheckModule(ReactApplicationContext reactContext) {
                                          •         super(reactContext);
                                          •         mSP = reactContext.getSharedPreferences("react_bundle",Context.MODE_PRIVATE);
                                          •     }
                                          •     public String getName() {
                                          •         return "UpdateCheck";
                                          •     }
                                          •     public Map<String,Object> getConstants() {
                                          •         Map<String,Object> constants = MapBuilder.newHashMap();
                                          •         //跟随apk一起打包的bundle基础版本号
                                          •         String bundleVersion = BuildConfig.BUNDLE_VERSION;
                                          •         //bundle更新后的当前版本号
                                          •         String cacheBundleVersion = mSP.getString(BUNDLE_VERSION,0); Box-sizing: border-Box;">"");
                                          •         if(!TextUtils.isEmpty(cacheBundleVersion)){
                                          •             bundleVersion = cacheBundleVersion;
                                          •         }
                                          •         constants.put(BUNDLE_VERSION,bundleVersion);
                                          •         return constants;
                                          •     }
                                          •     @ReactMethod
                                          •     void check(String currVersion){
                                          •         BmobQuery<AppInfo> query = new BmobQuery<>();
                                          •         query.setLimit(1);
                                          •         query.addWhereGreaterThan(new FindListener<AppInfo>() {
                                          •             @Override
                                          •             null){
                                          •                     null && !list.isEmpty()){
                                          •                         final AppInfo info = list.get(0);
                                          •                         File reactDir = new File(getReactApplicationContext().getCacheDir(),0); Box-sizing: border-Box;">"react_native");
                                          •                         //获取到更新消息,说明bundle有新版,在解压前先删除掉旧版
                                          •                         deleteDir(reactDir);
                                          •                         if(!reactDir.exists()){
                                          •                             reactDir.mkdirs();
                                          •                         }
                                          •                         "bundle-patch.zip");
                                          •                         BmobFile patchFile = info.getBundle();
                                          •                         //下载bundle-patch.zip文件
                                          •                         patchFile.download(saveFile,136); Box-sizing: border-Box;">new DownloadFileListener() {
                                          •                             @Override
                                          •                             null) {
                                          •                                     log("下载完成");
                                          •                                     //解压patch文件到react_native文件夹下
                                          •                                     boolean result = unzip(saveFile);
                                          •                                     if(result){//解压成功后保存当前最新bundle的版本
                                          •                                         mSP.edit().putString(BUNDLE_VERSION,info.getVersion()).apply();
                                          •                                         if(info.isImmediately()) {//立即加载bundle
                                          •                                             ((ReactApplication) getReactApplicationContext()).getReactNativeHost().clear();
                                          •                                             getCurrentActivity().recreate();
                                          •                                         }
                                          •                                     }else{//解压失败应该删除掉有问题的文件,防止RN加载错误的bundle文件
                                          •                                         File reactDir = "react_native");
                                          •                                         deleteDir(reactDir);
                                          •                                     }
                                          •                                 } else {
                                          •                                     e.printStackTrace();
                                          •                                     log("下载bundle patch失败");
                                          •                                 }
                                          •                             }
                                          •                             void onProgress(Integer per,136); Box-sizing: border-Box;">long size) {
                                          •                             }
                                          •                         });
                                          •                     }
                                          •                 }else{
                                          •                     e.printStackTrace();
                                          •                     log("获取版本信息失败");
                                          •                 }
                                          •             }
                                          •         });
                                          •     }
                                          • }
                                          • 代码中注释已经解释了其中的重要部分,需要注意的是,AppInfo增加了个boolean型immediately字段,来控制bundle是否立即生效

                                          •   
                                          •   
                                          • 1
                                          •   
                                          •   
                                          • 2
                                          •   
                                          •   
                                          • 3
                                          •   
                                          •   
                                          • 4
                                          •   
                                          •   
                                          • 5
                                          •   
                                          •   
                                          • 6
                                          •  
                                          •  private Boolean immediately;//bundle是否立即生效
                                          •     //要下载的bundle文件
                                          • }
                                          • 还有在getConstants()方法获取当前bundle版本时,使用BuildConfig.BUNDLE_VERSION来标记和apk一起打包的bundle基础版本号,也就是assets下的bundle版本号,该字段是通过gradle的buildConfigField来定义的。打开app/build.gradle,然后在下面所示的位置添加buildConfigField定义,具体如下:

                                          • //省略了其它代码
                                          • android{
                                          •     defaultConfig {
                                          •         buildConfigField "String",0); Box-sizing: border-Box;">"BUNDLE_VERSION",0); Box-sizing: border-Box;">'"1.0.0"'
                                          •     }
                                          • }
                                          • 接着,不要忘记将自定义的UpdateCheckModule注册到Packages里。如果,你对自定义module还不是很了解,请看这里

                                          • 最后,就是在JS端使用UpdateCheckModule来发起版本检测更新了。

                                          • 我们先在XiFan/js/db 创建一个配置文件Config.js

                                          •   
                                          •   
                                          • 1
                                          •   
                                          •   
                                          • 2
                                          •   
                                          •   
                                          • 3
                                          •   
                                          •   
                                          • 4
                                          •  
                                          •  
                                          •  
                                          •  
                                              • 1
                                              • 2
                                              • 3
                                              • 4
                                              const Config = {
                                            •     bundleVersion: '1.0.0'
                                            • };
                                            • export default Config;
                                            • 代码很简单,Config里面只是定义了个bundleVersion字段,表示当前bundle版本号。
                                              每次要发布新版bundle时,更新下这个文件的bundleVersion即可。

                                            • 然后,我们在MainScene.js的componentDidMount()函数中发起版本检测更新

                                            • //MainScene.js
                                            • //省略了其他代码
                                            • import {
                                            •     NativeModules
                                            • } from 'react-native';
                                            • import Config from './db/Config';
                                            • var UpdateCheck = NativeModules.UpdateCheck;
                                            • export default class MainScene extends Component{
                                            •     componentDidMount(){
                                            •         console.log('当前版本号:'+UpdateCheck.CurrentBundleVersion);
                                            •         UpdateCheck.check(Config.bundleVersion)
                                            •     }
                                            • }
                                            • 这样就完成了,基本的bundle更新流程了。

                                            • 总结

                                            • 本篇文章主要分析了RN android端bundle的加载过程,并且在分析理解下,实现了完整bundle包的基本热更新,但是这只是热更新的一部分,还有很多方面可以优化,比如:多模块的多bundle热更新、bundle拆分差量更新、热更新的异常回退处理、多版本bundle的动态切换、bundle的更新和apk的更新相结合等等,这也是之后继续研究学习的方向。

                                            • 最后,这个是项目的github地址,本章节的内容是在android分支上开发的,如需查看完整代码,克隆下来后请切换分支。

                                            猜你在找的React相关文章