ReactNative分布式热更新系统

热更新是一个非常方便的方案。在应对大量用户和深度定制的时候一定不能使用开源的方案。
一般第三方的这种方案,服务器带宽较小,或者不够灵活,不能满足自己的想法。
这里推荐自己实现对应的热更新方案。只需要少量代码即可支持。
下面推荐一种灵活的热更新方案。包括客户端的改造、接口设计、界面开发,同时是开源的!可以自由改造。

体验地址:demo 用户名密码都是:admin

ReactNative分布式热更新系统

基础数据的准备和实现

首先第一点,一个APP如果要支持热更新,需要在打开APP(或者其他进入RN页面之前)就要判断是否需要更新bundle文件。这里就是我们实现热更新的节点。一旦需要热更新就开始下载文件,而判断的接口就是我们这次文章的核心内容。这里简单贴出安卓和ios两端的下载逻辑。

请求之前需要在head中附带上客户端的几个重要信息。客户端版本号version、客户端唯一id:clientid、客户端类型platform、客户端品牌brand。

ios下载的例子

-(void)doCheckUpdate
{
  self.upView.viewButtonStart.hidden = YES;
  if ([XCUploadManager isFileExist:[XCUploadManager bundlePathUrl].path])
  {//沙盒里已经有了下载好的jsbundle,以沙盒文件优先
    self.oldSign = [FileHash md5HashOfFileAtPath:[XCUploadManager bundlePathUrl].path];
  }else
  {//真机计算出的包内bundlemd5有变化,可能是压缩了,所以这里写死初始化的md5
    //    NSString *ipPath = [[NSBundle mainBundle] pathForResource:@"main" ofType:@"jsbundle"];
    //    self.oldSign = [FileHash md5HashOfFileAtPath:ipPath];
    self.oldSign = projectBundleMd5;
  }
  
  AFHTTPSessionManager *_sharedClient = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"http://test.com"]];
  
  [self initAFNetClient:_sharedClient];

  [_sharedClient GET:@"api/check" parameters:nil progress:nil success:^(NSURLSessionDataTask * __unused task, id JSON) {
    NSDictionary *dic = [JSON valueForKeyPath:@"data"];
    BOOL isNeedLoadBundle = YES;
    if ([dic isKindOfClass:[NSDictionary class]])
    {
      self.updateSign = [dic stringForKey:@"sign"];
      self.downLoadUrl = [dic stringForKey:@"downloadUrl"];

      if(self.updateSign.length && self.oldSign.length && (![self.updateSign isEqualToString:self.oldSign]))
      {
        //需要更新bundle文件了
        self.upView.viewUpdate.hidden = NO;
        [self updateBundleNow];
        isNeedLoadBundle = NO;
      }else
      {
        //不需要更新bundle文件,再处理跳过按钮显示逻辑
        [self.upView showSkipButtonOrNot];
      }
    }
    if (isNeedLoadBundle) {
      [self loadBundle];
    }
  } failure:^(NSURLSessionDataTask *__unused task, NSError *error) {
    [self loadBundle];
  }];
}

安卓下载的例子

private void requestData() {

        subscribe = DalingNetwork
                .getDalingApi()
                .getBundleVersion()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new BaseSubscriber<BundleVersionResponse>() {
                    @Override
                    public void onError(Throwable e) {
                        startMainActivity();
                        e.printStackTrace();
                    }

                    @Override
                    public void onNext(final BundleVersionResponse response) {
                        isJSNeedUpdate = false;
                        if (response.status == 0) {
                            if (response.data != null) {
                                if (MainApplication.getApplication().getBundleMD5().equalsIgnoreCase(response.data.sign)) {
                                    //和本地版本相同,直接进入主页
                                    isJSNeedUpdate = false;
                                    tv_skip.setVisibility(View.VISIBLE);
                                    startMainActivity();
                                } else {
                                    //下载升级
                                    isJSNeedUpdate = true;
                                    downloadSign = response.data.sign;
                                    downloadUrl = response.data.downloadUrl;
                                    downLoad(response.data.downloadUrl, response.data.sign);
                                }
                            }
                        } else {
                            startMainActivity();
                        }
                    }
                });
    }

系统设计方案

首先来看一下我们是怎样设计客户端获取更新逻辑的。
ReactNative分布式热更新系统

  1. 客户端请求的时候会带上版本号、平台2个重要信息。
  2. 接口拿到请求之后查询对应的本地缓存,没有则去数据库查询。
  3. 从查询结果中筛查对应的3段数据:白名单、灰度、全量,判断顺序从左到右。
  4. 返回查询之后对应的结果。

数据库等设计

上面的设计是基础的逻辑,下面我们继续细化逻辑。其中为了支持更好的性能和分布式做了一些其他的方案设计。

根据逻辑自行设计是完全可以的