【问题标题】:Delayed background task sometimes runs only when app returns to foreground延迟的后台任务有时仅在应用返回前台时运行
【发布时间】:2013-06-10 23:55:54
【问题描述】:

我正在编写一个 iOS 应用程序,用于在参与的零售地点扫描条形码,在客户扫描打印在收据上的二维码后,该地点将代表客户向慈善机构捐款。

如果用户在参与地点停留了 60 秒或更长时间,我想向他们发送本地通知,提醒他们扫描他们可能从那里购买的任何收据。

我的问题是,当用户进入某个区域时,我想将呼叫延迟 60 秒 - 如果在 60 秒后他们仍在该区域中触发本地通知 - 但是,有时在 stillInRegion 内调用 sendLocalNotification在应用程序返回前台之前不会触发。我相信这与线程有时在延迟结束之前结束有关,但我不确定。我已经尝试了在 stackoverflow 和其他地方(块、nstimers 等)上可以找到的所有方法,但无济于事。有关如何更好地解决此问题的任何想法?

- (void) sendLocalNotification:(NSString *)regionId {
NSLog(@"we entered %@ and we're currently in %@", regionId, self.currentRegionId);
if ([regionId isEqualToString:self.currentRegionId]) {// if we're still in the region, send a local notification
    UILocalNotification *localNotif = [[UILocalNotification alloc] init];
    if (localNotif == nil) return;
    NSDate *fireTime = [[NSDate date] addTimeInterval:2]; // adds 2 secs
    localNotif.fireDate = fireTime;
    localNotif.alertBody = [NSString stringWithFormat:@"Did you just visit %@? If so, don't forget to scan your receipt!", regionId];
    localNotif.applicationIconBadgeNumber = [UIApplication sharedApplication].applicationIconBadgeNumber+1;
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
    [localNotif release];
}
}

- (void) stillInRegion:(CLRegion *)region {

NSLog(@"did enter region: %@", region.identifier);

[self performSelector:@selector(sendLocalNotification:) withObject:region.identifier afterDelay:60];
}

- (void) locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
if (self.didLaunchForRegionUpdate) {
    NSString *path = [DGGeofencingHelper applicationDocumentsDirectory];
    NSString *finalPath = [path stringByAppendingPathComponent:@"notifications.dg"];
    NSMutableArray *updates = [NSMutableArray arrayWithContentsOfFile:finalPath];

    if (!updates) {
        updates = [NSMutableArray array];
    }

    NSMutableDictionary *update = [NSMutableDictionary dictionary];

    [update setObject:region.identifier forKey:@"fid"];
    [update setObject:[NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]] forKey:@"timestamp"];
    [update setObject:@"enter" forKey:@"status"];

    [updates addObject:update];

    [updates writeToFile:finalPath atomically:YES];
} else {
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    [dict setObject:@"enter" forKey:@"status"];
    [dict setObject:region.identifier forKey:@"fid"];
    NSString *jsStatement = [NSString stringWithFormat:@"DGGeofencing.regionMonitorUpdate(%@);", [dict JSONString]];
    [self.webView stringByEvaluatingJavaScriptFromString:jsStatement];
}
self.currentRegionId = region.identifier;
self.cRegionEnterTime =[NSDate date];
[self stillInRegion:region];
}

【问题讨论】:

    标签: ios delay nstimer background-process geofencing


    【解决方案1】:

    locationManager:didEnterRegion 是否在前台被调用,然后您的应用程序进入后台? 我不太清楚您何时调用这些方法,但您可以尝试按如下方式创建后台任务:

    1. 添加一个类型为 NSUInteger 的属性,例如 bgTaskIdentifier,用于存储您的后台任务标识符。
    2. 在您致电 [self stillInRegion:region] 之前;添加以下代码:

      bgTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{}];

    3. 它应该调用您的 stillInRegion 方法,即使您进入后台也会继续,因此延迟应该继续计算!最后,您应该结束后台任务。为此,请在您的 sendLocalNotification 方法末尾的 if 块之后添加以下行:

      [[UIApplication sharedApplication] endBackgroundTask:bgTaskIdentifier];

    如果这有帮助,请告诉我们!请原谅我的英语不好!

    祝你有美好的一天!

    【讨论】:

    • 感谢您的想法,我实际上需要 didEnterRegion 来处理应用程序处于后台或前台时的调用。一个用例是用户在口袋里有手机,在零售商的商店,后台的应用程序向用户发送通知。
    • 不错!请记住,iOS 只允许 10 分钟的后台执行!我希望它会随着 iOS 7 的变化而改变!
    • 那么,我可以创建一个后台任务来处理已经在后台的延迟吗?比如在后台可能会被应用调用的 didEnterRegion 方法?
    • 理论上你可以,但我不知道苹果是否喜欢这样!您可以阅读this链接以获取更多信息!
    • 再次感谢您的帮助。看来这可能是一个可行的解决方案。查看您共享的链接,我发现:“要请求后台执行时间,请调用 UIApplication 类的 beginBackgroundTaskWithExpirationHandler: 方法。如果您的应用程序在任务进行时移动到后台,或者您的应用程序已经在背景,这种方法会延迟您的应用程序的暂停。”我已经拥有位置后台权限,只需将任务延迟 60 秒。我稍后会发布我的发现的更新。谢谢!
    【解决方案2】:

    在我的 didEnterRegion 方法中,我使用以下代码来实现所需的结果:

     UIApplication*   app = [UIApplication sharedApplication];
        bgTaskIdentifier = [app beginBackgroundTaskWithExpirationHandler:^{
            [app endBackgroundTask:bgTaskIdentifier];
            bgTaskIdentifier = UIBackgroundTaskInvalid;
        }];
    
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    
            NSTimer* t = [NSTimer scheduledTimerWithTimeInterval:180 target:self  selector:@selector(stillInRegion:) userInfo:region.identifier repeats:NO];
    
            [[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode];
    
            [[NSRunLoop currentRunLoop] run];
        });
    

    非常接近 @lucaslt89 所拥有的,但略有不同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-22
      • 1970-01-01
      • 1970-01-01
      • 2021-09-08
      相关资源
      最近更新 更多