代码改变世界

app 后台持续定位

2016-11-04 10:54  PingKang  阅读(5424)  评论(1编辑  收藏  举报

现在几乎所有的app都会使用GPS 定位,但是只有少数的app 会要求在应用退到后台以后,还要求持续定时定位的,此类app一般只有导航类app才会使用,且审核不会被拒!

当然,以上问题都可以和苹果官方进行沟通解决。

现在对定位的代码进行说明:

info文件中要增加以下字段:

NSLocationAlwaysUsageDescription

此key配合权限获取

[self.locationManager requestAlwaysAuthorization];

以上两行必不可少 ,否则会出现应用退到后台以后,只有定时器在执行 ,定位获取不到的问题(既不走成功,也不走失败的回调)

以下为定位的代码:此类中包含了定时器

//
//  AutoLocatedModel.m
//  JHAutoLocated
//
//  Created by pk on 2016/11/2.
//  Copyright © 2016年 pk. All rights reserved.
//

#import "AutoLocatedModel.h"

#import <UIKit/UIKit.h>

@interface AutoLocatedModel ()<CLLocationManagerDelegate>



@property (nonatomic, strong) NSTimer *timer;

@end

@implementation AutoLocatedModel

+ (instancetype)shareInstance{
    static dispatch_once_t onceToken;
    static AutoLocatedModel * model = nil;
    dispatch_once(&onceToken, ^{
        model = [[AutoLocatedModel alloc] init];
        model.locationManager = [[CLLocationManager alloc] init];
        model.locationManager.delegate = model;
        
        model.locationManager.desiredAccuracy=kCLLocationAccuracyBest;//定位精确度
        model.locationManager.distanceFilter = kCLDistanceFilterNone;//任何运动均接受,任何运动将会触发定位更新
        if ([UIDevice currentDevice].systemVersion.floatValue >= 9.0) {
            model.locationManager.allowsBackgroundLocationUpdates = YES;
        }

        [model createTimer];
        [model startLocated];
    });
    return model;
}

#pragma mark - 定时器相关
- (void)createTimer{
    //触发计时器
    [self deleteTimer];
    self.timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(startLocated) userInfo:nil repeats:YES];
}

- (void)deleteTimer{
    if ([self.timer isValid]) {
        [self.timer invalidate];
        self.timer =nil;
    }
}

- (void)startLocated{
    
    [self.locationManager requestAlwaysAuthorization];
    [self.locationManager requestWhenInUseAuthorization];
    self.locationManager.pausesLocationUpdatesAutomatically = NO;
    // 判断定位操作是否被允许
    if([CLLocationManager locationServicesEnabled]) {
        // 开始定位
        [self.locationManager stopUpdatingLocation];
        [self.locationManager startUpdatingLocation];
        NSLog(@"开始执行定位");
        NSLog(@"locationManager = %@",self.locationManager);
    }else {
        //提示用户无法进行定位操作
        NSLog(@"定位关闭");
    }
    
}

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{
    if (status == kCLAuthorizationStatusNotDetermined || status == kCLAuthorizationStatusDenied) {
        NSLog(@"暂未获取到权限");
    }else{
        [manager startUpdatingLocation];
    }
}

-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
    [manager stopUpdatingLocation];
    //此处locations存储了持续更新的位置坐标值,取最后一个值为最新位置,如果不想让其持续更新位置,则在此方法中获取到一个值之后让locationManager stopUpdatingLocation
    CLLocation *currentLocation = [locations lastObject];
    
    CLLocationCoordinate2D coor = currentLocation.coordinate;

    NSLog(@"经纬度 = latitude: %f longitude:%f ",coor.latitude,coor.longitude);
//    [self.locationManager stopUpdatingLocation];
    
    [self createTimer];
    
    //反向地理编码
//    CLGeocoder *clGeoCoder = [[CLGeocoder alloc] init];
//    CLLocation *cl = [[CLLocation alloc] initWithLatitude:coor.latitude longitude:coor.longitude];
//    
//    [clGeoCoder reverseGeocodeLocation:cl completionHandler: ^(NSArray *placemarks,NSError *error) {
//             for (CLPlacemark *placeMark in placemarks) {
//                NSDictionary *addressDic = placeMark.addressDictionary;
//                NSString *state=[addressDic objectForKey:@"State"];
//                NSString *city=[addressDic objectForKey:@"City"];
//                NSString *subLocality=[addressDic objectForKey:@"SubLocality"];
//                NSString *street=[addressDic objectForKey:@"Street"];
//
//                NSLog(@"所在城市====%@ %@ %@ %@", state, city, subLocality, street);
//                 break;
//             }
//     
//    }];
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    NSLog(@"获取地址失败 = %@",error);
    if (error.code == kCLErrorDenied) {
        // 提示用户出错原因,可按住Option键点击 KCLErrorDenied的查看更多出错信息,可打印error.code值查找原因所在
    }
}



@end

另外我们在要appdelegate 中编写进入后台的方法

//
//  AppDelegate.m
//  JHAutoLocated
//
//  Created by pk on 2016/11/2.
//  Copyright © 2016年 pk. All rights reserved.
//

#import "AppDelegate.h"
#import "AutoLocatedModel.h"

@interface AppDelegate ()
{
    UIBackgroundTaskIdentifier backgroundTaskIdentifier;
    UIBackgroundTaskIdentifier oldBackgroundTaskIdentifier;

}

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

- (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.
    
    NSLog(@"进入后台,要开始后台任务");
    
    [application endBackgroundTask:oldBackgroundTaskIdentifier];
    oldBackgroundTaskIdentifier = backgroundTaskIdentifier;
    
    if ([[UIDevice currentDevice] isMultitaskingSupported] == YES) {
        //TODO: 缺少用户登录判断
        backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
            AutoLocatedModel * autoLocate = [AutoLocatedModel shareInstance];
            if([CLLocationManager significantLocationChangeMonitoringAvailable]) {
                 [autoLocate.locationManager startMonitoringSignificantLocationChanges];
            }
            [autoLocate startLocated];
            
        }];
    }
   
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
    
}

- (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.
    NSLog(@"被激活了");
    //TODO: 缺少用户登录判断
    AutoLocatedModel * autoLocate = [AutoLocatedModel shareInstance];
    [autoLocate startLocated];
    
    
}

- (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

@end

获取到定位以后,可以将位置上传到服务器 ,此处还有很多可以优化的地方 ,例如:可以利用两次定位的距离来判断 只有距离大于多少时进行上传 ,已保证服务器减少垃圾数据等