react-native之热更新

622 查看

什么是热更新

简单说就是不需要去应用市场重新下载,直接打开app就会下载更新的内容然后进入app,类似于经常玩游戏,游戏里需要更新,然后就有个进度条在读取。总结就是可以不通过应用市场来进行升级,极大的提升了app修bug和赋予新功能的能力

RN热更新的原理是什么

一个完整的RN-app程序通常包含以下几个部分:

  • native代码部分

  • js代码部分-rn代码、依赖的第三方库、业务代码等

  • 图片资源部分

native代码发生了变动

如果你的项目的native代码发生了变动,对不起,热更新不能满足你的需求,你只能硬更新,让用户重新下载新的来覆盖旧的app。截止的当前日期,RN的版本还只是0.32,距离1.0还很遥远。所以经常会有需要用到的功能,而RN原生没有封装,所以只能亲自来写,当然,如果我们有一个完整的无线团队,那么是极好的,直接去把无线团队里的类库中筛选个一些可能会用到的功能,先提前封装进来,尽量的减少热更新的次数。

图片资源或者是js发生变动

而如果我们是想修改图片资源或者是js的代码部分,好的,可以使用热更新,那么既然是热更新,我就置想去修改变动的内容,计算方法:新版本(V3.1) - 旧版本(v3.0) = 增量包

好的,以上都是理论原理内容,由于项目期只有2个人在做RN前端方面的开发,所以没有足够的时间去开发公司内部的热更新。所以使用了第三方的组件react-native-pushy

如何使用react-native-pushy

注册一个pushy账号

pushy

配置Bundle URL(iOS)

在工程target的Build Phases->Link Binary with Libraries中加入libz.tbd、libbz2.1.0.tbd

在你的AppDelegate.m文件中增加如下代码:

// ... 其它代码

#import "RCTHotUpdate.h"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
#if DEBUG
  // 原来的jsCodeLocation
  jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
#else
  jsCodeLocation=[RCTHotUpdate bundleURL];
#endif
  // ... 其它代码
}

配置Bundle URL(Android)

0.29及以后版本:在你的MainApplication中增加如下代码:

// ... 其它代码

import cn.reactnative.modules.update.UpdateContext;
public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    protected String getJSBundleFile() {
        return UpdateContext.getBundleUrl(MainApplication.this);
    }
    // ... 其它代码
  }
}

0.28及以前版本:在你的MainActivity中增加如下代码:

// ... 其它代码

import cn.reactnative.modules.update.UpdateContext;

public class MainActivity extends ReactActivity {

    @Override
    protected String getJSBundleFile() {
        return UpdateContext.getBundleUrl(this);
    }
    // ... 其它代码
}

添加热更新功能

页面需要引用react-native-update模块

import {
  isFirstTime,
  isRolledBack,
  packageVersion,
  currentVersion,
  checkUpdate,
  downloadUpdate,
  switchVersion,
  switchVersionLater,
  markSuccess,
} from 'react-native-update';

通常情况下,热更新的判断需要在app启动上来就要进行判断,那么大多数都会写在index.android.js中

  componentWillMount(){
    //去除debug时候的警告,测试的时候建议打开,hotloading的时候可以关掉
    // console.disableYellowBox = true;
    // 2s 后如果还没有响应 则提示并取消
    let freshedFlag = false;
    let timeout = setTimeout(() => {
         timeout && clearTimeout(timeout);
         
         if (freshedFlag) return;

         freshedFlag = true;

         console.log('超时');
         
         //做些什么,比如setState让页面跳过

    }, REQ_TIMEOUT);
    
    //防止反触发,就是更新完了回滚
    markSuccess();
    
    //异步函数checkUpdate可以检查当前版本是否需要更新
    
    checkUpdate(appKey).then(info => {
      // freshedFlag 为 true 则说明超时
      console.log('timeout'+freshedFlag);
      if (freshedFlag) {
          return;
      } else {
          freshedFlag = true;
      }
      //包过期,需要下载最新版的应用(非热更新)
      if (info.expired) {
          //进度条隐藏,新版本弹窗,提供下载地址
          this.setState({
              progressState:false,
              showDialog:true,
              downloadUrl:info.downloadUrl
          })
      }
      //当前版本是最新版本,无需热更新 
      else if (info.upToDate) {
          this.setState({
            progressState:false,
            progressNum: 100,
            welcome:false,
            update:false
          })
      } else {
          //需要热更新了
          this.doUpdate(info)
      }

    }).catch(err => {
      this.setState({
        progressState:false,
        welcome:false,
      })
    });
  }
  //热更新函数
  doUpdate = info => {
    
    //做点什么,让你的UI显示出来,提供个假性的进度条什么的
    
    downloadUpdate(info).then(hash => {
        //下载完版本返回一个hash字符串,是当前笨笨的唯一标示,然后切换版本
      switchVersion(hash);
    }).catch(err => {
      this.setState({
        progressState:false,
        welcome:false,
      })
    });
  };

就理论而言,热更新操作到此结束,但是实际使用过程中,5000个用户大概会有100个丢掉的可能性。不是特别的准,也存在少量用户回滚版本的行为。听说公司其他部门的团队做的app用的是codePush,后期也可以多研究一下。