`

实战iOS7之后台多任务

阅读更多
在WWDC2013中 水果公司把iOS7的中后台多任务单独开出来一个Session讲述(Session 204 What’s New with Multitasking),其对后台多任务的改动如下:
  • 1. 改变了后台任务的运行方式(Background Task)
  • 2. 增加了后台获取(Background Fetch)
  • 3. 增加了推送唤醒(静默推送,Silent Remote Notifications)
  • 4. 增加了后台传输(NSURLSession的BackgroundSession)

开发者如果可以更好的利用这些特性,可以很大的提升自己的应用体验,比方说,使用后台获取特性,在你打开微博之前,最新的微博已经自动刷出来,不需要用户再手动刷一次。

知识点



用法
后台任务
后台任务是任务级别的配置,它的作用是当应用切换到后台后仍能保持一段运行时间以完成一些耗时的任务,通常这个时间比较短,不适宜做耗时大的操作,用法

1.启动后台任务
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
	...
    }];
2.结束后台任务
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUpdateTask];


后台获取
后台获取是应用级配置,其可以帮助应用在合适的时候触发一次后台获取,我们只能配置最快什么时候做一次获取,但是具体什么时候做,这得看设备的心情,不过水果公司说,其会根据用户习惯做后台获取,比方说你每天中午12:00使用这个应用,那么它就会在11:50给你做一次后台获取,是不是这么神,有待检验。用法:

1.在info.list中为应用配置后台获取
2.在系统启动配置后台获取的(最快)频率
3.在AppDelegate中实现方法-application:performFetchWithCompletionHandler: 执行后台获取代码

远程唤醒通知
在iOS7中,对远程通知进行了改造,新的远程通知可以附带上一些参数,这样可以减少一些不必要的网络请求,提升性能.它使设备可以接收远端推送后让系统唤醒设备和我们的后台应用,并先执行一段代码来准备数据和UI,然后再提示用户有推送。这时用户如果解锁设备进入应用后将不会再有任何加载过程,新的内容将直接得到呈现。注意,因为,每一次远程通知都会唤醒设备,为了保证设备续航,水果公司对发送通知的频率是有限制的,尽量少用。用法

1.为应用配置使用远程推送
2.使用最新格式的notification
{
    "aps" : {
        "content-available" : 1
    },
    "content-id" : 42
}
3.AppDelegate中配置delegate方法处理notification:
-(void)application:(UIApplication *)application 
  didReceiveRemoteNotification:(NSDictionary *)userInfo 
        fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler

NSURLSession之BackgroundSession
这个主要就是用来处理大数据量的下载的,其保证应用即使在后台也不影响数据的上传和下载。用法:
1.创建NSURLSession的backgrounSession,并对其进行配置(参见上一篇文章)
2.使用该Session启动一个数据传输任务。
3.在AppDelegate中实现方法告诉应用:
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier
  completionHandler:(void (^)())completionHandler

4.在具体的ViewController中实现NSURLSessionTask的Delegate方法,根据任务完成或出错的情况对UI进行更新:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
                              didFinishDownloadingToURL:(NSURL *)location;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                           didCompleteWithError:(NSError *)error;

一旦后台传输的状态发生变化(包括正常结束和失败)的时候,应用将被唤醒并运行appDelegate中的回调,接下来NSURLSessionTask的Delegate方法将在后台被调用

实战
这儿我实现了一个后台获取的Demo,我使用后台获取去获取一张图片,显示在页面上。下面是演示效果:



实现代码:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //设定BackgroundFetch的频率
    [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
…
}

#pragma mark - backgroundFetch delegates
-(void) application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    //当后台获取被触发时,或调用下面的代码,这儿我直接去调用MainViewController的方法去刷新UI。
    UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
    MainViewController *mainViewController  = (MainViewController *)navigationController.topViewController;
    [mainViewController fetchImageWithCompletionHandler:completionHandler];
    //修改Icon的bageNumber提醒后台又刷新,强迫症者慎用
    [UIApplication sharedApplication].applicationIconBadgeNumber +=1;
}


MainViewController.m
-(void)fetchImageWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    //调用子UIViewController的方法刷新UI
    NSLog(@"main controller called fetch");
    [self.backgroundFetchViewController initImageViewWithCompletionHandler:completionHandler];
}

BackgroundFetchViewController.m
#import "BackgroundFetchViewController.h"

@interface BackgroundFetchViewController ()

@end

@implementation BackgroundFetchViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

-(NSURL *)createDirectoryForDownloadItemFromURL:(NSURL *)location
{
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *urls = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
    NSURL *documentsDirectory = urls[0];
    return [documentsDirectory URLByAppendingPathComponent:[location lastPathComponent]];
}

//把文件拷贝到指定路径
-(BOOL) copyTempFileAtURL:(NSURL *)location toDestination:(NSURL *)destination
{
    
    NSError *error;
    NSFileManager *fileManager = [NSFileManager defaultManager];
    [fileManager removeItemAtURL:destination error:NULL];
    [fileManager copyItemAtURL:location toURL:destination error:&error];
    if (error == nil) {
        return true;
    }else{
        NSLog(@"%@",error);
        return false;
    }
}

- (void)showImageWithURL:(NSURL *)imageURL
{
    UIImage *image = [UIImage imageWithContentsOfFile:[imageURL path]];
    self.imageView.image = image;
    self.imageView.contentMode = UIViewContentModeScaleAspectFit;
    self.imageView.hidden = NO;
}

-(void) initImageViewWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    NSLog(@"Background Fetch View Controller called");
    NSURLSessionDownloadTask *task = [[self session] downloadTaskWithRequest:[self request] completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
        if (!error) {
//            获取新的数据,刷新UI
            NSURL *imageURL = [self createDirectoryForDownloadItemFromURL:location];
            NSLog(@"Fetch image successful, %@",[imageURL path]);
            [self copyTempFileAtURL:location toDestination:imageURL];
            [self showImageWithURL:imageURL];
//            调用compeletionHandler, 系统会刷新应用在App Switcher中的UI
            completionHandler(UIBackgroundFetchResultNewData);
        }else{
            NSLog(@"Fetch data failed");
            completionHandler(UIBackgroundFetchResultFailed);
        }
    }];
    [task resume];
    
}


- (NSURLSession *)session
{
    //创建NSURLSession
    NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession  *session = [NSURLSession sessionWithConfiguration:sessionConfig];
    return session;
}

- (NSURLRequest *)request
{
    //创建请求
    NSURL *url = [NSURL URLWithString:@"http://upload.wikimedia.org/wikipedia/commons/e/e0/Steve_Jobs_with_the_Apple_iPad_no_logo_(cropped).jpg"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    return

所有代码在这儿:https://github.com/xianlinbox/iOS7_New
参考文章:
http://www.shinobicontrols.com/blog/posts/2013/09/24/ios7-day-by-day-day-3-background-fetch
http://www.objc.io/issue-5/multitasking.html
http://onevcat.com/2013/08/ios7-background-multitask/
  • 大小: 177.5 KB
  • 大小: 7.2 MB
3
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics