您的位置:首页 > 移动开发 > IOS开发

iOS7后台数据下载教程

2013-12-26 16:30 567 查看
原文:iOS 7 SDK: Background Transfer Service

这个教程将会教会你如何在后台传输数据,如何使用iOS7提供的多任务API。我将会教会你如何在后台下载一个文件,并且在文件下载完成时弹出一个本地的提醒。   后台数据传输起源于iOS6,允许在前台或者后台下载数据,但是时间是受限制的。最大的问题就是时间是受限制的让用户无法上传或下载大文件。这就是为什么苹果在iOS7上要提升框架的原因。 在iOS7中,这个功能有了很大变化,包括:

1.iOS系统管理上传和下载任务。

2.当用户关闭应用程序时后台仍然可以传输数据

3.时间不受限制

4.它可以在任意时间加入队列(前台或者后台)

5.应用程序需要被唤醒来获取验证,错误,或者完成情况

6.应用程序会有一个进度展示视图 后台传输可以应用在几个非常有用的地方:上传照片或者视频,结合后台提取和远程通知,用于保持应用程序在最新时间。

下面是范例工程。

注:原文,作者是去下载了一个PDF,由于下载的苹果那个PDF感觉比较慢,就改成下载我自己服务器上的一个mp3文件了。

1.创建工程 我们需要一个MainViewController来做这里的主要工作:

1)包含一个按钮,一个ProgeressView

像这样:





 

然后,我们需要在工程头文件里面添加我们需要的成员。

完成之后,我们的MainViewController.h会像这样

 

#import <AVFoundation/AVFoundation.h>

@interface MainViewController : UIViewController<NSURLSessionDelegate,NSURLSessionTaskDelegate,NSURLSessionDownloadDelegate,UIDocumentInteractionControllerDelegate>
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;

@property (nonatomic)NSURLSession *session;
@property (nonatomic)NSURLSessionDownloadTask *downloadTask;
@property (strong,nonatomic)UIDocumentInteractionController *documentInteractionController;
@property (nonatomic,strong)AVPlayer *player;

- (IBAction)startDownload:(id)sender;

下一步我们需要使用NSURLSession来完成任务。

我们要在viewDidLoad中初始化我们需要的session成员

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.

self.session = [self backgroundSession];
self.progressView.progress = 0;
self.progressView.hidden = YES;

}

 

然后我们要完成一个单一的session,使用dispatch_once来确保它会是同一个对象,里面有个要点,请仔细看注释

 

- (NSURLSession *)backgroundSession {
static NSURLSession *session = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{

//这个sessionConfiguration 很重要, com.zyprosoft.xxx  这里,这个com.company.这个一定要和 bundle identifier 里面的一致,否则ApplicationDelegate 不会调用handleEventsForBackgroundURLSession代理方法
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.zyprosoft.backgroundsession"];
session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
});
return session;
}

我们再给开始按钮方法添加开始下载任务的执行

- (IBAction)startDownload:(id)sender {

if (self.downloadTask) {
return;
}
NSURL *downloadURL = [NSURL URLWithString:DownloadURLString];
NSURLRequest *request = [NSURLRequest requestWithURL:downloadURL];
self.downloadTask = [self.session downloadTaskWithRequest:request];
[self.downloadTask resume];
self.progressView.hidden = NO;

}

再接着我们需要来完成最重要的事情,把Session的代理方法完成

首先我们完成 NSURLSessionDownloadTaskDelegate

//这个方法用来跟踪下载数据并且根据进度刷新ProgressView
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
if (downloadTask == self.downloadTask) {
double progress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;
NSLog(@"下载任务: %@ 进度: %lf", downloadTask, progress);
dispatch_async(dispatch_get_main_queue(), ^{
self.progressView.progress = progress;
});
}
}

