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段数据:白名单、灰度、全量,判断顺序从左到右。
- 返回查询之后对应的结果。
数据库等设计
上面的设计是基础的逻辑,下面我们继续细化逻辑。其中为了支持更好的性能和分布式做了一些其他的方案设计。
根据逻辑自行设计是完全可以的