12.2.2. 方案
使用UIApplication的beginBackgroundTaskWithE
12.2.3. 讨论
当一个iOS应用被送到后台,它的主线程会被暂停。你用NSThread的detachNewThreadSelector:toTar get:withObject:类方法创建的线程也被挂起了。如果你想在后台完成一个长期任务,就必须调用UIApplication的beginBackgroundTaskWithE
DevDiv 翻译:kyelup cloudhsu 耐心摩卡 wangli2003j3 xiebaochun dymx101 jimmylianf BeyondVincent 20DevDiv 校对:laigb kyelup DevDiv 编辑:BeyondVincent 版本 1.0 | 2012 年 07 月 30 日
和其所有被暂停的线程被放入后台。
当你的程序在前台时,UIApplication的backgroundTimeRemaining属性等于DBL_MAX常量,这是double类型可表示的最大值(和这个值相当的integer通常等于-1)。在iOS被要求在程序被完全挂起之前给于更多的执行时间,这个属性指明了在完成任务前程序拥有多少秒。
在程序中你可以多次调用beginBackgroundTaskWithE
在后台时,程序不应该执行完全的功能,也不应该处理大量数据。事实上,他们只应该完成一个长期任务。
比如,一个程序正在调用一个web service API,并且还没有从服务器上的那个API接收到响应。在此期间,如果程序被送入后台,它可以请求更多的时间,直到它从服务器收到响应。一旦响应被接收,程序必须保存其状态,并调用UIApplication的endBackgroundTask:实例方法将任务标记为完成。
让我们看一个例子。我将从在应用程序委托中定义一个UIBackgroundTaskIdentifi
#import <UIKit/UIKit.h>
@interface Completing_a_Long_Running_Task_in_the_BackgroundAppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, unsafe_unretained) UIBackgroundTaskIdentifi
@property (nonatomic, strong) NSTimer *myTimer;
@end
接下来我们继续同步属性:
#import "Completing_a_Long_Running_Task_in_the_BackgroundAppDelegate.h" @implementation Completing_a_Long_Running_Task_in_the_BackgroundAppDelegate
@synthesize window = _window;
@synthesize backgroundTaskIdentifier
现在,让我们创建定时器,并在程序被送到后台时启动它:
- (BOOL) isMultitaskingSupported{
BOOL result = NO;
if ([[UIDevice currentDevice]
respondsToSelector:@selector(isMultitaskingSupported)]){ result = [[UIDevice currentDevice] isMultitaskingSupported];
}
return result;
}
- (void) timerMethod:(NSTimer *)paramSender{
NSTimeInterval backgroundTimeRemaining =
[[UIApplication sharedApplication] backgroundTimeRemaining];
if (backgroundTimeRemaining == DBL_MAX){ NSLog(@"Background Time Remaining = Undetermined");
} else { iOS 5 Programming Cookbook www.devdiv.com 翻译整理
DevDiv 翻译:kyelup cloudhsu 耐心摩卡 wangli2003j3 xiebaochun dymx101 jimmylianf BeyondVincent 21DevDiv 校对:laigb kyelup DevDiv 编辑:BeyondVincent 版本 1.0 | 2012 年 07 月 30 日
NSLog(@"Background Time Remaining = %.02f Seconds",
backgroundTimeRemaining);
} }
- (void)applicationDidEnterBackg
if ([self isMultitaskingSupported] == NO){
return; }
self.myTimer =
[NSTimer scheduledTimerWithTimeIn
target:self
selector:@selector(timerMethod:) userInfo:nil
repeats:YES];
self.backgroundTaskIdentifier
[application beginBackgroundTaskWithE
}]; }
你可以看到,在后台任务的完成处理者(completion handler)中,我们调用了应用程序委托的endBackgroundTask方法。这是一个我们编写的方法,如下:
- (void) endBackgroundTask{
dispatch_queue_t mainQueue = dispatch_get_main_queue();
__weak Completing_a_Long_Running_Task_in_the_BackgroundAppDelegate *weakSelf = self;
dispatch_async(mainQueue, ^(void) {
Completing_a_Long_Running_Task_in_the_BackgroundAppDelegate *strongSelf = weakSelf;
if (strongSelf != nil){
[strongSelf.myTimer invalidate];
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier
strongSelf.backgroundTaskIdentifier
}); }
在长期任务结束后,我们需要做一些事情进行清理:
1. 结束所有的线程和定时器,不管他们是基础定时器还是GCD中创建的。
2.调用UIApplication的endBackgroundTask:方法来结束后台任务。
3.将任务标识设置为UIBackgroundTaskInvalid,标志我们的任务结束。
最后,当我们的应用回到前台,如果我们的后台任务还在执行中,我们需要确保我们在干掉它:
- (void)applicationWillEnterFore
if (self.backgroundTaskIdentifier
[self endBackgroundTask]; }
}
在我们的例子中,不论何时程序被送到后台,我们都会要求更多时间以完成一个长期任务(例如,在这里是我们计时器的代码)。在我们的时间里,我们不断的读取UIApplication实例中backgroundTimeRemaining属性的值,将它打印到控制台。在UIApplication的beginBackgroundTask WithExpirationHandler: 实例方法中,在程序的额外时间内完成一个长期任务之前,我们提供的代码将被执行(一版大概在任务过期前5到10秒)。在此,我们只要调用UIApplication的endBackgroundTask:实例方法来结束任务。