重要位置变化不会在设备上触发

8
我正在使用重大位置变化监控。当我在模拟器中运行代码并检查高速公路选项时,我会定期收到位置更新。我抓取这些更新并将它们保存在NSUserDefaults中,然后每10秒更新tableview,以便查看是否有任何更新。如果我在真实设备上运行应用程序,则不会收到任何更新。我把手机放在口袋里走了80公里,去了2个城市。没有更新。不确定是否弄错了什么。我附上了代码。这段代码是复制粘贴的,请随意测试。只需确保在故事板中使用TableViewController并将单元格ID设置为ID。我错过了什么?我正在iPhone 5上进行测试。

info.plist:

enter image description here

编辑:在苹果文档中找到了这个。我的locationManager需要以不同的方式创建吗?

当应用程序因位置更新而重新启动时,传递给你的 application:willFinishLaunchingWithOptions: 或 application:didFinishLaunchingWithOptions: 方法的启动选项字典包含 UIApplicationLaunchOptionsLocationKey 键。该键的存在表示新位置数据正在等待传递到您的应用程序。 要获取该数据,您必须创建一个新的 CLLocationManager 对象,并重新启动您之前运行的位置服务。 当您重新启动这些服务时,位置管理器将向其委托传递所有未决位置更新。

编辑2:

根据此,位置应至少每15分钟更新一次。我的代码中有错误。

如果您的应用程序不需要 GPS 级别的精度,并且您不需要 持续跟踪,则可以使用重大更改位置服务。关键是要正确使用重大更改位置服务, 因为它会在至少每15分钟唤醒系统和您的应用程序,即使没有发生位置更改, 并且它会持续运行直到您停止它。

编辑3:将以下代码添加到AppDelegate的didFinishLaunchingWithOptions:中,以查看应用程序是否被唤醒。它没有被唤醒-我在表视图中看不到200 200条目。有些可疑的事情正在发生。

 if let options = launchOptions {
            print("options")
            if (launchOptions![UIApplicationLaunchOptionsLocationKey] != nil){
                locationManager.startUpdatingLocation()
                self.lat.append(Double(200))
                self.lon.append(Double(200))
                self.times.append(NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .NoStyle, timeStyle: .ShortStyle))
                NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
                NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
                NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")
            }

代码:

//AppDelegate:
import UIKit
import CoreLocation

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {

    var lat:[CLLocationDegrees]!
    var lon:[CLLocationDegrees]!
    var times:[String]!
    var distances: [String]!

    var window: UIWindow?
    var locationManager: CLLocationManager!


    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

        locationManager=CLLocationManager()
        locationManager.delegate=self
        locationManager.requestAlwaysAuthorization()

        let isFirstLaunch = NSUserDefaults.standardUserDefaults().objectForKey("lat")
        if isFirstLaunch == nil{
            lat = [CLLocationDegrees]()
            lon = [CLLocationDegrees]()
            times = [String]()
            NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
            NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
            NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")
        }else{
            lat = NSUserDefaults.standardUserDefaults().arrayForKey("lat") as! [CLLocationDegrees]
            lon = NSUserDefaults.standardUserDefaults().arrayForKey("lon") as! [CLLocationDegrees]
            times = NSUserDefaults.standardUserDefaults().objectForKey("time") as! [String]
//            distances = NSUserDefaults.standardUserDefaults().objectForKey("distance") as! [String]

        }  
        return true
    }



    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        print("location updated")

        self.lat.append(locations[0].coordinate.latitude)
        self.lon.append(locations[0].coordinate.longitude)
        self.times.append(NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .NoStyle, timeStyle: .ShortStyle))

        NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
        NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
        NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")

        print("Location: \(locations[0].coordinate.latitude)   \(locations[0].coordinate.longitude)")
    }

    func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
        print("did change AS")
        switch status {
        case .AuthorizedWhenInUse:
            locationManager.startMonitoringSignificantLocationChanges()
        case .AuthorizedAlways:
            print("to always")
            locationManager.startMonitoringSignificantLocationChanges()
            if lat.count==0{
                self.lat.append((locationManager.location?.coordinate.latitude)!)
                self.lon.append((locationManager.location?.coordinate.longitude)!)

                self.times.append(NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .NoStyle, timeStyle: .ShortStyle))

                NSUserDefaults.standardUserDefaults().setObject(lat, forKey: "lat")
                NSUserDefaults.standardUserDefaults().setObject(lon, forKey: "lon")
                NSUserDefaults.standardUserDefaults().setObject(times, forKey: "time")


            }

//            locationManager.startUpdatingLocation()
            break
        default:
            locationManager.stopMonitoringSignificantLocationChanges()
            break
        }
    }
}

// 视图控制器

import UIKit

class TableViewController: UITableViewController {

    let appDel = UIApplication.sharedApplication().delegate as! AppDelegate

    override func viewDidLoad() {
        super.viewDidLoad()

        NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: "updateTableView", userInfo: nil, repeats: true)
    }



    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return appDel.lon.count
    }


    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("ID", forIndexPath: indexPath)
        cell.textLabel?.text = "\(appDel.lat[indexPath.row])   \(appDel.lon[indexPath.row])    \(appDel.times[indexPath.row])"
        cell.textLabel?.font = UIFont.systemFontOfSize(9)


        return cell
    }

    func updateTableView(){
    self.tableView.reloadData()
    }
}

