我正在尝试将iOS后台应用程序提取到我的应用程序中。在Xcode中进行测试时可以正常运行,但在设备上运行则不行!
在application:didFinishLaunchingWithOptions中我尝试了使用setMinimumBackgroundFetchInterval的各种间隔,包括UIApplicationBackgroundFetchIntervalMinimum
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// tell the system we want background fetch
//[application setMinimumBackgroundFetchInterval:3600]; // 60 minutes
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
//[application setMinimumBackgroundFetchInterval:1800]; // 30 minutes
return YES;
}
我已经实现了application:performFetchWithCompletionHandler
void (^fetchCompletionHandler)(UIBackgroundFetchResult);
NSDate *fetchStart;
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
fetchCompletionHandler = completionHandler;
fetchStart = [NSDate date];
[[NSUserDefaults standardUserDefaults] setObject:fetchStart forKey:kLastCheckedContentDate];
[[NSUserDefaults standardUserDefaults] synchronize];
[FeedParser parseFeedAtUrl:url withDelegate:self];
}
-(void)onParserFinished
{
DDLogVerbose(@"AppDelegate/onParserFinished");
UIBackgroundFetchResult result = UIBackgroundFetchResultNoData;
NSDate *fetchEnd = [NSDate date];
NSTimeInterval timeElapsed = [fetchEnd timeIntervalSinceDate:fetchStart];
DDLogVerbose(@"Background Fetch Duration: %f seconds", timeElapsed);
if ([self.mostRecentContentDate compare:item.date] < 0) {
DDLogVerbose(@"got new content: %@", item.date);
self.mostRecentContentDate = item.date;
[self scheduleNotificationWithItem:item];
result = UIBackgroundFetchResultNewData;
}
else {
DDLogVerbose(@"no new content.");
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:60];
localNotification.alertBody = [NSString stringWithFormat:@"Checked for new posts in %f seconds", timeElapsed];
localNotification.timeZone = [NSTimeZone defaultTimeZone];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
fetchCompletionHandler(result);
}
我已经(使用Xcode的Debug / SimulateBackgroundFetch
我已成功通过新方案测试,如另一个SO答案(https://stackoverflow.com/a/29923802/519030)
在设备上运行但未连接到Xcode时,我的代码没有执行。我打开了应用程序,关闭了应用程序(没有杀死应用程序!),等了几个小时和几天。我已经尝试登录fetch handers,还编写了代码来发送本地通知。
我曾经成功地在设备上看到了我的本地通知测试,事实上iOS似乎触发了三次获取,每次大约相隔大约十五分钟,但之后再也没有发生过。
我知道用于确定允许背景提取发生频率的算法是一个谜,但我希望它能在一段时间内偶尔运行。
我无法测试其他什么,或者如何解决为什么它似乎在模拟器中工作但在设备上没有。
感谢任何建议!
答案 0 :(得分:24)
您的问题是您在调用完成处理程序之前从performFetchWithCompletionHandler
返回,因为网络提取操作正在后台进行,您在委托方法中调用完成处理程序。由于iOS认为您不遵守规则,因此会拒绝您使用后台获取功能。
要解决此问题,您需要调用beginBackgroundTaskWithExpirationHandler
,然后在调用完成处理程序后结束该任务。
类似的东西:
UIBackgroundTaskIdentifier backgroundTask
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
fetchCompletionHandler = completionHandler;
fetchStart = [NSDate date];
self.backgroundTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:self.backgroundUpdateTask];
self.backgroundTask = UIBackgroundTaskInvalid;
}];
[[NSUserDefaults standardUserDefaults] setObject:fetchStart forKey:kLastCheckedContentDate];
[FeedParser parseFeedAtUrl:url withDelegate:self];
}
-(void)onParserFinished
{
DDLogVerbose(@"AppDelegate/onParserFinished");
UIBackgroundFetchResult result = UIBackgroundFetchResultNoData;
NSDate *fetchEnd = [NSDate date];
NSTimeInterval timeElapsed = [fetchEnd timeIntervalSinceDate:fetchStart];
DDLogVerbose(@"Background Fetch Duration: %f seconds", timeElapsed);
if ([self.mostRecentContentDate compare:item.date] < 0) {
DDLogVerbose(@"got new content: %@", item.date);
self.mostRecentContentDate = item.date;
[self scheduleNotificationWithItem:item];
result = UIBackgroundFetchResultNewData;
}
else {
DDLogVerbose(@"no new content.");
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.alertBody = [NSString stringWithFormat:@"Checked for new posts in %f seconds", timeElapsed];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
fetchCompletionHandler(result);
[[UIApplication sharedApplication] application endBackgroundTask:self.backgroundUpdateTask];
self.backgroundTask = UIBackgroundTaskInvalid;
}
我使用这种方法的测试应用程序最初每15分钟执行一次获取,但随着时间的推移它会变得不那么频繁。如果没有后台任务,它就会出现您遇到的相同问题。
我发现将背景提取间隔设置为UIApplicationBackgroundFetchIntervalMinimum
以外的其他内容也会有所帮助。我的测试应用程序正在以3600(一小时)的后台获取间隔运行,并且已经可靠地触发了几天;即使手机重启后又没有再运行应用程序。但实际的触发间隔是2-3小时。
我的示例应用是here
答案 1 :(得分:11)
要实现后台提取,您必须执行以下三项操作:
后台获取频率
我们的应用程序执行的频率背景提取由系统决定,它取决于:
换句话说,您的应用程序完全受制于为您安排后台获取的系统。此外,每次您的应用程序使用后台提取时,最多只需30秒即可完成此过程。
包含在AppDelegate中 (将以下代码更改为您的提取需求)
-(void) application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(@"Background fetch started...");
//---do background fetch here---
// You have up to 30 seconds to perform the fetch
BOOL downloadSuccessful = YES;
if (downloadSuccessful) {
//---set the flag that data is successfully downloaded---
completionHandler(UIBackgroundFetchResultNewData);
} else {
//---set the flag that download is not successful---
completionHandler(UIBackgroundFetchResultFailed);
}
NSLog(@"Background fetch completed...");
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
return YES;
}
要检查您的应用是否启用了后台刷新,请使用以下代码:
UIBackgroundRefreshStatus status = [[UIApplication sharedApplication] backgroundRefreshStatus];
switch (status) {
case UIBackgroundRefreshStatusAvailable:
// We can do background fetch! Let's do this!
break;
case UIBackgroundRefreshStatusDenied:
// The user has background fetch turned off. Too bad.
break;
case UIBackgroundRefreshStatusRestricted:
// Parental Controls, Enterprise Restrictions, Old Phones, Oh my!
break;
}