如何解决iOS后台应用程序取消无法解决?

Jas*_*son 30 xcode objective-c ios background-fetch

我想让iOS后台应用程序获取在我的应用程序中工作.在Xcode中进行测试时,它可以正常运行,但在设备上运行却没有!

  • 我的测试设备运行的是iOS 9.3.5(我的部署目标是7.1)
  • 我在Xcode中的目标"Capabilities"下的"Background modes"下启用了"Background fetch"

在此输入图像描述

在应用程序中: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;
}
Run Code Online (Sandbox Code Playgroud)

我已经实现了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);
}
Run Code Online (Sandbox Code Playgroud)
  • 我已经(成功!)使用Xcode的Debug/SimulateBackgroundFetch对模拟器和设备进行了测试

  • 我已经成功测试了一个新方案,如另一个SO答案所示(/sf/answers/2094666171/)

  • 我的测试显示在大约0.3秒内在performFetch方法中执行的代码(所以它不需要很长时间)
  • 我已验证设备在设置中启用了后台刷新.
  • 当然,我已经看过其他SO问题,希望其他人经历同样的事情.:)

在设备上运行但未连接到Xcode时,我的代码没有执行.我打开了应用程序,关闭了应用程序(没有杀死应用程序!),等了几个小时和几天.我已经尝试登录fetch handers,还编写了代码来发送本地通知.

我曾经成功地在设备上看到了我的本地通知测试,事实上iOS似乎触发了三次获取,每次大约相隔大约十五分钟,但之后它再也没有发生过.

我知道用于确定允许背景提取发生频率的算法是一个谜,但我希望它能在一段时间内偶尔运行.

我无法测试其他什么,或者如何解决为什么它似乎在模拟器中工作但不在设备上工作.

感谢任何建议!

Pau*_*w11 27

您的问题是您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;
}
Run Code Online (Sandbox Code Playgroud)

我使用这种方法的测试应用程序最初每15分钟执行一次获取,但随着时间的推移它变得不那么频繁.如果没有后台任务,它会显示您遇到的相同问题.

我发现将背景提取间隔设置为其他内容UIApplicationBackgroundFetchIntervalMinimum也有帮助.我的测试应用程序运行时的后台获取间隔为3600(一小时),并且已经可靠地触发了几天; 即使手机重启后又没有再运行应用程序.但实际触发间隔为2-3小时.

我的示例应用就在这里


Tec*_*eko 12

为了实现后台提取,您必须做三件事:

  • 选中应用功能的后台模式中的背景提取框.
  • 使用setMinimumBackgroundFetchInterval(_ :)设置适合您的应用程序的时间间隔.
  • 在您的app委托中实现应用程序(_:performFetchWithCompletionHandler :)以处理后台提取.

背景提取频率

我们的应用程序执行背景提取的频率由系统决定,它取决于:

  • 网络连接是否在该特定时间可用
  • 设备是否处于唤醒状态即运行状态
  • 您的应用程序在之前的后台提取中消耗了多少时间和数据

换句话说,您的应用程序完全受制于为您安排后台获取的系统.此外,每次应用程序使用后台提取时,最多只需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;
}
Run Code Online (Sandbox Code Playgroud)

要检查您的应用是否启用了后台刷新,请使用以下代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

  • TechSeeko - 这个答案是对后台刷新要求的一个很好的总结,但没有解决核心问题.第一个,我认为保罗钉牢,是一个编码问题,没有标记后台任务.第二个基本上是试图了解我们可以期望我们的应用程序获取远程数据的频率和模式.似乎答案可能是"不要期望一致性",这是不幸的.您的摘要可能对其他人有用 - 谢谢. (6认同)