ReactNative 告别CodePush,自建热更新版本升级环境

微软的CodePush热更新非常难用大家都知道,速度跟被墙了没什么区别。

另外一方面,我们不希望把代码放到别人的服务器。自己写接口更新总归感觉安全一点。

so,就来自己搞个React-Native APP的热更新管理工具吧。暂且命名为hotdog。

 

/**************************************************/

首先我们要弄清react-native启动的原理,是直接调用jslocation的jsbundle文件和assets资源文件。

由此,我们可以自己通过的服务器接口去判断版本,并下载最新的然后替换相应的文件,然后从这个文件调用启动APP。这就像之前的一些H5APP一样做版本的管理。

 

以iOS为例,我们需要分以下几步去搭建这个自己的RN升级插件:

一、设置默认jsbundle地址(比如document文件夹):

1.首先打包的时候把jsbundle和assets放入copy bundle resource,每次启动后,检测document文件夹是否存在,不存在则拷贝到document文件夹,然后给RN框架读取启动。

我们建立如下的bundle文件管理类:

MXBundleHelper.h

#import <Foundation/Foundation.h>@interface MXBundleHelper : NSObject+(NSURL *)getBundlePath;@end

MXBundleHelper.m

#import "MXBundleHelper.h"
#import "RCTBundleURLProvider.h"
@implementation MXBundleHelper
+(NSURL *)getBundlePath{
#ifdef  DEBUGNSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];return jsCodeLocation;
#else//需要存放和读取的document路径//jsbundle地址NSString *jsCachePath = [NSString stringWithFormat:@"%@/\%@",NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0],@"main.jsbundle"];//assets文件夹地址NSString *assetsCachePath = [NSString stringWithFormat:@"%@/\%@",NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0],@"assets"];//判断JSBundle是否存在BOOL jsExist = [[NSFileManager defaultManager] fileExistsAtPath:jsCachePath];//如果已存在if(jsExist){NSLog(@"js已存在: %@",jsCachePath);//如果不存在}else{NSString *jsBundlePath = [[NSBundle mainBundle] pathForResource:@"main" ofType:@"jsbundle"];[[NSFileManager defaultManager] copyItemAtPath:jsBundlePath toPath:jsCachePath error:nil];NSLog(@"js已拷贝至Document: %@",jsCachePath);}//判断assets是否存在BOOL assetsExist = [[NSFileManager defaultManager] fileExistsAtPath:assetsCachePath];//如果已存在if(assetsExist){NSLog(@"assets已存在: %@",assetsCachePath);//如果不存在}else{NSString *assetsBundlePath = [[NSBundle mainBundle] pathForResource:@"assets" ofType:nil];[[NSFileManager defaultManager] copyItemAtPath:assetsBundlePath toPath:assetsCachePath error:nil];NSLog(@"assets已拷贝至Document: %@",assetsCachePath);}return [NSURL URLWithString:jsCachePath];
#endif
}

 

二、做升级检测,有更新则下载,然后对本地文件进行替换:

假如我们不立即做更新,可以更新后替换,然后不会影响本次APP的使用,下次使用就会默认是最新的了。

如果立即更新的话,需要使用到RCTBridge类里的reload函数进行重启。

这里通过NSURLSession进行下载,然后zip解压缩等方法来实现文件的替换。

 

MXUpdateHelper.h

#import <Foundation/Foundation.h>
typedef void(^FinishBlock) (NSInteger status,id data);@interface MXUpdateHelper : NSObject
+(void)checkUpdate:(FinishBlock)finish;
@end

MXUpdateHelper.m

