iOS Background Modes

关于iOS后台操作只介绍了目前公司项目需要用到的:

一、Background Fetch(后台获取)

  后台获取干的事情就是在用户打开应用之前就有机会(没错,就是有机会)执行代码来获取数据,刷新UI。这样在用户打开APP的时候,最新的内容就已经呈现在用户眼前了,而不用当APP启动后再去加载。想一下,不再是进来等待数据请求完成然后再删一下刷新界面,反正听起来是相当令人振奋人心的。

具体操作:

<1>Target -> Capabilities -> Background Modes -> Background fetch (勾选)

<2>在应用启动后调用:[[UIApplicationsharedApplication]setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];这里的时间间隔代表“在上次获取或者关闭应用之后,在这段时间内一定不会去做后台获取”。也就是说真正具体到什么时候进行后台获取完全取决于系统的心情了,我们无法控制。系统只会在一些特定情况下(比如接受到邮件,推送...)顺便帮你的应用获取一下,也有可能专门为你的应用唤醒下设备。需要注意的是:如果应用对数据即使性不是特别高的话就不要用UIApplicationBackgroundFetchIntervalMinimum,而是自定义时间,可以尽可能的减少能耗。

PS:上面加粗的那句一定要有,要不系统会自动判定为UIApplicationBackgroundFetchIntervalNever,也就是永远不进行后台获取。

<3>完成前两步之后只需要在AppDelegate里实现:-application:performFetchWithCompletionHandler:就可以了。开发者需要的就是在这个代理里请求数据然后刷新UI,并通知系统获取结束。需要注意的是系统不会给你过长的时间去做fetch,一般会少于一分钟,而且看别人的博客说fetch在绝大多数情况下是和别的应用公用网络连接的(我自己没测试过,只是感觉好有意思,这样流量会算到别人的app上吗!!!)。在获取完成后必须通知系统获取完成,调用代理里的completionHandler:(),括号里传一个叫UIBackgroundFetchResult作为参数,可以传UIBackgroundFetchResultNewData,UIBackgroundFetchResultNoData,UIBackgroundFetchResultFailed三种。分别表示获取到了新数据(此时系统将对现在的UI状态截图并更新App Switcher中你的应用的截屏),没有新数据,以及获取失败。写一个简单的吧:

- (void)application:(UIApplication*)application performFetchWithCompletionHandler:(void(^)(UIBackgroundFetchResult))completionHandler{

UINavigationController *navigationController = (UINavigationController*)self.window.rootViewController;

YDMainViewController *fetchViewController = navigationController.topViewController;

if([fetchViewController respondsToSelector:@selector(fetchDataResult:)]) {

[fetchViewController fetchDataResult:^(NSError *error, NSArray *results){

if(!error) {

if(results.count !=0) {

//Update UI with results.

//Tell system all done.

completionHandler(UIBackgroundFetchResultNewData);

}else{

completionHandler(UIBackgroundFetchResultNoData);

}

}else{

completionHandler(UIBackgroundFetchResultFailed);

}

}];

}else{

completionHandler(UIBackgroundFetchResultFailed);

}

}

PS:当然,实际情况中会比这要复杂得多,用户当前的ViewController是否合适做获取,获取后的数据如何处理都需要考虑。另外要说明的是上面的代码在获取成功后直接在appDelegate里更新UI,这只是为了能在同一处进行说明,但却是不正确的结构。比较好的做法是将获取和更新UI的业务逻辑都放到fetchViewController里,然后向其发送获取消息的时候将completionHandler作为参数传入,并在fetchViewController里完成获取结束的报告。

一、Remote-Notification(远程通知)

<1>Target -> Capabilities -> Background Modes -> Remote notifications(勾选)

<2>需要设置为静默推送: aps { content-available:1 alert:{...} } ,公司用的是极光推送,他们的官方文档有介绍。说实话,极光相当不靠谱,其中的坑大家自己去体会吧!

<3>最后在Appdelegate里实现-application:didReceiveRemoteNotification:fetchCompletionHandle:,iOS10.0以上要实现- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {}

