ionic2热更新插件cordova-hot-code-push-plugin更新下载文件

时间:2022-12-07 22:59:02

以iOS为例

说下需求:在主项目中根据需要下载子项目(或文件),并子项目能利用主项目中的cordova-hot-code-push-plugin的跟随主项目更新。一个是包含全部子项目的页面,一个是选择下载子项目后的页面,都是iOS原生。

一、必要条件

1、首先要搭建个本地服务器,可参考http://blog.csdn.net/liuyuyefz/article/details/8072485,简单快捷。

      先把要下载的项目压缩包放在本地服务器的 /usr/local/apache-tomcat-7.0.32/webapps/www里面,以便下载。

2、cordova-hot-code-push-plugin插件已安装在项目。

3、本地服务器已开启。

二、下载(iOS原生做的下载)

1、首先要先设置好下载路径,下载路径要跟热更新的下载更新路径是相同的,需要注意一点,文件不是存放在项目文件夹,是存放在项目运行的路径

 NSUserDefaults *user=[NSUserDefaults standardUserDefaults];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSAllLibrariesDirectory, NSUserDomainMask, YES);
    NSLog(@"------------paths=%@",paths);
    
    NSString *documentsDirectory = [NSString stringWithFormat:@"%@/Application Support/com.ionicframework.superappionic558256/cordova-hot-code-push-plugin/%@/www",[paths objectAtIndex:0],[user objectForKey:@"currentReleaseVersionName"]];
看一下拼接路径:

[paths objectAtIndex:0]:这个不必说,自然是自动获取;

Application Support:值得一提的是这个文件夹,这个文件是进入cordova,ionic页面运行后出现的(个人认为进入cordova就可以有),所以,必须要进入cordova,ionic页面先运行一次,否则没有这个文件夹。此时,肯定有人会说,既然这样那我直接让做管理子项目下载的页面控制器继承于CDVViewController,省了前面的复杂,我利用属性传值测试了一下,CDVViewController与UIViewController之间push的时候要么不传值,要么崩,但是,NSUserDefaults是可以的,原因大家自己去找吧;

com.ionicframework.superappionic558256:测试了一下,清空运行缓存再运行,两次生成的都是这个名字文件夹,这跟config.xml有关,由里面的id决定;

<widget id="com.ionicframework.superappionic558256" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
cordova-hot-code-push-plugin:插件名;

[user objectForKey:@"currentReleaseVersionName"]:当前显示给用户的版本,即chcp.json里面的release对应的值。去HCPPlugin.m里面的doLocalInit方法取值

/**
 *  Perform initialization of the plugin variables.执行插件的初始化
 */
- (void)doLocalInit {
    _defaultCallbackStoredResults = [[NSMutableArray alloc] init];
    
    // init plugin config from xml
    _pluginXmlConfig = [HCPXmlConfig loadFromCordovaConfigXml];
    
    // load plugin internal preferences
    _pluginInternalPrefs = [HCPPluginInternalPreferences loadFromUserDefaults];
    if (_pluginInternalPrefs == nil || _pluginInternalPrefs.currentReleaseVersionName.length == 0) {
        _pluginInternalPrefs = [HCPPluginInternalPreferences defaultConfig];
        [_pluginInternalPrefs saveToUserDefaults];
    }
    
    NSLog(@"Currently running release version %@", _pluginInternalPrefs.currentReleaseVersionName);
    
    //取当前版本号
    NSUserDefaults *user=[NSUserDefaults standardUserDefaults];
    [user setObject:_pluginInternalPrefs.currentReleaseVersionName forKey:@"currentReleaseVersionName"];
    
    // init file structure for www files
    _filesStructure = [[HCPFilesStructure alloc] initWithReleaseVersion:_pluginInternalPrefs.currentReleaseVersionName];
    
    NSLog(@"====%s",__FUNCTION__);
}

www:就是项目的www文件。