#import "MXUpdateHelper.h"@implementation MXUpdateHelper
+(void)checkUpdate:(FinishBlock)finish{NSString *url = @"http://www.xxx.com/xxxxxxx";NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];[newRequest setHTTPMethod:@"GET"];[NSURLConnection sendAsynchronousRequest:newRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * response, NSData * data, NSError * connectionError) {if(connectionError == nil){//请求自己服务器的API,判断当前的JS版本是否最新/*{"version":"1.0.5","fileUrl":"http://www.xxxx.com/xxx.zip","message":"有新版本,请更新到我们最新的版本","forceUpdate:"NO"}*///假如需要更新NSString *curVersion = @"1.0.0";NSString *newVersion = @"2.0.0";//一般情况下不一样,就是旧版本了if(![curVersion isEqualToString:newVersion]){finish(1,data);}else{finish(0,nil);}}}];
}
@end

 

 

三、APPdelegate中的定制,弹框,直接强制更新等

如果需要强制刷新reload,我们新建RCTView的方式也需要稍微改下,通过新建一个RCTBridge的对象。

因为RCTBridge中有reload的接口可以使用。

 

#import "AppDelegate.h"
#import "RCTBundleURLProvider.h"
#import "RCTRootView.h"
#import "MXBundleHelper.h"
#import "MXUpdateHelper.h"
#import "MXFileHelper.h"
#import "SSZipArchive.h"
@interface AppDelegate()<UIAlertViewDelegate>
@property (nonatomic,strong) RCTBridge *bridge;
@property (nonatomic,strong) NSDictionary *versionDic;
@end@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{NSURL *jsCodeLocation;jsCodeLocation = [MXBundleHelper getBundlePath];_bridge = [[RCTBridge alloc] initWithBundleURL:jsCodeLocationmoduleProvider:nillaunchOptions:launchOptions];RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:_bridge moduleName:@"MXVersionManager" initialProperties:nil];rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];UIViewController *rootViewController = [UIViewController new];rootViewController.view = rootView;self.window.rootViewController = rootViewController;[self.window makeKeyAndVisible];__weak AppDelegate *weakself = self;//更新检测[MXUpdateHelper checkUpdate:^(NSInteger status, id data) {if(status == 1){weakself.versionDic = data;/*这里具体关乎用户体验的方式就多种多样了,比如自动立即更新,弹框立即更新,自动下载下次打开再更新等。*/UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:data[@"message"] delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"现在更新", nil];[alert show];//进行下载,并更新//下载完,覆盖JS和assets,并reload界面
//      [weakself.bridge reload];
    }}];return YES;
}- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{if(buttonIndex == 1){//更新[[MXFileHelper shared] downloadFileWithURLString:_versionDic[@"fileurl"] finish:^(NSInteger status, id data) {if(status == 1){NSLog(@"下载完成");NSError *error;NSString *filePath = (NSString *)data;NSString *desPath = [NSString stringWithFormat:@"%@",NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]];[SSZipArchive unzipFileAtPath:filePath toDestination:desPath overwrite:YES password:nil error:&error];if(!error){NSLog(@"解压成功");[_bridge reload];}else{NSLog(@"解压失败");}}}];}
}

 

流程简单,通过接口请求版本,然后下载到document去访问。 其中需要做版本缓存,Zip的解压缩,以及文件拷贝等。

运行iOS工程可以看到效果。 初始为1.0.0版本,然后更新后升级到1.0.1版本。

 

demo: https://github.com/rayshen/MXHotdog

 

转载于:https://www.cnblogs.com/rayshen/p/5737293.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/470310.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript高级之ECMASript 7、8 、9 、10 新特性

第3章 ECMASript 7 新特性 3.1. Array.prototype.includes Includes 方法用来检测数组中是否包含某个元素&#xff0c;返回布尔类型值 3.2. 指数操作符 在ES7中引入指数运算符「 **」&#xff0c;用来实现幂运算&#xff0c;功能与 Math.pow结果相同 第4章 ECMASript 8 新特…

python socket 大文件_python:socket传输大文件示例

#!/usr/bin/python# -*- coding: utf-8 -*-importsysreload(sys)sys.setdefaultencoding("utf-8")importtime等待连接等待发送文件读取数据写入文件并且保存等待连接importsocketimportthreadingimporttimeimportstructdeffunction(newsock, address):FILEINFO_SIZEs…

swagger core 和 swagger ui 如何关联【窥探】

几个片段&#xff1a; package io.swagger.jaxrs.listing;import io.swagger.annotations.ApiOperation; import org.apache.commons.lang3.StringUtils;import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.ws.rs.GET; import javax.ws.r…

vb 根据pid获取句柄_C++中避免返回指向对象内部的句柄(handles)

点蓝色字关注“CurryCoder的程序人生”微信公众号&#xff1a;CurryCoder的程序人生欢迎关注我&#xff0c;一起学习&#xff0c;一起进步!1.问题的引入假如你正在给一个应用写一个矩形类&#xff0c;这个矩形由左上角和右下角的顶点坐标表示。为了表示这两个点&#xff0c;我们…

关于MapReduce中自定义Combine类(一)

MRJobConfigpublic static fina COMBINE_CLASS_ATTR属性COMBINE_CLASS_ATTR "mapreduce.job.combine.class"————子接口&#xff08;F4&#xff09; JobContent方法getCombinerClass————子实现类 JobContextImpl实现getCombinerClass方法&#xff1a;public C…

react脚手架配置代理解决跨域问题

一、问题描述&#xff1a; 控制台报错&#xff0c;出现跨域问题 二、解决方案 配置代理&#xff1a; 第一种配置方式&#xff1a; 在package.json中追加如下配置 "proxy":"http://localhost:5000"说明&#xff1a; 优点&#xff1a;配置简单&#xff…

kstools工具是什么牌子_2020年平衡车推荐,电动平衡车哪个牌子好?老司机教你如何选购电动平衡车...

2020年平衡车推荐&#xff0c;电动平衡车哪个牌子好&#xff1f;老司机教你如何选购电动平衡车随着我国科技的发展&#xff0c;生活水平的提高&#xff0c;在很多地方都出现了电动平衡车的身影&#xff0c;人们将电动平衡车当做短距离代步的工具&#xff0c;也是非常实用的。很…

plsql 中的一些好的设置和快捷键总结

1、SQL语句字符全部大写 自认为这是个好习惯&#xff0c;信息系统的核心是数据库&#xff0c;系统出问题时最先要查的就是SQL语句&#xff0c;怎样在浩瀚的日志中快速找到那条SQL语句是件比较痛苦的事情。 SQL语句全部大写并不能彻底解决这一问题&#xff0c;但在一堆代码…

python工控怎么样_搞工控不了解python,好比雄鹰断了翅膀,理由在这里!

这时&#xff0c;距离下班只有30分钟了。无奈&#xff0c;先梳理一下要展示的数据指标&#xff1a;达成率&#xff0c;环比&#xff0c;增长、人均单产、人力成本等数据的演算、推导、分析&#xff0c;还要匹配对应的PPT数据魔方....数据很多&#xff0c;先把每个人的数据调出来…

react父子组件通信案例

父组件&#xff1a;App组件 子组件&#xff1a;Search组件、List组件 案例需求&#xff1a;文本框中输入关键词&#xff0c;点击搜索按钮后&#xff0c;下方列表展示出搜索结果 实现思路&#xff1a; 子组件Search组件向父组件App传递状态&#xff08;状态包括&#xff1a;是否…

模块与包(8)

第8章 复杂程序组织模块&#xff1a;包含函数或者类的Python程序导入模块形式&#xff1a;import 模块名import 模块名 as 新名字 from 模块名 import 函数名from 模块名 import * #导入模块中的所有代码使用import和from导入&#xff0c;调用函数的不同&#xff1a;使…

matlab画线不同颜色_怎样画线框图才有意义

我们常轻忽身边习以为常的事物&#xff0c;觉得没有必要为一些看似简单又可有可无的东西浪费时间——例如线框图。虽然没必要凡事都寻根问底&#xff0c;但当面对复杂问题时&#xff0c;脚踏实地回归基本面也许才是根本解法。本文章深入介绍程序开发界面设计中&#xff0c;最简…

react 消息订阅-发布机制(解决兄弟组件通信问题)

消息订阅-发布机制 工具库: PubSubJS下载: npm install pubsub-js --save使用: 1)import PubSub from ‘pubsub-js’ //引入 2)PubSub.subscribe(‘delete’, function(data){ }); //订阅 3)PubSub.publish(‘delete’, data) //发布消息 App组件&#xff1a; import React,…

