后台运行
当应用程序进入后台时,系统会自动回调应用程序委托的applicationDidEnterBackground:方法。
应用可以在该方法中完成转入后台前需要做的准备工作,所有的应用需要做以下事情。
释放所有可以释放的内存。
保存用户数据或状态信息,所有没写入磁盘的文件或信息,在进入后台之前,都应该写入磁盘,因为程序可能在后台被杀死。
进入后台时释放内存
当程序进入后台之后,为了确保获得最佳的用户体验,建议释放那些占用内存较大且可以重新获取的资源----
这是因为当应用处于后台时,iOS系统会优先终止那些占用内存大的应用。如果应用尽可能释放其所占用的内存,
那么应用就可以在后台存或更久。从这个角度来看,可以得到一个结论:应用暂停时所占用的内存减少,iOS彻底终止该应用的风险就越低。
如果应用没有启用ARC机制,程序需要在应用进入后台时,将那些需要释放的资源的引用计数变为0,从而让系统回收这些资源。
当应用转入前台时,系统需要重新恢复这些资源。
如果应用启用了ARC机制,程序只要在应用进入后台时,将应用那些需要释放的资源的变量赋为nil即可。当应用转入前台时,
系统需要重新恢复这些资源。
代 码 片 段 |
// 使用默认的通知中心监听应用转入前台的过程 // 应用转入前台时会向通知中心发送UIApplicationWillEnterForegroundNotification // 从而激发enterFore:方法 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector:@selector(enterBack:) name:UIApplicationWillEnterForegroundNotification object:[UIApplication sharedApplication]]; // 使用默认的通知中心监听应用转入前台的过程 // 应用转入前台时会向通知中心发送UIApplicationDidEnterBackgroundNotification // 从而激发enterFore:方法 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector:@selector(enterBack:) name:UIApplicationDidEnterBackgroundNotification object:[UIApplication sharedApplication]]; |
说明 |
上面程序控制当应用转入前台时,该视图控制器的enterFore:方法被调用;当应用转入后台时,该控制器的enterBack:方法被调用,下面是enterBack:方法的代码 |
|
- (void) enterBack:(NSNotification *)notification { NSLog(@”—enterBack---”); // 转入后台时将可以迅速重建,而且占用内存较大的对象设为nil,以便系统释放内存 bgLayer1.contents = nil; bgLayer2.contents = nil; ePlaneImage1 = nil; } |
说明 |
由于该应用已经启用了ARC机制,因此上面方法只要将这些图片、音乐资源的变量设为nil,系统就可以回收这些图片、音乐资源所占用的内存。 当应用再次转入前台时,该控制器的enterFore:方法被调用,该方法将负责再次加载这些图片、音乐资源。下面是enterFore:方法的代码。 |
代码片段 |
- (void) enterFore:(NSNotification *)notification { NSLog(@”===enterFore===”); bgLayer1.contents = (id)[bgImage CGImage]; bgLayer2.contents = (id)[bgImage CGImage]; ePlaneImage1 = [UIImage imageNamed:@”e1”]; } |
说明 |
通过上面的处理方法,程序可以在应用转入后台时释放大部分内存,使得该应用在后台以少量内存运行,从而降低该应用被iOS系统终止的风险。当该应用转入前台时,系统将会再次初始化 这些资源,从而保证iOS应用可以迅速恢复。 |
进入后台时保存状态
当应用进入后台时,如果程序有一些状态数据没有保存,而iOS系统可能在内存紧张时终止该应用,那么就可能导致该应用丢失这些状态数据。
为了让应用不会丢失状态数据,程序可以在应用转入后台时记录应用状态,应用转入后台时将会调用视图控制器的enterBack:方法,因此在该方法后面增加如下代码:
// 使用NSUserDefaults存储系统积分
[[NSUserDefaults standardUserDefaults] setInteger:score forkey:@”score”];
上面的代码只是简单地使用了NSUserDefault来保存程序状态。如果程序需要保存的状态数据较多,也可采用plist属性文件或者其他形式来保存程序状态。
接下来同样可以在应用转入前台时恢复程序状态,应用转入前台时将会调用视图控制器的enterFore:方法,因此在该方法后面增加如下代码:
// 使用NSUserDefaults读取系统已经保存的积分
NSNumber* scoreNumber;
if((scoreNumber = [[NSUserDefault standardUserDefaults]
objectForKey:@”score”]))
{
score = scoreNumber.integerValue;
}
请求更多的后台时间
当应用转入后台后,不要在主线程中执行超过5秒的任务,如果应用进入后台花费了太多时间(即applicationDidEnterBackground:方法的执行体花费太多时间),应用可能从内存中被删除.
假如应用程序正在执行文件下载或文件传输等,当应用进入后台时,如果该任务还没有执行完成,应用转入后台该任务就会被暂停.千万不要强制在applicationDidEnterBackground:方法中直接完成该任务----因为这会导致应用进入后台花费太多时间,iOS系统可能直接从内存中删除该应用.正确的做法是: 以applicationDidEnterBackground:方法为平台,告诉系统进入后台还是更多的任务需要完成,从而向系统申请更多的后台时间.在这种方式下,当我们的应用处于后台时,即使用户正在使用其他应用,只要系统还是足够的内存,我们的应用就可以保存在内存中,iOS系统会保留应用运行一段时间。
为了请求更多的后台时间,按如下步骤执行: |
1.调用UIApplication对象的beginBackgroundTaskWithExpirationHandler:方法请求获取更多的后台执行时间,该方法默认请求额外获得10分钟后台时间。该方法需要传入一个代码块作为参数,如果请求获取后台执行时间失败,将会执行该代码块。该变量可作为后台任务的标识符。 |
2.调用dispatch_async()方法将指定代码块提交给后台执行. |
3.后台任务执行完成时,调用UIApplication对象的endBackgroundTask:方法结束后台任务 |
例如如下示例应用,该应用在系统转入后台时请求了后台执行时间,然后启动一个代码块,该代码块循环100次,模拟执行一个耗时的下载任务.从该程序转入后台的执行过程可以看出,通过这种机制即可让iOS应用在后台执行更长时间. |
下面是该应用的视图控制器类的实现部分代码 |
ViewController.m @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 使用默认的通知中心监听应用转入后台的过程 // 应用转入后台时会向通知中心发送UIApplicationDidEnterBackgroundNotification // 从而激发enterBack:方法 [[NSNotificationCenter defaultCenter] addObserve:self selector:@selector(enterBack:) name:UIApplicationDidEnterBackgroundNotification object:[UIApplication sharedApplication]]; } - (void)enterBack:(NSNotification *)notification { UIApplication *app = [UIApplication sharedApplication]; // 定义一个UIBackgroundTaskIdentifier类型(本质就是NSUInteger)的变量 // 该变量将作为后台任务的标识符 __block UIBackgroundTaskIdentifier backTaskId; backTaskId = [app beginBackgroundTaskWithExpirationHandler:^ { NSLog(@”===在额外申请的10分钟内依然没有完成任务===”); // 结束后台任务 [app endBackgroundTask:backTaskId]; }]; if(backTaskId == UIBackgroundTaskInvalid) { NSLog(@”===iOS版本不支持后台运行,后台任务启动失败===”); return; } // 将代码块以异步方式提交给系统的全局并发队列 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) , ^{ NSLog(@”===额外申请的后台任务时间为: %f===” , app.backgroundTimeRemaining); // 其他内存清理的代码也可以在此处完成 for(int i = 0 ; i < 100 ; i++) { NSLog(@”下载任务完成了%d%%” , i);// 转换成百分比 // 暂停10秒模拟正在执行后台下载 [NSThread sleepForTimeInterval:10]; } NSLog(@”===剩余的后台任务时间为: %f===” , app.backgroundTimeRemaining); // 结束后台任务 [app endBackgroundTask:backTaskId]; }); } @end |