你是否真的看到授权请求?如果没有,你的 plist 文件中是否有 NSLocationAlwaysUsageDescription? - Airsource Ltd
是的,请求弹出了。而且,在 info.plist 中设置了 NSLocationAlwaysUsageDescription 字符串。 - potato
你是否通过双击主屏幕并向上滑动应用程序来关闭它?如果你用这种方式关闭应用程序,它将无法运行后台的重要变更服务。 - Rob
是的,我正在做那个。我认为大多数用户也经常这样做。有更好的暂停应用程序的方法吗? - potato
不,那并不是暂停应用程序,而是完全终止并防止进一步的后台交互。不要杀死应用程序,只需轻触主屏幕按钮一次并转到另一个应用程序,而不是关闭其他应用程序。 - Rob
2个回答

15

代码存在以下几个问题:

  1. iOS启动应用程序时未开始位置更新。
  2. 未请求后台位置更新。
  3. 代码依赖于授权状态的更改来启动位置更新。
  4. didChangeAuthorizationStatus方法中缺少break语句。

以下是相关文件:

第一个文档说明,当iOS启动应用程序时,必须完全配置位置管理器对象,以便将位置事件传递给应用程序:

重新启动时,您仍然必须配置位置管理器对象并调用此方法[startMonitoringSignificantLocationChanges]以继续接收位置事件。

这是我通常的做法,以确保位置管理器完全启动,无论是通过图标还是通过iOS启动的(抱歉我使用ObjectiveC)。

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

  NSLog(@"app launching");
  bgTask = UIBackgroundTaskInvalid;

  locationMgr = [[CLLocationManager alloc] init];
  [locationMgr setDelegate:self];
  if([locationMgr respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)])
    [locationMgr setAllowsBackgroundLocationUpdates:YES];
  CLAuthorizationStatus authorizationStatus= [CLLocationManager authorizationStatus];

  if([launchOptions valueForKey:UIApplicationLaunchOptionsLocationKey] != nil) {
    NSLog(@"relaunching because of significant location change - restarting SLC");
    [locationMgr startMonitoringSignificantLocationChanges];
  }
  else
  {
    if (authorizationStatus == kCLAuthorizationStatusAuthorizedAlways) {
        NSLog(@"launching with authorization to always use location - starting SLC");
        [locationMgr startMonitoringSignificantLocationChanges];
    }
    else
    {
        NSLog(@"launching with no authorization to always use location - requesting authorization");
        if([locationMgr respondsToSelector:@selector(requestAlwaysAuthorization)])
            [locationMgr requestAlwaysAuthorization];
    }
  }

  return YES;
}

注意调用setAllowsBackgroundLocationUpdates。这是iOS9中的新功能,每次应用启动时都必须执行,如果您想在后台接收位置更新。(就像是“我不是开玩笑的,我知道我在后台模式中要求它们,但我真的想要它们。”)。

如果应用程序希望在挂起时接收位置更新,则必须在其应用程序的Info.plist文件中包含UIBackgroundModes键(具有位置值),并将此属性的值设置为YES。

最后,您不能依赖于didChangeAuthorizationStatus被调用。如果您拥有授权kCLAuthorizationStatusAuthorizedAlways,则调用[locationMgr requestAlwaysAuthorization]不会导致授权状态发生更改,因此也不会调用didChangeAuthorizationStatus

如果在调用requestWhenInUseAuthorization或requestAlwaysAuthorization方法时已经知道授权状态,则位置管理器不会向该方法报告当前授权状态。


没问题 - 苹果公司在每个iOS版本的发布中都逐渐使CoreLocation更难以正确使用。我认为它在iOS6左右是完美的,现在它只是晦涩难懂。祝你的项目好运。 - BitByteDog
1
无可厚非。另外,我已经玩了几天了。苹果宣传称,即使实际位置没有变化,重要位置也会每15分钟至少触发一次。如果我不动,我就不会收到任何更新。你是否看到应用程序每15分钟更新一次位置,即使你没有移动? - potato
我只看到它在单元格更改时触发,拆下SIM卡,你将永远不会被唤醒。我认为15分钟是指手机不会锁定在单个基站上并停留在那里,因为移动蜂窝网络在衰落的射频环境中运行。这意味着先前强的信号随时间变得更弱。手机更换基站的速率取决于RF衰落的速率和相邻基站使用的功率差阈值参数的速率。 - BitByteDog

0

我认为你需要无论如何开始更新位置信息。你尝试过插入断点来查看你到达了哪里吗?


如果我将startUpdatingLocation设置为true,我每秒钟会收到3个更新。我认为这只适用于标准位置服务。我还没有尝试过,因为在模拟器上运行得非常好。 - potato
在后台挂起的应用程序上是否可以使用断点? - potato
@krompir2 - 不行,在这种情况下你不能使用断点。我通常使用NSLog消息(这样我就可以查看控制台)。我还发现将这些消息记录到某些持久性存储中,然后在将应用程序带到前景时,在表格视图或类似的东西中显示这些保存的消息非常有用,这样我就可以诊断出应用程序在不在前景时发生了什么。 - Rob
您可以在挂起或未运行的进程上设置断点。 - BitByteDog
1
如果进程被暂停,在Xcode中单击“调试”菜单,然后选择“附加到进程”-选择您的进程。现在您已经附加了进程,当进程运行时,如果它遇到断点,它将停止。如果进程未运行,在Xcode中单击“调试”菜单,然后选择“按PID或名称附加到进程...”。输入您的进程名称。单击“附加”。调试器将在启动时因为一个SLC而附加到您的进程上。 - BitByteDog

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接