即使应用程序终止,iOS 9 如何获取位置

Posted

技术标签:

【中文标题】即使应用程序终止,iOS 9 如何获取位置【英文标题】:iOS 9 how get locations even if app terminated 【发布时间】:2015-12-21 08:32:15 【问题描述】:

我了解如何在后台检索位置。而且我知道即使终止Continious location updates even if app is terminated in ios

,也有机会获得位置

但是。我有应用程序 Moves 和 Foursquare。如果这个应用程序甚至没有运行(我终止所有应用程序并且没有应用程序正在运行)然后我转到“隐私”并更改这个应用程序的位置以禁用(从不),我可以看到状态栏中的箭头消失了。但是当我启用位置更新(始终)时,状态栏中再次出现箭头,此时应用程序未运行。所以这个应用程序开始获取有关位置的信息。如何?即使有几天没有启动 MOves,这个应用程序也会向我显示过去几天的正确路线。他们如何检索过去几天的位置信息,甚至应用程序没有启动?

【问题讨论】:

这不是一个真正的编程问题,更多的是关于操作系统的工作原理。在基本层面上,应用程序告诉系统在用户授予权限后它将“始终”使用位置。系统将决定何时告诉您的应用该位置,并通过使用方法允许您对该位置执行某些操作。 你找到解决这个问题的方法了吗? 如果您愿意使用 VOIP 推送,请查看此链接 - appcaretaker.com/2018/06/05/… 【参考方案1】:

我找到了解决方案。 这在 IOS 9 中对我有用。即使重新启动 IOS 设备,它也会继续运行。

http://mobileoop.com/getting-location-updates-for-ios-7-and-8-when-the-app-is-killedterminatedsuspended

GitHub 示例: https://github.com/voyage11/GettingLocationWhenSuspended

更新

您必须使用[myLocationManager startMonitoringSignificantLocationChanges],而不是[myLocationManager startUpdatingLocation]

下面是我的 AppDelegate。

@implementation LocationAppDelegate

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

    NSLog(@"didFinishLaunchingWithOptions");

    self.shareModel = [LocationManager sharedManager];
    self.shareModel.afterResume = NO;

    [self.shareModel addApplicationStatusToPList:@"didFinishLaunchingWithOptions"];

     UIAlertView * alert;

    //We have to make sure that the Background App Refresh is enable for the Location updates to work in the background.
    if ([[UIApplication sharedApplication] backgroundRefreshStatus] == UIBackgroundRefreshStatusDenied) 

        alert = [[UIAlertView alloc]initWithTitle:@""
                                          message:@"The app doesn't work without the Background App Refresh enabled. To turn it on, go to Settings > General > Background App Refresh"
                                         delegate:nil
                                cancelButtonTitle:@"Ok"
                                otherButtonTitles:nil, nil];
        [alert show];

     else if ([[UIApplication sharedApplication] backgroundRefreshStatus] == UIBackgroundRefreshStatusRestricted) 

        alert = [[UIAlertView alloc]initWithTitle:@""
                                          message:@"The functions of this app are limited because the Background App Refresh is disable."
                                         delegate:nil
                                cancelButtonTitle:@"Ok"
                                otherButtonTitles:nil, nil];
        [alert show];

     else 

        // When there is a significant changes of the location,
        // The key UIApplicationLaunchOptionsLocationKey will be returned from didFinishLaunchingWithOptions
        // When the app is receiving the key, it must reinitiate the locationManager and get
        // the latest location updates

        // This UIApplicationLaunchOptionsLocationKey key enables the location update even when
        // the app has been killed/terminated (Not in th background) by iOS or the user.

        NSLog(@"UIApplicationLaunchOptionsLocationKey : %@" , [launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]);
        if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) 

            // This "afterResume" flag is just to show that he receiving location updates
            // are actually from the key "UIApplicationLaunchOptionsLocationKey"
            self.shareModel.afterResume = YES;

            [self.shareModel startMonitoringLocation];
            [self.shareModel addResumeLocationToPList];
        
    

    return YES;


- (void)applicationDidEnterBackground:(UIApplication *)application 
    NSLog(@"applicationDidEnterBackground");
    [self.shareModel restartMonitoringLocation];

    [self.shareModel addApplicationStatusToPList:@"applicationDidEnterBackground"];




- (void)applicationDidBecomeActive:(UIApplication *)application 
    NSLog(@"applicationDidBecomeActive");

    [self.shareModel addApplicationStatusToPList:@"applicationDidBecomeActive"];

    //Remove the "afterResume" Flag after the app is active again.
    self.shareModel.afterResume = NO;

    [self.shareModel startMonitoringLocation];