下面介绍下一些限制和应用(在CSDN上看到的,未亲测,目前这个还在开发当中)

  因为一旦推送成功,用户的设备将被唤醒,因此这类推送不可能不受到限制。Apple将限制此类推送的频率,当频率超过一定限制后,带有content-available标志的推送将会被阻塞,以保证用户设备不被频繁唤醒。按照Apple的说法,这个频率在一小时内个位数次的推送的话不会有太大问题。

  Apple给出了几个典型的应用情景,比如一个电视节目类的应用,当用户标记某些剧目为喜爱时,当这些剧有更新时,可以给用户发送静默的唤醒推送通知,客户端在接到通知后检查更新并开始后台下载(后台还在研究当中)。下载完成后发送一个本地推送告知用户新的内容已经准备完毕。这样在用户注意到推送并打开应用的时候,所有必要的内容已经下载完毕,UI也将切换至用户喜爱的剧目,用户只需要点击播放即可开始真正使用应用,这绝对是无比顺畅和优秀的体验。另一种应用情景是文件同步类,比如用户标记了一些文件为需要随时同步,这样用户在其他设备或网页服务上更改了这些文件时,可以发送静默推送然后使用后台传输来保持这些文件随时是最新。

  总的来说,其实后台获取和静默推送在很多方面是很类似的,特别是实现和处理的方式,但是它们适用的情景是完全不同的。后台获取更多地使用在泛数据模式下,也即用户对特定数据并不是很关心,数据应该被更新的时间也不是很确定,典型的有社交类应用和天气类应用;而静默推送或者是推送唤醒更多地应该是用户感兴趣的内容发生更新时被使用,比如消息类应用和内容型服务等。根据不同的应用情景,选择合适的后台策略(或者混合使用两者),以带给用户绝佳体验。

三、后台传输(Background Transfer Service)

iOS6.0及以下的我不太清楚,其实我解除iOS的时候就已经到8.0以上了。目前后台传输必须要使用到iOS7.0的网络连接新类,NSURLSession,这是iOS7用来替代以前的NSURLConnection的。多的不说直接贴开启后台任务的代码:

NSURLSessionConfiguration *configure = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.company.appname.BackgroundSession"];

NSURLSession *session = [NSURLSession sessionWithConfiguration:configure delegate:self delegateQueue:nil];

NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"yourDownloadUrl"]];

NSURLSessionTask *task = [session downloadTaskWithRequest:request];

[task resume];

//下面是AFNetworking的后台任务的初始化,其他的大家应该都知道的(因为我没发现不用AFNetworking的,所以顺便贴一下)

//AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.company.appname.BackgroundSession"]];

  最后在AppDelegate中实现application:handleEventsForBackgroundURLSession:completionHandler:

-(void)application:(UIApplication*)applicationhandleEventsForBackgroundURLSession:(NSString*)identifiercompletionHandler:(void(^)())completionHandler{

//Check if all transfers are done, and update UI

//Then tell system background transfer over, so it can take new snapshot to show in App Switcher

 completionHandler();

//self.backTaskCompletionHandle = completionHandle;//建议用法,下面有介绍

//You can also pop up a local notification to remind the user}

NSURLSession和对应的NSURLSessionTask有以下重要的delegate方法可以使用:

-(void)URLSession:(NSURLSession*)sessiondownloadTask:(NSURLSessionDownloadTask*)downloadTaskdidFinishDownloadingToURL:(NSURL*)location;

-(void)URLSession:(NSURLSession*)sessiontask:(NSURLSessionTask*)taskdidCompleteWithError:(NSError*)error;

当后台传输的状态发生变化(包括正常结束和失败)的时候,应用将被唤醒并运行appDelegate中的回调,接下来NSURLSessionTask的委托方法将在后台被调用。虽然上面的例子中直接在appDelegate中call了completionHandler,但是实际上更好的选择是在appDelegate中暂时持有completionHandler,即上面注释掉的代码,然后在NSURLSessionTask的delegate方法中检查是否确实完成了传输并更新UI后,再调用appDelegate.completionHandler(),记得释放掉。另外,你的应用到现在为止只是在后台运行,想要提醒用户传输完成的话,也许你还需要在这个时候发送一个本地推送(记住在这个时候你的应用是可以执行代码的,虽然是在后台),这样用户可以注意到你的应用的变化并回到应用,并开始已经准备好数据和界面。

PS:网上有看到一些限制条件,但是目前还没有测试出来,如有大神知道的请一定要留言告诉我,谢谢!还有现在在做静默推送,恳请大神们指导下。

最后,如果收到通知,但是没有点击通知,而是通过点击icon进入,求获取消息内容的方法。目前我们是用当app再次进入前台的时候去请求后台存的推送,当然要和本地数据库最新一条比对一下。再次跪求有没有其他的方法......

你可能感兴趣的:(iOS Background Modes)