2、下载,ionic2项目创建时就自带了AFNetworking,所以用它来下载,文件都是压缩包Zip的形式,代码如下:

 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    //存放文件的路径
    NSString  *fullPath = [NSString stringWithFormat:@"%@/%@.zip", documentsDirectory, projectName];
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://192.168.100.234:8080/www/%@.zip",projectName]];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSessionDownloadTask *task =
    [manager downloadTaskWithRequest:request
                            progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
                                return [NSURL fileURLWithPath:fullPath];
                            }
                   completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
                       
                   }];
    [task resume];


下载路径URL是本地服务器的IP+www+项目名称.zip,项目压缩包是提前放在本地服务器上的。

3、解压并删除Zip

 // 解压
        NSString *zipPath =[NSString stringWithFormat:@"%@/%@.zip",documentsDirectory,projectName];
        NSString *destinationPath = [NSString stringWithFormat:@"%@",documentsDirectory];
        [SSZipArchive unzipFileAtPath:zipPath toDestination:destinationPath];
        
        NSFileManager* fileManager=[NSFileManager defaultManager];
        BOOL blHave=[[NSFileManager defaultManager] fileExistsAtPath:zipPath];
        if (!blHave) {
            NSLog(@"no  have");
            return ;
        }else {
            NSLog(@" have");
            //移除Zip
            BOOL blDele= [fileManager removeItemAtPath:zipPath error:nil];
            if (blDele) {
                NSLog(@"delete success");
            }else {
                NSLog(@"delete fail");
            }
            
        }

解压用网上的SSZipArchive就可以了。

所以,总结全部,下载功能代码如下,

#pragma mark -文件下载
-(void)downloadFileByAFNetworkingWithProject:(NSString*)projectName documentsDirectory:(NSString*)documentsDirectory{
 
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    //存放文件的路径
    NSString  *fullPath = [NSString stringWithFormat:@"%@/%@.zip", documentsDirectory, projectName];
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://192.168.100.241:8080/www/%@.zip",projectName]];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSessionDownloadTask *task =
    [manager downloadTaskWithRequest:request
                            progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
                                return [NSURL fileURLWithPath:fullPath];
                            }
                   completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
                       
                   }];
    [task resume];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 解压
        NSString *zipPath =[NSString stringWithFormat:@"%@/%@.zip",documentsDirectory,projectName];
        NSString *destinationPath = [NSString stringWithFormat:@"%@",documentsDirectory];
        [SSZipArchive unzipFileAtPath:zipPath toDestination:destinationPath];
        
        NSFileManager* fileManager=[NSFileManager defaultManager];
        BOOL blHave=[[NSFileManager defaultManager] fileExistsAtPath:zipPath];
        if (!blHave) {
            NSLog(@"no  have");
            return ;
        }else {
            NSLog(@" have");
            //移除Zip
            BOOL blDele= [fileManager removeItemAtPath:zipPath error:nil];
            if (blDele) {
                NSLog(@"delete success");
            }else {
                NSLog(@"delete fail");
            }
            
        }
    });

}

projectName和documentsDirectory都是在外部调用时传入的,可根据自己的需要调整是否需要传入。

需要注意一点,本人在调用执行的时候,下载速度会慢于解压,导致解压时还没有Zip文件,所以解压用dispatch_after做了一个2s的时间延迟,延迟时间可根据情况做设置。

三、更新

1、修改HCPContentManifest.m

官方说法

/**
 *  Model for content manifest.
 *  Content manifest is a configuration file, that holds the list of all web project files with they hashes.内容清单是一个配置文件,包含所有web项目的列表文件哈希表。
 *  Used to determine which files has been removed from the project, which are added or updated.用于确定哪些文件已经从项目中删除,添加或更新。
 */

这个HCPContentManifest就是Web内容清单,找到- (HCPManifestDiff *)calculateDifference:(HCPContentManifest *)comparedManifest方法,修改下面这部分

 // find new files
    for (HCPManifestFile *newFile in comparedManifest.files) {
        BOOL isFound = NO;
        for (HCPManifestFile *oldFile in self.files) {
            if ([newFile.name isEqualToString:oldFile.name]) {
                isFound = YES;
                break;
            }
        }
        if (!isFound) {
            [addedFiles addObject:newFile];
        }
    }