- (void)applicationWillTerminate:(UIApplication *)application 
    NSLog(@"applicationWillTerminate");
    [self.shareModel addApplicationStatusToPList:@"applicationWillTerminate"];



@end

我的自定义 LocationManager。

@implementation LocationManager

//Class method to make sure the share model is synch across the app
+ (id)sharedManager 
    static id sharedMyModel = nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^
        sharedMyModel = [[self alloc] init];
    );

    return sharedMyModel;



#pragma mark - CLLocationManager

- (void)startMonitoringLocation 
    if (_anotherLocationManager)
        [_anotherLocationManager stopMonitoringSignificantLocationChanges];

    self.anotherLocationManager = [[CLLocationManager alloc]init];
    _anotherLocationManager.delegate = self;
    _anotherLocationManager.allowsBackgroundLocationUpdates = true;
    _anotherLocationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
    _anotherLocationManager.activityType = CLActivityTypeOtherNavigation;

    if(IS_OS_8_OR_LATER) 
        [_anotherLocationManager requestAlwaysAuthorization];
    
    [_anotherLocationManager startMonitoringSignificantLocationChanges];


- (void)restartMonitoringLocation 
    [_anotherLocationManager stopMonitoringSignificantLocationChanges];

    if (IS_OS_8_OR_LATER) 
        [_anotherLocationManager requestAlwaysAuthorization];
    
    [_anotherLocationManager startMonitoringSignificantLocationChanges];



#pragma mark - CLLocationManager Delegate

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations

    NSLog(@"locationManager didUpdateLocations: %@",locations);

    for (int i = 0; i < locations.count; i++) 

        CLLocation * newLocation = [locations objectAtIndex:i];
        CLLocationCoordinate2D theLocation = newLocation.coordinate;
        CLLocationAccuracy theAccuracy = newLocation.horizontalAccuracy;

        self.myLocation = theLocation;
        self.myLocationAccuracy = theAccuracy;
    

    [self addLocationToPList:_afterResume];




#pragma mark - Plist helper methods

// Below are 3 functions that add location and Application status to PList
// The purpose is to collect location information locally

- (NSString *)appState 
    UIApplication* application = [UIApplication sharedApplication];

    NSString * appState;
    if([application applicationState]==UIApplicationStateActive)
        appState = @"UIApplicationStateActive";
    if([application applicationState]==UIApplicationStateBackground)
        appState = @"UIApplicationStateBackground";
    if([application applicationState]==UIApplicationStateInactive)
        appState = @"UIApplicationStateInactive";

    return appState;


- (void)addResumeLocationToPList 

    NSLog(@"addResumeLocationToPList");

    NSString * appState = [self appState];

    self.myLocationDictInPlist = [[NSMutableDictionary alloc]init];
    [_myLocationDictInPlist setObject:@"UIApplicationLaunchOptionsLocationKey" forKey:@"Resume"];
    [_myLocationDictInPlist setObject:appState forKey:@"AppState"];
    [_myLocationDictInPlist setObject:[NSDate date] forKey:@"Time"];

    [self saveLocationsToPlist];




- (void)addLocationToPList:(BOOL)fromResume 
    NSLog(@"addLocationToPList");

    NSString * appState = [self appState];

    self.myLocationDictInPlist = [[NSMutableDictionary alloc]init];
    [_myLocationDictInPlist setObject:[NSNumber numberWithDouble:self.myLocation.latitude]  forKey:@"Latitude"];
    [_myLocationDictInPlist setObject:[NSNumber numberWithDouble:self.myLocation.longitude] forKey:@"Longitude"];
    [_myLocationDictInPlist setObject:[NSNumber numberWithDouble:self.myLocationAccuracy] forKey:@"Accuracy"];

    [_myLocationDictInPlist setObject:appState forKey:@"AppState"];

    if (fromResume) 
        [_myLocationDictInPlist setObject:@"YES" forKey:@"AddFromResume"];
     else 
        [_myLocationDictInPlist setObject:@"NO" forKey:@"AddFromResume"];
    

    [_myLocationDictInPlist setObject:[NSDate date] forKey:@"Time"];

    [self saveLocationsToPlist];


- (void)addApplicationStatusToPList:(NSString*)applicationStatus 

    NSLog(@"addApplicationStatusToPList");

    NSString * appState = [self appState];

    self.myLocationDictInPlist = [[NSMutableDictionary alloc]init];
    [_myLocationDictInPlist setObject:applicationStatus forKey:@"applicationStatus"];
    [_myLocationDictInPlist setObject:appState forKey:@"AppState"];
    [_myLocationDictInPlist setObject:[NSDate date] forKey:@"Time"];

    [self saveLocationsToPlist];


