以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很多方法都是通用的。
(网不好,没上传图片。如果有更好的建议和方法可以留言啊!!)