如果 iOS 注册了 UIBackgroundModes 的位置,iOS 会唤醒已终止的应用程序吗?

Posted

技术标签:

【中文标题】如果 iOS 注册了 UIBackgroundModes 的位置,iOS 会唤醒已终止的应用程序吗?【英文标题】:Will iOS wake up the terminated app if it's registered with location for UIBackgroundModes? 【发布时间】:2012-08-27 17:24:00 【问题描述】:

我知道如果一个应用使用了“重大变化的位置服务”,如果有一个位置更新要传递,ios 会唤醒它,即使应用被终止。

如果应用程序使用标准位置服务并将位置指定为 UIBackgroundModes 的键,我无法找到有关此案例的明确答案:即使它被终止,iOS 是否也会唤醒它以提供更新?还是应用需要在后台运行才能获取位置更新回调?

更新:当我问这个问题时,我没有时间测试它。但是在这里得到答案后,我在我的应用程序的委托中编写了这段代码,以查看我终止的应用程序在获得位置更新时是否会重新启动。当我收到更新通知时,我正在显示 UILocalNotification。但是,当我终止我的应用程序然后更改我在城市的位置时,该应用程序没有重新启动,我也没有得到任何更新。你能告诉我我做错了什么吗?

更新 #2: 根据本问答中的最终调查结果,此代码没有任何问题,这是使用标准位置服务的应用程序的预期行为,终止后不会重新启动。

我已将位置添加为 Info.plist 文件中的 UIBackgroundMode 之一。

这是我的应用委托的位置相关部分:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions     

    [UIApplication sharedApplication].applicationIconBadgeNumber = 0;

    m_locManager = [[CLLocationManager alloc] init];
    m_locManager.delegate = self;
    [m_locManager startUpdatingLocation];
    return YES;


- (void)applicationDidEnterBackground:(UIApplication *)application 
    [m_locManager startUpdatingLocation];


- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error

    NSLog("%@", [NSString stringWithFormat:@"Background Fail %@", [error localizedDescription]]);


- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation

    UILocalNotification * theNotification = [[UILocalNotification alloc] init];
    theNotification.alertBody = [NSString stringWithFormat:@"Background location %.06f %.06f %@" , newLocation.coordinate.latitude, newLocation.coordinate.longitude, newLocation.timestamp];
    theNotification.alertAction = @"Ok";

    theNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];

    [[UIApplication sharedApplication] scheduleLocalNotification:theNotification];

【问题讨论】:

我不知道它可以唤醒应用程序的技巧,赞成 再一次,它不能用作定期唤醒您的应用程序的技巧!如果您将 UIBackgroundModes 仅用于以下目的,Apple 将立即拒绝您的应用:跟踪用户的位置 @runmad 是的,我知道这应该用于正确的目的。我的是跟踪用户的位置以便将其发送到服务器,这样如果他靠近某个区域,服务器就会向他发送推送通知。考虑到应用被拒绝,如果我使用重大更改位置服务而不是标准服务,我会更安全吗? 根据@matheszabi 的评论,我只是确保其他阅读本文的人不会尝试使用此解决方案来“作弊”并最终被拒绝:) 我个人不想要应用程序在我更改位置时一直在后台重新启动,只是因为它需要做一些奇怪的事情。 【参考方案1】:

您似乎找到了一个可行的解决方案,而且使用重大更改框架似乎可以为您节省一些电池寿命,只要它对您的目的足够准确。您的解决方案也是有益的,因为它允许您通过仅监控您最接近的区域来处理可能超过允许同时监控 20 个区域的每个应用程序限制。

我需要类似的功能,但认为重大更改对于我的应用来说不够准确,因此我挖掘了更多内容并找到了以下内容。

根据Location Awareness Programming Guide,在“监控基于形状的区域”下:

在 iOS 中,与您的应用关联的区域会一直被跟踪,包括您的应用未运行时。如果在应用程序未运行时跨越区域边界,则该应用程序将重新启动到后台以处理事件。同样,如果应用程序在事件发生时被挂起,它会被唤醒并给予很短的时间来处理事件。