- (void)saveLocationsToPlist 
    NSString *plistName = [NSString stringWithFormat:@"LocationArray.plist"];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docDir = [paths objectAtIndex:0];
    NSString *fullPath = [NSString stringWithFormat:@"%@/%@", docDir, plistName];

    NSMutableDictionary *savedProfile = [[NSMutableDictionary alloc] initWithContentsOfFile:fullPath];

    if (!savedProfile) 
        savedProfile = [[NSMutableDictionary alloc] init];
        self.myLocationArrayInPlist = [[NSMutableArray alloc]init];
     else 
        self.myLocationArrayInPlist = [savedProfile objectForKey:@"LocationArray"];
    

    if(_myLocationDictInPlist) 
        [_myLocationArrayInPlist addObject:_myLocationDictInPlist];
        [savedProfile setObject:_myLocationArrayInPlist forKey:@"LocationArray"];
    

    if (![savedProfile writeToFile:fullPath atomically:FALSE]) 
        NSLog(@"Couldn't save LocationArray.plist" );
    



@end

不要忘记在 plist 文件中启用背景位置。

并将消息设置为 LocationAlwaysUsageDescription。

【讨论】:

为什么在这种情况下需要后台提取? 嗨@SMKS。因为该应用程序将在后台模式下运行。【参考方案2】:

我只是按照@Busata 的回答,它在 iOS 12 上运行良好。

在这里,我将用 Swift 发布答案。

LocationManager 类:

class NABackgroundRideLocationTrackingManager: 
NSObject,CLLocationManagerDelegate 
    var locationManager: CLLocationManager?
    private static var privateShared : 
NABackgroundRideLocationTrackingManager?
    var traveledDistance: Double = 0
    class func shared() -> NABackgroundRideLocationTrackingManager 
    guard let uwShared = privateShared else 
        privateShared = NABackgroundRideLocationTrackingManager()
        return privateShared!
    
    return uwShared

class func destroy() 
    privateShared = nil


func startMonitoringLocation()
    if locationManager != nil
        locationManager?.stopMonitoringSignificantLocationChanges()
    
    locationManager = CLLocationManager()
    locationManager?.delegate = self
    locationManager?.allowsBackgroundLocationUpdates = true
    locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    locationManager?.activityType = CLActivityType.otherNavigation
    locationManager?.requestAlwaysAuthorization()
    locationManager?.startMonitoringSignificantLocationChanges()

func restartMonitoringLocation()
    locationManager?.stopMonitoringSignificantLocationChanges()
    locationManager?.requestAlwaysAuthorization()
    locationManager?.startMonitoringSignificantLocationChanges()

func postLocations(userLocation: CLLocation)
    var params: [String:String] = [:]
    params["latitude"] = String(userLocation.coordinate.latitude)
    params["longitude"] = String(userLocation.coordinate.longitude)
    NAAppManager.postLocationOnBackground(params, success:  (success) in
        print("location Posted")
    )  (failure) in
        print("Failed")
    


func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
    let location = locations.last
    self.postLocations(userLocation: location!)




Appdelegate:

class AppDelegate: UIResponder, UIApplicationDelegate 

var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 

    if UIApplication.shared.backgroundRefreshStatus == .restricted 

    else if UIApplication.shared.backgroundRefreshStatus == .denied 

    else
        if ((launchOptions?[UIApplication.LaunchOptionsKey.location]) != nil)
            NABackgroundRideLocationTrackingManager.shared().startMonitoringLocation()
        
    


func applicationDidEnterBackground(_ application: UIApplication) 
    NABackgroundRideLocationTrackingManager.shared().restartMonitoringLocation()


func applicationDidBecomeActive(_ application: UIApplication) 
    NABackgroundRideLocationTrackingManager.shared().startMonitoringLocation()


【讨论】:

以上是关于即使应用程序终止,iOS 9 如何获取位置的主要内容,如果未能解决你的问题,请参考以下文章

即使在终止应用程序后,我们还能获取位置信息吗? [关闭]

在后台模式和终止模式下获取位置更新 IOS

当应用程序被终止/终止/暂停时,cordova 获取 iOS 7 和 8 的位置更新

life360 等应用程序如何在终止状态下获取位置更新?

即使应用程序被杀死/终止,位置更新

即使应用程序被杀死/终止,位置更新