众所周知,iOS操作系统上的App,不仅仅是外形美,同时其也是以流畅著称,可以说是极端的流畅。在流畅的外表下,是操作系统对于程序的调度、前后台的切换、内存的管理都达到了极致。
在日常的iOS开发中,不免会遇到需要App在被切到后台之后,仍然需要运行一个进程完成某些特殊的操作,一般后台运行进程作用有以下几类:
- 后台更新数据、同步刷新UI
- 与服务端通信,发送心跳,如“XMPP”类App
- 获取用户地理信息数据(GPS),如新“高德地图”
- 多媒体App用来继续播放音乐,如“酷狗音乐”
- 网络电话类App(VoIP)
要完成上述操作,有以下集中解决方案,每种方案各有优劣,适用于不同情况:
一、VoIP
优势:
- 永远不会被后台进程强杀
- 实现起来简单
劣势:
- 只能限定于VoIP类App使用。如无这VoIP功能,如果将App上传到AppStore,则会被拒绝。(虽然看起来没有问题,但是苹果就是这么“吹毛求疵”)
听名字就知道,这个方案是Apple专门为VoIP类App量身定制的。如果你的App并没有VoIP相关功能,在提交AppStore时会被拒绝,但是如果你只是企业内部分发,并不提交AppStore审核的话,就没有啥问题了。使用方法如下:
步骤一、在XCode中为你的Ap工程打开Background Modles,并勾选Voiceover IP选项,等于告诉iOS操作系统你需要此项功能,系统不能在后台强制杀掉我的进程,因为我要在后台进行一些操作。
同时,也可以在工程的plist文件中加入如下选项:
Key:Required background modes
Value:App provides Voice over IP services
步骤二、在需要后台运行的地方执行操作
- [[UIApplication sharedApplication] setKeepAliveTimeout:600 handler:^{
- //干活
- }];
这个函数会以设定的时间间隔被调用,不仅仅在后台,App在前台时候,这个函数一样能够被调用。所以可以把它当作一个横跨前后台的NSTimer来使用。
注意:
1、间隔时长必须大于600s,适合和服务器保持通讯,保证用户登录状态。
2、复活后,执行时间只有10s,之后又继续挂起。
3、 使用clearKeepAliveTimeout来取消唤醒
4、app必须是能和VoIP功能相关的内容,否则提交App Store会被拒绝。
二、Background fetch方式:
优势:
- 是官方推荐的后台数据刷新方法
- 实现比较简单
劣势:
- 程序被唤醒执行的时间不可控
- 被唤醒后执行时长有限制
步骤一、设置Background fetch:
步骤二、设置被唤醒最小时间间隔:
[applicationsetMinimumBackgroundFetchInterval:1.0f];
步骤二、需要执行的代码片段
– (void)application:(UIApplication *)applicationperformFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
注意:
1、代码被执行的时间间隔不可控,操作系统会以科学的方式来调用。
2、设置的时间只是最小时间间隔、操作系统只能保证在最小时间间隔内不会被调用,但是至于是1m还是1h还是多长时间调用,与操作系统当前的状态、用户打开App的频率有关系。
3、被操作系统唤醒后,只能有30s的时间执行你的任务,30s后,将被再次挂起。按说以国内的网络环境来说,基本上30s都能解决问题了。
三、播放空白声音配合“backgroundTask”进行后台运行
优势:
- 适用于需要进行后台播放音乐,记录地理位置变化等功能的App
- 可以用来进行一些简单的后台操作(如保活),而不被AppStore的审核人员拒绝,比较隐蔽
- 唤醒时间间隔可控
劣势:
- 实现相对于其他几种方案都要麻烦
步骤一:在Info.plist中,添加”Required background modes”键,value为:App plays audio,申明你需要使用后台播放音频的功能。
步骤二:调节音频会话的特性:
- – (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptionsNSDictionary *)launchOptions
- {
- self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
- // Override point for customization after application launch.
- NSError *setCategoryErr = nil;
- NSError *activationErr = nil;
- [[AVAudioSession sharedInstance]
- setCategory: AVAudioSessionCategoryPlayback
- error: &setCategoryErr];
- [[AVAudioSession sharedInstance]
- setActive: YES
- error: &activationErr];
- self.window.backgroundColor = [UIColor whiteColor];
- [self.window makeKeyAndVisible];
- return YES;
- }
- – (void)applicationDidEnterBackground:(UIApplication *)application{
- UIApplication* app = [UIApplication sharedApplication];
- __block UIBackgroundTaskIdentifier bgTask;
- bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
- dispatch_async(dispatch_get_main_queue(), ^{
- if (bgTask != UIBackgroundTaskInvalid)
- {
- bgTask = UIBackgroundTaskInvalid;
- }
- });
- }];
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- dispatch_async(dispatch_get_main_queue(), ^{
- if (bgTask != UIBackgroundTaskInvalid)
- {
- bgTask = UIBackgroundTaskInvalid;
- }
- });
- });
- }
这样,就可以在你需要重复执行的地方,实现一个NSTimer,有了上面的步骤之后,你的NSTimer就能不论前台后台都可以运行了。
注意:
和前三个不一样,前三个解决方案是程序被挂起之后又被唤醒,但是,这个解决方案是程序一直在后台运行,会非常耗内存,需要开发者关注。
四、远程消息推送,激活
优点:
- 实现简单
- 唤醒时间可控,由后台控制
缺点:
- 需要后台编码,跑服务进行App唤醒
步骤一、服务端设置推送的消息中加上“content-available”:
- {
- “aps” : {
- “content-available” : 1
- },
- “content-id” : 42
- }
步骤二、在接收消息的地方,进行唤醒后需要的操作