这两天跟同事学习IOS的更新文件的需求,他们的项目是html5打包封装的游戏,里面的文件有大量图片,音频素材,html,js文件。
现在要处理版本更新这一块。
初步需求设计预想是通过版本更新检测,请求服务器下载zip文件到沙箱进行解压处理。
示意如下:
版本号对比———-> 请求zip更新包————->写入沙箱NSDocumentDirectory—>解压覆盖旧文件 ——>清理更新包和旧的资源文件
1.准备的工作
1 .AFNetworking 2.5.4版本
2.ZipArchive
准备两个类库,AFNetworking负责下载zip文件,ZipArchive负责解压Zip文件。准备好就配置一下 其中ZipArchive 的版本调整过 配置的过程如下
Add Main.h and Main.m to your project.
Add the minizip folder to your project.
Add the libz library to your target
简单直接办法把你下载文件拖到你的工程目录去,注意黄色文件夹和蓝色文件夹不一样。
添加libz进去,引用的时候import “Main.h” (其实Main命名让人纠结,暂时凑合够用就好了)
准备工作完成后可以写一个测试用例
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController <UIAlertViewDelegate>
@end
#import "ViewController.h"
#import "AFNetworking.h"
#import "UIProgressView+AFNetworking.h"
#import "Main.h"
@interface ViewController ()
@property (nonatomic,strong) UIProgressView *progressView;
@property (nonatomic,strong) UILabel *progressLabel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CGRect frame = CGRectMake(10, 100, self.view.frame.size.width-20, 40);
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = frame;
[button setTitle:@"更新" forState: UIControlStateNormal];
button.backgroundColor = [UIColor greenColor];
[button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
self.progressView = [[UIProgressView alloc]initWithFrame:CGRectMake(10, 200, self.view.frame.size.width-20, 2)];
[self.view addSubview:self.progressView];
self.progressLabel =[[UILabel alloc] initWithFrame:CGRectMake(10, 160, 200, 20)];
[self.view addSubview:self.progressLabel];
}
-(void) buttonClicked:(UIButton *)button
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"版本更新" message:@"版本发现有更新内容需要更新吗?" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
[alert show];
}
-(void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(buttonIndex == 1)
{
[self downloadFile:@"testZipTime.zip"
url:@"http://xxxxxxxxx/app/testZipTime.zip"];//换成你的地址路径
}
}
-(void) downloadFile:(NSString *)fileName url:(NSString *) filePath
{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc]initWithSessionConfiguration:configuration];
NSURL *url = [NSURL URLWithString:filePath];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionDownloadTask *downloadTask =[manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSURL *docmentDirectoryPath = [NSURL fileURLWithPath:path];
return [docmentDirectoryPath URLByAppendingPathComponent:fileName ];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
NSLog(@"@dowload to %@",filePath);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSString *zipPath = [path stringByAppendingPathComponent:fileName];
BOOL isOK = [Main unzipFileAtPath:zipPath toDestination:path];
if(isOK)
{
NSLog(@"更新好了");
[self deleteFileByName:fileName];
dispatch_async(dispatch_get_main_queue(), ^{
self.progressLabel.text = @"更新成功了";
});
}
}];
[manager setDownloadTaskDidWriteDataBlock:^(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
NSLog(@"进度数据:%f",(float)totalBytesWritten/(float)totalBytesExpectedToWrite);
float precess = round(((float)totalBytesWritten/(float)totalBytesExpectedToWrite ) *100);
NSNumber *num = [[NSNumber alloc]initWithFloat:precess];
dispatch_async(dispatch_get_main_queue(), ^{
NSString *newStr = [NSString stringWithFormat:@"更新进度中...%@%%",[num stringValue] ];
self.progressLabel.text = newStr;
});
}];
[downloadTask resume];
[self.progressView setProgressWithDownloadProgressOfTask:downloadTask animated:YES];
}
-(void) deleteFileByName:(NSString *) fileName
{
NSFileManager *manager = [NSFileManager defaultManager];
NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [docPaths objectAtIndex:0];
path = [path stringByAppendingPathComponent:fileName];
NSError *error = nil;
if ([manager removeItemAtPath:path error:&error])
{
NSLog(@"删除文件成功了");
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
运行后效果图
因为经常打开沙箱文件位置查看数据包是否加装完。安装
ZLGotoSandboxPlugin 插件就能快速定位到相应的沙箱去
2.下载进度条显示
下载的进度条采用了AFNetworking 提供的便利方法
原本使用kvo 配合NSProgress 也可以显示进行的百分比,但是在测试过程发现奇怪现象数据没有触发到,于是改用另外一个方法AFURLSessionManager的setDownloadTaskDidWriteDataBlock 方法,这样百分进度就可以显示。
setDownloadTaskDidWriteDataBlock
3.更新UI显示
采用GCD 的方法在主线程对UI进行刷新
dispatch_async(dispatch_get_main_queue(), ^{
self.progressLabel.text = @"更新成功了";
});
4.压缩zip文件
ZipArchive 给我们提供了一个Main的方法处理压缩包,目前看来仅仅只是需要用一个简单的功能,凑合使用。这个方法需要填写两个目录路径。一个是文件路径,一个是解压的位置。这时候这个解压的反馈过程就弄好了。
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSString *zipPath = [path stringByAppendingPathComponent:fileName];
BOOL isOK = [Main unzipFileAtPath:zipPath toDestination:path];
5.删除文件
解压成功后需要对原有的包进行删除,使用 NSFileManager为我们提供的方法进行删除操作。
-(void) deleteFileByName:(NSString *) fileName
{
NSFileManager *manager = [NSFileManager defaultManager];
NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [docPaths objectAtIndex:0];
path = [path stringByAppendingPathComponent:fileName];
NSError *error = nil;
if ([manager removeItemAtPath:path error:&error])
{
NSLog(@"删除文件成功了");
}
}
6.遇到问题
主要是对AFNetworking 框架不熟悉,使用起来也 发生被动情况。没有考虑多线程的情况,之前使用NSURLConnection的情况也出现类似的问题。
更新操作还没有考虑容量判断,防止手机容量不足更新失败情况
没有考虑断线续传的情况。
升级后批量清理旧的文件目录
下一步尝试思考这些问题。