Hybrid App(混合模式移動應(yīng)用)是指介于web-app、native-app這兩者之間的app耘柱,兼具“Native App良好用戶交互體驗(yàn)的優(yōu)勢”和“Web App跨平臺開發(fā)的優(yōu)勢”虚循。
1.首次打開App
第一次打開App同欠,自然是先解壓Hybrid Zip啦。通過‘CFBundleShortVersionString.CFBundleVersion’生成的版本標(biāo)識符來判斷是否需要重新解壓Zip包横缔,主要針對的是app通過更新上來需要解壓新安裝包中的Zip包铺遂。
// 解壓Hybrid Zip包
- (void)unzipH5ResourcesFile {
// 解壓代理
[BLNHybridDelegate sharedInstance].zipDelegate = self;
[H5ResourceFileManager sharedInstance].versionDict = @{
@"venue" : @"0",
};
// 版本標(biāo)識符
NSString *key = @"BLN_APP_BUILD_VERSION";
NSString *value = [[NSUserDefaults standardUserDefaults] valueForKey:key];
NSString *versionStr = [NSString stringWithFormat:@"%@.%@",APP_VERSION,APP_BUILD_VERSION];
// AppStore更新App,則刪除本地解壓的Zip包及資源
if (![value isEqualToString:versionStr]) {
[[H5ResourceFileManager sharedInstance] clearH5Resource];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"BLN_HTML_ISUNZIP"];
}
// 解壓Zip
@weakify(self)
[[H5ResourceFileManager sharedInstance] setupHtmlFileWithName:@"venue"
finishblock:^(id obj, NSInteger err) {
// 記錄版本及標(biāo)識符剪廉,并檢查Zip版本更新
@normalize(self)
if (![[NSUserDefaults standardUserDefaults] objectForKey:@"BLN_HTML_ISUNZIP"]) {
[[NSUserDefaults standardUserDefaults] setObject:@"0" forKey:@"BLN_HTML_VERSION"];
[[NSUserDefaults standardUserDefaults] setObject:@1 forKey:@"BLN_HTML_ISUNZIP"];
[[NSUserDefaults standardUserDefaults] setObject:versionStr forKey:key];
[self checkH5ResourcesFile];
}
else
[self checkH5ResourcesFile];
}];
}
#pragma mark - SSZipArchiveDelegate
// 解壓代理
- (BOOL)filePath:(NSString *)filePath unZipToPath:(NSString *)toPath {
if ([SSZipArchive unzipFileAtPath:filePath toDestination:toPath]) {
return YES;
}
else {
return NO;
}
}
2.檢查版本更新
需要注意的是娃循,為了避免App長時(shí)間停留在后臺而導(dǎo)致無法及時(shí)更新Zip資源包,我們還需要在App后臺進(jìn)入前臺的時(shí)候斗蒋,做一次檢查更新捌斧。
// 后臺進(jìn)入前臺
- (void)applicationWillEnterForeground:(UIApplication *)application {
[[H5ResourceFileManager sharedInstance] checkH5ResourcesFile];
}
#pragma mark – Private Methods
// 檢查更新
- (void)checkH5ResourcesFile {
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"BLN_HTML_ISUNZIP"]) {
[[H5ResourceFileManager sharedInstance] checkH5ResourcesFile];
}
}
3.獲取本地版本號
self.loactionVersion = (NSString *)[[NSUserDefaults standardUserDefaults] objectForKey:@"BLN_HTML_VERSION"];
4.獲取服務(wù)器最新版本
如App版本為2.0.1,則訪問地址為個(gè)eg:‘https://ios.download.com/hybird/app_2_0_1.json’ 泉沾。這樣做的目的是每個(gè)app版本都需要訪問自己所對應(yīng)的版本更新文件纸兔。
/**
拼接json文件請求地址
@return 下載地址
*/
- (NSString *)getH5ResourcesDownLoadURL {
NSArray *numberArry = [APP_VERSION componentsSeparatedByString:@"."];
NSMutableString *localVersion = [[NSMutableString alloc] initWithString:self.baseURL];
for (int i = 0; i < numberArry.count; i++) {
NSString *value = numberArry[i];
[localVersion appendString:[NSString stringWithFormat:@"_%@",value]];
}
[localVersion appendString:@".json"];
return localVersion;
}
5.服務(wù)器JSON內(nèi)容
JSON內(nèi)容如下
{
"lastVersion": "20161201173611",// 最新版本
"md5": "be85803fbb78fa2d4d1a95a6f09a6183",// Zip的MD5校驗(yàn)碼
"url": "https://ios.download.com/hybird/20161201173611.zip",// 最新版Zip下載地址
"data": [
{
"version": "20161123194301",// Zip版本
"url": "https://ios.download.com/hybird/20161123194301-20161201173611.zip",// 下載地址
"md5": "bd9da2ce1a56a59760483d5097bdd76b"http:// Zip的MD5校驗(yàn)碼
},
{
"version": "20161130140816",// Zip版本
"url": "https://ios.download.com/hybird/20161130140816-20161201173611.zip",// 下載地址
"md5": "03d6174f95934ef78c4af3b904096992"http:// Zip的MD5校驗(yàn)碼
}
]
}
6.對比版本號 全量/增量更新
如果本地Zip版本號可以在data數(shù)組中找到突勇,則執(zhí)行執(zhí)行增量更新,如果找不到則做全量更新,全量更新需要刪除本地解壓的資源伴郁。
NSDictionary *dic = [responseObject mj_JSONObject];
if(dic && [dic objectForKey:@"lastVersion"]) {
self.lastVersion = [dic valueForKey:@"lastVersion"];
NSLog(@"%s LastVersion zip verson is %@",__FUNCTION__,self.lastVersion);
// 校驗(yàn)是否是最新版本
if([self.lastVersion longLongValue] > [self.loactionVersion longLongValue]) {
NSArray *data = dic[@"data"];
if (!data) {
_requestLoadTask = nil;
return;
}
// 增量更新
for (NSDictionary *info in data) {
if ([info[@"version"] isEqualToString:self.loactionVersion]) {
self.lastHashString = info[@"md5"];
[self downloadH5ResourcesZipWithURL:info[@"url"] clearHTMLResource:NO fractionCompleted:^(double count) {
}];
return;
}
}
// 全量更新
self.lastHashString = dic[@"md5"];
[self downloadH5ResourcesZipWithURL:dic[@"url"] clearHTMLResource:YES fractionCompleted:^(double count) {
}];
}
else {
_requestLoadTask = nil;
NSLog(@"%s This zip is lastVersion",__FUNCTION__);
}
}
else {
_requestLoadTask = nil;
NSLog(@"%s No json file",__FUNCTION__);
}
7.下載Hybrid Zip
下載過程中增加SVProgressHUD顯示下進(jìn)度條。
MJWeakSelf
_downLoadTask = [[BFHTTPManager sharedInstance] downloadTaskWithRequest:request
progress:^(NSProgress * _Nonnull downloadProgress) {
// 進(jìn)度條
dispatch_async(dispatch_get_main_queue(), ^{
if (downloadProgress)
{
[SVProgressHUD setDefaultMaskType:SVProgressHUDMaskTypeGradient];
[SVProgressHUD showProgress:downloadProgress.fractionCompleted status:@"更新中..."];
}
});
}
destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
// 存放路徑
NSString *cachesPath = [LKFilePath cachesPath];
NSString *finalPath = [cachesPath stringByAppendingString:@"/Resources.zip"];
return [NSURL fileURLWithPath:finalPath];
}
completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
[SVProgressHUD setDefaultMaskType:SVProgressHUDMaskTypeNone];
[SVProgressHUD dismiss];
// 錯誤處理
···
// 下載完成
···
});
}];
[_downLoadTask resume];
8.安全校驗(yàn)(MD5值校驗(yàn))
為了防止Zip包被攔截篡改兵怯,對下載到本地的Zip進(jìn)行MD5值的校驗(yàn)敷存。
//目前文件所在地址
NSString *zipFilePath = [filePath path];// 將NSURL轉(zhuǎn)成NSString
YYFileHash *fileHashSting = [YYFileHash hashForFile:zipFilePath types:YYFileHashTypeMD5];
BOOL same = ([weakSelf.lastHashString compare:fileHashSting.md5String options:NSCaseInsensitiveSearch | NSNumericSearch] == NSOrderedSame);
if (!same) {
dispatch_async(dispatch_get_main_queue(), ^{
[BFCustomHUD showInfoWithStatus:@"文件MD5值校驗(yàn)失敗"];
});
return;
}
9.解壓
全量更新
如果是全量更新,則刪除原先解壓的資源柴我。
// 刪除h5資源
[[H5ResourceFileManager sharedInstance] clearH5Resource];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"BLN_HTML_ISUNZIP"];
解壓
// 目標(biāo)文件夾地址
NSString *destnation = [[LKFilePath documentPath] stringByAppendingFormat:@"/html5/%@",self.htmlVersion];
if ([LKFilePath touchDirectory:destnation]) {
dispatch_async(dispatch_get_main_queue(), ^{
// 更新界面
[SVProgressHUD showWithStatus:@"解壓中..."];
});
bool unzipSuccess = [SSZipArchive unzipFileAtPath:zipFilePath toDestination:destnation];
if (unzipSuccess) {
weakSelf.loactionVersion = weakSelf.lastVersion;
dispatch_async(dispatch_get_main_queue(), ^{
// 更新界面
[BFCustomHUD showSuccessWithStatus:@"更新成功"];
// 如果有H5頁面 返回首頁
for (UIViewController *vc in [LKGlobalNavigationController sharedInstance].viewControllers) {
if ([vc isKindOfClass:[BLNHybridViewController class]]) {
[[LKGlobalNavigationController sharedInstance] popToRootViewControllerAnimated:NO];
return ;
}
}
});
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *toPath = [[LKFilePath documentPath] stringByAppendingFormat:@"/html5/%@",weakSelf.lastVersion];
// prePath 為原路徑解寝,cenPath 為目標(biāo)路徑
if([fileManager moveItemAtPath:destnation toPath:toPath error:nil] != YES) {
NSLog(@"移動文件失敗");
[BFCustomHUD showInfoWithStatus:@"升級失敗"];
return;
}
else {
NSLog(@"移動文件成功");
}
}
}
10.更新版本版本號等標(biāo)識符
[[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithFormat:@"%@",weakSelf.lastVersion] forKey:@"BLN_HTML_VERSION"];
[[NSUserDefaults standardUserDefaults] setObject:@1 forKey:@"BLN_HTML_ISUNZIP"];
[BLNReadAndSavePlist savePlistContent:weakSelf.lastVersion
withContentKey:@"venue_H5Version"
withPath:ph_updateResourcePlistName];
//初始化模塊的最后更新時(shí)間
[BLNReadAndSavePlist savePlistContent:[NSDate date]
withContentKey:@"venue_H5LastUpdateTime"
withPath:ph_updateResourcePlistName];