ReactNative分布式热更新系统
热更新是一个非常方便的方案。在应对大量用户和深度定制的时候一定不能使用开源的方案。
一般第三方的这种方案,服务器带宽较小,或者不够灵活,不能满足自己的想法。
这里推荐自己实现对应的热更新方案。只需要少量代码即可支持。
下面推荐一种灵活的热更新方案。包括客户端的改造、接口设计、界面开发,同时是开源的!可以自由改造。
体验地址:demo 用户名密码都是:admin
基础数据的准备和实现
首先第一点,一个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(); } } }); }
系统设计方案
首先来看一下我们是怎样设计客户端获取更新逻辑的。
- 客户端请求的时候会带上版本号、平台2个重要信息。
- 接口拿到请求之后查询对应的本地缓存,没有则去数据库查询。
- 从查询结果中筛查对应的3段数据:白名单、灰度、全量,判断顺序从左到右。
- 返回查询之后对应的结果。
数据库等设计
上面的设计是基础的逻辑,下面我们继续细化逻辑。其中为了支持更好的性能和分布式做了一些其他的方案设计。
根据逻辑自行设计是完全可以的