//下载任务完成,这个方法在下载完成时触发,它包含了已经完成下载任务得 Session Task,Download Task和一个指向临时下载文件得文件路径
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *URLs = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *documentsDirectory = [URLs objectAtIndex:0];
NSURL *originalURL = [[downloadTask originalRequest] URL];
NSURL *destinationURL = [documentsDirectory URLByAppendingPathComponent:[originalURL lastPathComponent]];
NSError *errorCopy;
// For the purposes of testing, remove any esisting file at the destination.
[fileManager removeItemAtURL:destinationURL error:NULL];
BOOL success = [fileManager copyItemAtURL:location toURL:destinationURL error:&errorCopy];
if (success) {
dispatch_async(dispatch_get_main_queue(), ^{
//download finished - open the pdf

//原文下载的苹果的pdf,感觉有点慢,自己又没找到比较大一点的pdf,干脆就放了个mp3歌曲在自己服务器上,所以下载完就播放mp3吧
//            self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:destinationURL];
//            // Configure Document Interaction Controller
//            [self.documentInteractionController setDelegate:self];
//            // Preview PDF
//            [self.documentInteractionController presentPreviewAnimated:YES];
//            self.progressView.hidden = YES;

//播放音乐
self.player = [AVPlayer playerWithURL:destinationURL];
[self.player play];

});
} else {
NSLog(@"复制文件发生错误: %@", [errorCopy localizedDescription]);
}
}

//这个方法我们暂时用不上
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{

}

接着我们需要完成NSURLSessionTaskDelegate需要用到的代理方法

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
if (error == nil) {
NSLog(@"任务: %@ 成功完成", task);
} else {
NSLog(@"任务: %@ 发生错误: %@", task, [error localizedDescription]);
}
double progress = (double)task.countOfBytesReceived / (double)task.countOfBytesExpectedToReceive;
dispatch_async(dispatch_get_main_queue(), ^{
self.progressView.progress = progress;
});
self.downloadTask = nil;
}

最后,我们要完成NSURLSessionDelegate代理方法

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
ZYBackgroundServiceAppDelegate *appDelegate = (ZYBackgroundServiceAppDelegate *)[[UIApplication sharedApplication] delegate];
if (appDelegate.backgroundSessionCompletionHandler) {
void (^completionHandler)() = appDelegate.backgroundSessionCompletionHandler;
appDelegate.backgroundSessionCompletionHandler = nil;
completionHandler();
}
NSLog(@"所有任务已完成!");
}

这里我们用到了AppDelegate里面的一个成员block,用来执行当后台下载完成时调度这个方法。所以我们还要去完成我们的AppDelegate

我们在AppDelegate.h里面添加一个完成时执行的block,添加完应该是这样的

@interface ZYBackgroundServiceAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (copy) void (^backgroundSessionCompletionHandler)();

@end

然后,我们去AppDelegate.m里面完成我们需要弹出本地提醒的方法就大功告成了!

在AppDelegate.m里面,我们需要完成下面几个重要的方法:

添加完应该是这样的

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];

//初始化
MainViewController *mainViewController = [[MainViewController alloc]initWithNibName:@"MainViewController" bundle:Nil];
mainViewController.title = @"后台下载测试Demo";
UINavigationController *rootNav = [[UINavigationController alloc]initWithRootViewController:mainViewController];
self.window.rootViewController = rootNav;

[self.window makeKeyAndVisible];
return YES;
}

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier
completionHandler:(void (^)())completionHandler {
self.backgroundSessionCompletionHandler = completionHandler;
//添加本地通知
[self presentNotification];
}

-(void)presentNotification{
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.alertBody = @"下载完成!";
localNotification.alertAction = @"后台传输下载已完成!";
//提示音
localNotification.soundName = UILocalNotificationDefaultSoundName;
//icon提示加1
localNotification.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber] + 1;
[[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
}

我们最后需要当用户点击进来时,把图标上面的数字提醒设置为0

- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

application.applicationIconBadgeNumber = 0;

}

最终我们运行效果会是这样的:









 

最后的最后是工程的源代码了:

ZYBackgroundService

background transfer,

iOS后台下载,iOS7,
NSURLSession
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: