React-Native开发iOS篇-热更新的实现

需求

1.在打开APP的时候进行网络请求,检查是否有网络更新。

2.如果有网络更新,下载新的版本,再次打开APP的时候,就直接连接到新的内容。

具体功能的实现:

AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

  UpdataLoader* upLoatder = [UpdataLoader shareUpLoader];
  //保证存放路径是存在的
  [upLoatder createPath];
  //检查更新并下载,有更新则直接下载,无则保持默认配置
  [upLoatder getAppVersion];
  //定义加载bundle的URL
  NSURL *jsCodeLocation;
  
  NSString* iOSBundlePath = [[upLoatder iOSFileBundlePath] stringByAppendingPathComponent:@"index.ios.bundle"];
  if ([[NSFileManager defaultManager] fileExistsAtPath:iOSBundlePath]) {
   jsCodeLocation = [NSURL URLWithString:iOSBundlePath];
  }else{
    jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
  }
  
//  NSLog(@"jsCodeLocation%@",jsCodeLocation);
  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"******"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];
  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
  [DownLoadTool defaultDownLoadTool].view = rootView;
  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  return YES;
}

上述代码是在Appdelegate中的实现,关键点有两个:
1.实现对版本号的数据持久化存储。
2.如何加载沙盒路径下的js.bundle文件。


实现数据持久化的方法

在三种数据持久化得方式中,我选择了plist文件去存储版本号和下载路径。

//获取版本信息储存的文件路径
-(NSString*)getVersionPlistPath{
  
  //获取沙盒路径
  NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  NSString* path = [paths objectAtIndex:0];
  NSLog(@"the save version file's path is :%@",path);
  //填写文件名
  NSString* filePath = [path stringByAppendingPathComponent:kVersionPlist];
  //  NSLog(@"文件路径为:%@",filePath);
  return filePath;
}

//创建或修改版本信息
-(void)writeAppVersionInfoWithDictiony:(NSDictionary*)dictionary{
  NSString* filePath  = [self getVersionPlistPath];
  [dictionary writeToFile:filePath atomically:YES];
}

分为两步,第一步确定存储路径,第二步将要存储的信息调用

 [dictionary writeToFile:filePath atomically:YES];

的方法去写入本地路径。


实现从本地文件夹中获取bundle并加载

在这里,有以下几个问题需要去解决:

  • 如何下载?
  • 下载完毕后如何解压?
  • 解压完成后如何配置文件?

首先解决如何下载的问题,在这里我选择了第三方AFNetworking 3.0来实现对文件的下载。

-(void)downLoadWithUrl:(NSString*)url{
  [self showPromptWithStr:@"发现新的版本,开始下载..."];
  //根据url下载相关文件
  NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
  AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
  NSURL *URL = [NSURL URLWithString:url];
  NSURLRequest *request = [NSURLRequest requestWithURL:URL];
  NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
      //获取下载进度
//      NSLog(@"Progress is %f", downloadProgress.fractionCompleted);
  } destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
    //有返回值的block,返回文件存储路径
    NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
    NSURL* targetPathUrl = [documentsDirectoryURL URLByAppendingPathComponent:kiOSFileName];
    return [targetPathUrl URLByAppendingPathComponent:[response suggestedFilename]];

  } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
    if(error){
      //下载出现错误
      NSLog(@"%@",error);
      
    }else{
      [self showPromptWithStr:@"更新完毕。请重新启动******!"];
      //下载成功
      NSLog(@"File downloaded to: %@", filePath);
      self.zipPath = [[filePath absoluteString] substringFromIndex:7];
      //下载成功后更新本地存储信息
      [[UpdataLoader shareUpLoader] writeAppVersionInfoWithDictiony:[UpdataLoader shareUpLoader].versionInfo];
      //解压并删除压缩包
      if ([self unZip]) {
        [self deleteZip];
      };

    }
  }];
  [downloadTask resume];
}

在这里需要注意的是每个下载的设置默认是被挂起的,所以在设置完毕之后,需要调用

  [downloadTask resume];

使得下载主动进行。

在下载完毕之后的block回调中,是在主线程中进行的,在这里就可以进行一些UI的更新。

注意在完成之后返回的文件路径filePath为:
file:///*********
直接进行使用发现,获取不到文件。
必须将file:///去掉才能解决问题。
利用nsstring提供的方法来实现。

self.zipPath = [[filePath absoluteString] substringFromIndex:7];

OK解决的下载问题,现在来解决解压问题,解压方式我选择了第三方提供的SSZipArchive.
代码如下:


//解压压缩包
-(BOOL)unZip{
  if (self.zipPath == nil) {
    return NO;
  }
  //检查Document里有没有bundle文件夹
  NSString* path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
  NSString* bundlePath = [path stringByAppendingPathComponent:kiOSfileSetName];
  BOOL isDir;
  //如果有,则删除后解压,如果没有则直接解压
  if ([[NSFileManager defaultManager] fileExistsAtPath:bundlePath isDirectory:&isDir]&&isDir) {
    [[NSFileManager defaultManager] removeItemAtPath:bundlePath error:nil];
  }
  NSString *zipPath = self.zipPath;
  NSString *destinationPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
  BOOL success = [SSZipArchive unzipFileAtPath:zipPath
                                 toDestination:destinationPath];
  return success;
}

//删除压缩包
-(void)deleteZip{
  NSError* error = nil;
  [[NSFileManager defaultManager] removeItemAtPath:self.zipPath error:&error];
}

最后,来解决一下如何配置的问题:
在各种文档里都没有找到说明如何加载一个现有的js.bundle文件。
经过我个人的几番尝试,我找到了如下方法:

 NSURL *jsCodeLocation;
jsCodeLocation = [NSURL URLWithString:iOSBundlePath];

RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"Dingla"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];

iOSBUndlepath为沙盒内的文件路径。

至此,iOS的热更新就实现了。

git仓库点我

2017.09.14 PS:
因为文章写的比较早,源码我已经删除了。
感谢这位同学将代码补全。
附上地址点我

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容