是的,我们可以在 500m 以上或以下的情况下进行测试似乎不可能,所以我将它应用到我的代码中并进行测试。结果很棒。当应用程序被用户强制退出时,它会将所有位置数据保存在本地 plist 中,因此您可以通过该 plist 看到它。我不记得我在哪里看到了那个链接,但我分享了我的一堆直接代码,可以帮助你理解它。
最有趣的部分是我能够在应用程序处于终止状态时调用服务器,这真的很棒。
位置更改时,位置管理器似乎会在几毫秒内唤醒应用。
LocationManager.h
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#define IS_OS_8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
@interface LocationManager : NSObject
@property (nonatomic) CLLocationManager * anotherLocationManager;
@property (nonatomic) CLLocationCoordinate2D myLastLocation;
@property (nonatomic) CLLocationAccuracy myLastLocationAccuracy;
@property (nonatomic) CLLocationCoordinate2D myLocation;
@property (nonatomic) CLLocationAccuracy myLocationAccuracy;
@property (nonatomic) NSMutableDictionary *myLocationDictInPlist;
@property (nonatomic) NSMutableArray *myLocationArrayInPlist;
@property (nonatomic) BOOL afterResume;
+ (id)sharedManager;
- (void)startMonitoringLocation;
- (void)restartMonitoringLocation;
- (void)addResumeLocationToPList;
- (void)addLocationToPList:(BOOL)fromResume;
- (void)addApplicationStatusToPList:(NSString*)applicationStatus;
@end
LocationManager.m
#import "LocationManager.h"
#import <UIKit/UIKit.h>
#import "UpdateUserLocation.h"
#import "Constants.h"
@interface LocationManager () <CLLocationManagerDelegate>
@property (nonatomic , strong) UpdateUserLocation *updateUserLocation;
@end
@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.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"];
[self updatePreferredUserLocation:[NSString stringWithFormat:@"%f",self.myLocation.latitude] withLongitude:[NSString stringWithFormat:@"%f",self.myLocation.longitude]];
} 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" );
}
}
Appdelegate.m
#import "LocationManager.h"
@property (strong,nonatomic) LocationManager * locationShareModel;
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
enable when use track location at force quit
[self.locationShareModel restartMonitoringLocation];
[self.locationShareModel addApplicationStatusToPList:@"applicationDidEnterBackground"];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
enable when use track location at force quit
[self.locationShareModel addApplicationStatusToPList:@"applicationDidBecomeActive"];
//Remove the "afterResume" Flag after the app is active again.
self.locationShareModel.afterResume = NO;
[self.locationShareModel startMonitoringLocation];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
enable when use track location at force quit
[self.locationShareModel addApplicationStatusToPList:@"applicationWillTerminate"];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
NSLog(@"AG_Didfinishlaunching");
//enable when use track location at force quit
[self trackLocation:launchOptions];
}
- (void)trackLocation:(NSDictionary *)launchOptions {
self.locationShareModel = [LocationManager sharedManager];
self.locationShareModel.afterResume = NO;
[self.locationShareModel 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) {
///////// delete this alert view if no need
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];
UIAlertController *showMsgAlertController = [UIAlertController alertControllerWithTitle: @"Eventseeker" message: @"To use all location servies. Please turn on Background App Refresh, go to Settings > General > Background App Refresh" preferredStyle: UIAlertControllerStyleAlert];
UIAlertAction *showMsgAlertControllerOkAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault
handler:nil];
[showMsgAlertController addAction:showMsgAlertControllerOkAction];
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:showMsgAlertController animated:YES completion:nil];
});
/////////
} else if ([[UIApplication sharedApplication] backgroundRefreshStatus] == UIBackgroundRefreshStatusRestricted) {
///////// delete this alert view if no need
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.locationShareModel.afterResume = YES;
[self.locationShareModel startMonitoringLocation];
[self.locationShareModel addResumeLocationToPList];
}
}
}
最后但并非最不重要的一点是不要忘记在 plist 中添加始终访问位置的权限。当应用程序被强制退出时,这是访问它的非常重要的一步。用户必须选择始终使用位置访问,并且必须关闭省电模式,因为它会禁用 后台刷新模式。
请记住,此应用的位置始终访问和后台刷新模式必须打开才能访问处于终止状态的位置。
对于离线模式我没有测试它。但我认为它有效。