美文网首页React Native菜鸟入门RN iOS开发相关Hybrid开发
React-Native做本地服务器热更新/热部署(IOS)

React-Native做本地服务器热更新/热部署(IOS)

作者: 币乎iOS | 来源:发表于2018-02-28 16:49 被阅读1164次

    前言

            某个公司考虑使用React-Native的理由有很多,热更新/热部署可能是一个很大的原因。如果公司认为热更新/热部署是主要原因的话,那么这应该是一个很错误的决定。因为APP的质量和具有前瞻性的规划才是您真正需要考虑的事情,而不是热更新的补救。当然不可否认的是热更新/热部署很符合敏捷开发这个思维.......

    进入正题

            现在热更新的第三方服务很多:微软的CodePushRN中文网的Pushy......国内还是推荐使用国内的服务吧。

            既然有这么多第三方服务为何还要自己开发热更新/热部署功能呢?

            1、安全性

            2、可控性

            3、实现简单的业务逻辑开发起来其实很简单

            4、之前使用的微软CodePush的服务下载时出现很多问题,于是决定自己开发。

    开发思路

    开发思路

    开发步骤

    1、编译项目

    ios打包编译:CD到项目根目录运行命令

    react-native bundle --entry-file index.ios.js --platform ios --dev false --bundle-output ./ios/bundle/index.ios.jsbundle --assets-dest ./ios/bundle

    安卓打包请移步到打包APK

    ios打包完成后在项目中的ios目录中多出了bundle文件夹,这里就是我们打包出来的RN运行文件,实际上RN运行的就是这里的.jsbundle文件。

    编译项目目录

    2、压缩编译文件

    把bundle文件夹压缩为ZIP包

    3、把压缩的bundle的ZIP包上传到后端

    4、更新后台相关的配置文件,比如:RN包的版本号、更新日志、是否强制更新、是否静默更新、是否回滚......具体有多少功能是你们的自己的事


    5、下载最新的包,这里开始是写代码的时候了

    5.1首先网络请求服务的RN配置内容

    5.2对比本地RN的版本号

    5.3弹出更新

    5.4下载解压最新的包

    下载和解压文件的代码:下载使用的是AFN、解压使用的是SSZipArchive

    AFHTTPSessionManager *mgr = [AFHTTPSessionManager manager];

        //下载地址

        NSURL*mgrurl = [NSURLURLWithString:self.updateUrl];

        NSURLRequest*request = [NSURLRequestrequestWithURL:mgrurl];

        NSURLSessionDownloadTask*download = [mgrdownloadTaskWithRequest:requestprogress:^(NSProgress*_NonnulldownloadProgress) {

          //在主线程中调用

          [[NSOperationQueue mainQueue]addOperationWithBlock:^{

            NSLog(@"进度%f",1.0*downloadProgress.completedUnitCount/ downloadProgress.totalUnitCount);

            CGFloatjsw =1.0* downloadProgress.completedUnitCount/ downloadProgress.totalUnitCount* (PW-40);

            self.jdView.frame=CGRectMake(0,0, jsw,5);

            self.jdText.text= [NSStringstringWithFormat:@"更新进度(%.0f/100)",100.0*downloadProgress.completedUnitCount/ downloadProgress.totalUnitCount];

            self.suduText.text= [NSStringstringWithFormat:@"%lldkb/s",[MLRNSingtongetInterfaceBytes]/1024/1024];

          }];

        }destination:^NSURL*_Nonnull(NSURL*_NonnulltargetPath,NSURLResponse*_Nonnullresponse) {

          NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];

          NSLog(@"targetPath:%@",targetPath);

          NSLog(@"fullPath:%@",fullPath);

          return[NSURLfileURLWithPath:fullPath];

        }completionHandler:^(NSURLResponse*_Nonnullresponse,NSURL*_NullablefilePath,NSError*_Nullableerror) {

          //下载完成,解压

          //Caches路径

          NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];

          NSString *xxxx = [[filePath absoluteString] substringFromIndex:7];

          [SSZipArchive unzipFileAtPath: xxxx toDestination:cachesPath];

          [[NSOperationQueue mainQueue]addOperationWithBlock:^{

            [MBProgressHUD hideHUD];

            UIStoryboard*storayobard = [UIStoryboardstoryboardWithName:@"main"bundle:nil];

            self.view.window.rootViewController = storayobard.instantiateInitialViewController;

          }];

        }];

        [downloadresume];

    下载和解压和重启app

    5.5运行本地的RN文件

    在使用调用RN的地方(一般在AppDelegate中)

    修改RCTRootView初始化方法为

    RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:[[RCTBridge alloc] initWithDelegate:self launchOptions:nil] moduleName:@""name"" initialProperties:nil];

    修改RCTRootView初始化方法

    添加代理RCTBridgeDelegate

    添加代理

    实现RCTBridgeDelegate中的sourceURLForBridge方法

    - (NSURL*)sourceURLForBridge:(RCTBridge*)bridge {

      <#code#>

    }

    sourceURLForBridge方法

    在sourceURLForBridge方法中确定您是要使用最新那个URL中的包

    我这里的逻辑是

    判断沙盒是否存在.jsbundle文件 --->  有则使用沙盒中的.jsbundle

                                                                没有则使用本地打包的文件

    - (NSURL*)sourceURLForBridge:(RCTBridge*)bridge{

      NSURL*jsCodeLocation;

      // 取得沙盒目录

      NSString *localPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

      // 要检查的文件目录

      NSString*filePath = [localPath  stringByAppendingPathComponent:@"bundle/index.ios.jsbundle"];

      NSFileManager *fileManager = [NSFileManager defaultManager];

      if([fileManagerfileExistsAtPath:filePath]) {

        NSString*newUrl = [NSStringstringWithFormat:@"file://%@",filePath];

        jsCodeLocation = [NSURLURLWithString:newUrl];

        NSLog(@"文件存在");

        returnjsCodeLocation;

      }else{

    #if DEBUG

        // 原来的jsCodeLocation

        jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];

    #else

        jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"bundle/index.ios"withExtension:@"jsbundle"];

    #endif

        NSLog(@"文件不存在");

        returnjsCodeLocation;

      }

    }

    判断代码

    大功告成运行项目。

    到这里最基础的热更新/热部署服务就搭建完成。如果想要更加完善还是需要花点功夫的,毕竟师傅领进门修行靠个人。

    相关文章

      网友评论

      • 神魔狼:getInterfaceBytes 这个怎么获取的
        币乎iOS:下载进度相关的东西,你可以用你自己的方法去获取
      • 我本善良:撇开js和0C交互的原理来说,热更实际就是main.jsbundle文件的更新,本质就是本地文件的更新
        币乎iOS:@我本善良 是的

      本文标题:React-Native做本地服务器热更新/热部署(IOS)

      本文链接:https://www.haomeiwen.com/subject/ymyexftx.html