还有在“处理区域的跨界事件”中:

每次用户的当前位置跨越边界区域时,系统都会为您的应用生成适当的区域事件。如果您的应用程序已经在运行,这些事件将直接发送给任何当前位置管理器对象的委托。如果您的应用程序没有运行,系统会在后台启动它,以便它可以响应。应用可以实现以下方法来处理越界:

locationManager:didEnterRegion:

locationManager:didExitRegion:

在我的例子中,我只需要在越界时触发通知,因此我将 AppDelegate 设置为符合 CLLocationManagerDelegate,创建了一个 locationManager 属性,并将其放入我的实现文件中:

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions

    self.locationManager.delegate = self;
    return YES;


- (CLLocationManager *)locationManager

    if (!_locationManager) 
        _locationManager = [[CLLocationManager alloc] init];
        _locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    
    return _locationManager;


- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region

    UILocalNotification *notification = [[UILocalNotification alloc] init];
    notification.alertBody = NSLocalizedString(@"You crossed a boundary!", @"user crossed a boundary");
    [[UIApplication sharedApplication] presentLocalNotificationNow:notification];

测试表明无需设置 UIBackgroundModes 位置键即可。在这个用例中,操作系统会为您处理边界监控,然后将事件发送到您的应用程序,时间足以处理事件。这不会启动应用程序,它只是在后台运行它几秒钟以处理边界事件,在此期间您只能进行相关活动。希望这对其他人也有帮助!

【讨论】:

嗨,Daniel,就我而言,我必须执行多个 API 调用。我认为当用户跨越任何区域时,应用程序会唤醒几秒钟,不足以执行我的任务。我已通过有关跨区域的本地通知对此进行了检查。这有可能以某种方式延长应用程序唤醒时间吗?提前致谢。【参考方案2】:

是的,它的行为方式相同,只是它会更频繁地接收更新(因此会消耗更多电池)。

来源:App States and Multitasking 在跟踪用户位置部分下。

EDIT 重新阅读后,似乎程序将从 suspended 状态而不是 terminate 状态唤醒。不过,我不认为你真的需要这种后台模式。它专为需要精细位置信息的应用程序(如逐向导航)而设计。查看位置编程指南中有关“基于区域”编程的部分。您的 cmets 中的示例被列为此应用的用途之一。

再次编辑根据 cmets 中的讨论,最终的解决方案似乎是设置显着的位置变化,直到用户足够近,然后切换到精细的位置变化(并希望应用在此期间不会终止 ^^)

【讨论】:

为了扩展这个答案,你不能使用这个后台模式来启动你的应用程序,因为它实际上需要用户位置来执行某种更新。 非常正确@runmad Apple 将拒绝以这种方式运行的应用程序。 我写了一个测试代码来看看这个。该应用程序不会以这种方式重新启动,我不知道这是我做错了什么还是预期的事情。如果您想详细了解我的步骤,请查看我的问题。 你也用显着换位模式测试了吗? @borrrden 是的,我做到了。它在以显着位置更改模式终止后重新启动,但不使用标准位置模式(将 UIBackgroundMode 设置为位置)【参考方案3】:

一旦你杀死应用程序,位置更新就会发送到应用程序

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

您需要通过查找启动密钥来收集此委托方法中的位置更新,如下所示,

if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey])

//Code to handle the location update

希望这是您所寻找的。是的,您需要在 plist 中使用值“App registers for location updates”设置键“Required backround mode”。

【讨论】:

以上是关于如果 iOS 注册了 UIBackgroundModes 的位置,iOS 会唤醒已终止的应用程序吗?的主要内容,如果未能解决你的问题,请参考以下文章

在 Cordova 中检查是不是在 iOS 或 Android 上注册了 URL 方案

在 iOS 中使用电子邮件和密码注册

Android 和 iOS 上的 Facebook 用户 ID

iOS - 用于自动更新的存折注册 URL

iOS应用程序在登录/注册时提交被拒绝[关闭]

IOS 友盟使用详解