运行Myeclipse发生这事这是怎么回事,大神们

转载于:https://www.cnblogs.com/zhuh102/p/5753616.html

非零返回怎么解决_VLOOKUP如何返回多个值?

今天我来谈谈大家最熟悉的函数&#xff0c;也是使用频率最高的函数&#xff0c;基本是每天都在使用-VLOOKUP大家都知道VLOOKUP可以根据条件&#xff0c;查找并返回满足条件对应列的值&#xff0c;但是他的设定只是只能返回第一个满足条件的值如果我们要返回满足条件的多个值&am…

Fetch发送网络请求

1. 文档 https://github.github.io/fetch/https://segmentfault.com/a/1190000003810652 2. 特点 fetch: 原生函数&#xff0c;不再使用XmlHttpRequest对象提交ajax请求老版本浏览器可能不支持 3. 相关API GET请求 fetch(url).then(function(response) {return response.…

JSX详解React的事件绑定事件参数的传递

一、认识JSX 这段element变量的声明右侧赋值的标签语法是什么呢&#xff1f; 它不是一段字符串&#xff08;因为没有使用引号包裹&#xff09;&#xff0c;它看起来是一段HTML原生&#xff0c;但是我们能在js中直接给一个变量赋值html吗&#xff1f;其实是不可以的&#xff0c…

剑指Offer 从尾到头打印链表

题目描述 输入一个链表&#xff0c;从尾到头打印链表每个节点的值。 输入描述: 输入为链表的表头 输出描述: 输出为需要打印的“新链表”的表头 思路&#xff1a; 用容器vector&#xff0c;递归到最后一个元素&#xff0c;push_back到dev中。 AC代码&#xff1a; 1 /**2 * str…

python排序元组两个元素_在python中对具有3个元素的元组列表进行排...

只需对列表进行排序&#xff1b;默认排序功能可以满足您的需求.比较两个元组时,将根据它们的内容对其进行排序&#xff1b;首先对第一个元素进行排序,如果相等,则对第二个元素进行排序,依此类推.演示&#xff1a;>>> L [(14, 2, 3), (1, 14, 0), (14, 1, 1), (1, 14,…

叠积木

【题目描述】 约翰和贝西在叠积木。共有30000块积木&#xff0c;编号为1到30000。一开始&#xff0c;这些积木放在地上&#xff0c;自然地分成N堆。贝西接受约翰的指示&#xff0c;把一些积木叠在另一些积木的上面。一旦两块积木相叠&#xff0c; 彼此就再也不会分开了&#xf…