修改后,如下

 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    NSString *newProjName = [ud objectForKey:@"page"];//"proj1","proj2".etc
    NSLog(@"------%s=newProjName=%@",__FUNCTION__,newProjName);
    
    // find new files
    for (HCPManifestFile *newFile in comparedManifest.files) {
        BOOL isFound = NO;
        BOOL isNeed = NO;
        for (HCPManifestFile *oldFile in self.files) {
            if ([newFile.name isEqualToString:oldFile.name]) {
                isFound = YES;
                NSLog(@"++++----- newFile.name ======= %@", newFile.name);
                if (newProjName != nil) {
                    if ([newFile.name hasPrefix: newProjName]) {
                        NSLog(@"+++++++++++ newFile.name必须的 ======= %@", newFile.name);
                        isNeed = YES;
                    }
                }
                break;
            }
        }
        if (!isFound && isNeed) {
            [addedFiles addObject:newFile];
        }
    }

在进入某个子项目时,传值项目名到这,根据传值,进入的子项目就是更新后的。


2、修改CDVViewController.m

因为是从iOS原生进入ionic,用了如下方法:

 NSString *file=[NSString stringWithFormat:@"%@/index.html",_currentItems[indexPath.item]];
    NSLog(@"--------file==%@",file);
    [user setObject:file forKey:@"page"];
    [user synchronize];
    MainViewController *vc = [[MainViewController alloc] initWithFolderName:@"www" StartPage:file];
    [self.navigationController pushViewController:vc animated:true];

MainViewController是继承于CDVViewController,iOS原生进入ionic是需要通过cordova,所以修改如下

- (void)loadSettings
{
    CDVConfigParser* delegate = [[CDVConfigParser alloc] init];

    [self parseSettingsWithParser:delegate];

    // Get the plugin dictionary, whitelist and settings from the delegate.
    self.pluginsMap = delegate.pluginsDict;
    self.startupPluginNames = delegate.startupPluginNames;
    self.settings = delegate.settings;

    // And the start folder/page.
    if(self.wwwFolderName == nil){
        self.wwwFolderName = @"www";
    }
//    if(delegate.startPage && self.startPage == nil){
        //self.startPage = delegate.startPage;
//    }
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    self.startPage = [ud objectForKey:@"page"];
        NSLog(@"-------cdv-startPage==%@",self.startPage);

    // Initialize the plugin objects dict.
    self.pluginObjects = [[NSMutableDictionary alloc] initWithCapacity:20];
}

代理这部分注释掉,是因为会默认执行代理的,即Staging文件里面的config.xml的<content src="index.html" />,所以把self.startPage改成灵活的,可根据自己的选择进入想要进入的页面。


3、修改HCPPlugin.m

把indexPageFromConfigXml方法里面的“_indexPage = DEFAULT_STARTING_PAGE;”改成如下:

 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
        NSString *startUrl=[ud objectForKey:@"page"];
        _indexPage = startUrl;//DEFAULT_STARTING_PAGE;

做这个操作是因为:在更新后,第一次进入页面会进入默认的www/index.html,退出后,再进入,才是更新后的,为了解决这个问题而做的修改。

4、更新步骤

  前提:带有子项目的主项目在运行中。

  子项目更新:

       ①、在子项目中需要的地方做修改;

       ②、ionic serve;

       ③、cordova-hcp build;

       ④、ionic build iOS;

       ⑤、将子项目platforms--iOS中的www复制,放在主项目外部的www文件;

       ⑥、主项目cordova-hcp build;

       ⑦、将主项目外部的www文件复制,放到服务器 /usr/local/apache-tomcat-7.0.32/webapps,覆盖原来的www文件,如有其它需要下载的子项目,则放入新www中。


  主项目更新:

      ①、在主项目中需要的地方做修改;

      ②、ionic serve;

      ③、cordova-hcp build;

      ⑦、将主项目外部的www文件复制,放到服务器 /usr/local/apache-tomcat-7.0.32/webapps,覆盖原来的www文件。


以上就是主要功能,还有许多细节需要自己完善,里面很多操作是灵活,根据自己的需要用合适的方法就可以了,比如传值方式。

这里iOS和Android很多方法都是通用的。


(网不好,没上传图片。如果有更好的建议和方法可以留言啊!!)