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;@endMXBundleHelper.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; @endMXUpdateHelper.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
總結
以上是生活随笔為你收集整理的ReactNative 告别CodePush,自建热更新版本升级环境的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux的几个文本内容查看命令小结
- 下一篇: element-ui的paginatio