iOS后台/杀死状态下N分钟间隔持续定位实现求助
iOS后台位置更新实现求助:后台/杀死状态下定时追踪上传位置
需求说明
希望每隔N分钟追踪用户位置并上传至服务器,支持应用后台及杀死状态。
已尝试操作
- 开启Background Modes中的Location Updates、Background Fetch、Background Processing;
- 创建单例类
EmployeeAttendanceTracker用于定位追踪,并在AppDelegate的didFinishLaunchingWithOptions中调用; - 尝试使用BackgroundTasks,但注册的后台任务未按指定间隔执行。
相关代码
EmployeeAttendanceTracker类代码
class EmployeeAttendanceTracker: NSObject,CLLocationManagerDelegate { static let shared = EmployeeAttendanceTracker() private let locationManager = CLLocationManager() private var lastLocationDate = Date() static let LOCATION_INTERVAL = 1 var locationUpdate: (() -> Void)? private override init() { super.init() setupLocationManager() } private func setupLocationManager() { locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest locationManager.activityType = .other locationManager.distanceFilter = kCLDistanceFilterNone locationManager.allowsBackgroundLocationUpdates = true locationManager.pausesLocationUpdatesAutomatically = false locationManager.showsBackgroundLocationIndicator = true if #available(iOS 9.0, *) { locationManager.requestAlwaysAuthorization() } else { locationManager.requestWhenInUseAuthorization() } } func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { switch status { case .restricted: Logger.s("Location access restricted.") case .denied: Logger.s("User denied location access.") case .notDetermined: Logger.s("Location access not determined.") case .authorizedAlways: if #available(iOS 9, *) { locationManager.requestLocation() } else { locationManager.startUpdatingLocation() locationManager.startMonitoringSignificantLocationChanges() } default: break } } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let location = locations.last else { return } Logger.s("User latitude: \(location.coordinate.latitude), longitude: \(location.coordinate.longitude)") locationManager.stopUpdatingLocation() let now = Date() if isItTime(now: now as NSDate) { if shouldSendLocationToServer() { self.sendLocationToServer(location: location,completion: { self.locationUpdate?() }) }else{ self.locationUpdate?() } Logger.s(now) Logger.s(location) }else{ self.locationUpdate?() } }
AppDelegate.swift相关代码
func scheduleAppRefresh() { let request = BGAppRefreshTaskRequest(identifier: "com.example.location_update") request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) // 15 minutes from now do { try BGTaskScheduler.shared.submit(request) Logger.s("BG TASK REQUEST SUBMITTED") } catch { print("Unable to submit task request: \(error)") } } func handleAppRefresh(task: BGAppRefreshTask) { task.expirationHandler = { Logger.s("BG TASK EXPIRED") task.setTaskCompleted(success: false) } Logger.s("HANDLE BG TASK REQUEST") EmployeeAttendanceTracker.shared.locationUpdate = { task.setTaskCompleted(success: true) } } func registerBackGroundTasks() { BGTaskScheduler.shared.register( forTaskWithIdentifier: "com.example.location_update", using: DispatchQueue.global() ) { task in self.handleAppRefresh(task: task as! BGAppRefreshTask) } }
解决方案分析
1. BackgroundTasks 不适合固定间隔的位置追踪
BGAppRefreshTask 的执行时间完全由系统调度,无法保证严格按指定间隔执行,系统会根据用户行为、设备电量等因素调整唤醒时机,甚至在低电量或用户长时间不使用应用时完全暂停任务。这是你遇到任务不按时执行的核心原因。
2. 正确的后台位置追踪方案
要实现后台(包括杀死状态)下的定时位置上传,需要结合Core Location 的后台定位能力,而非依赖 BackgroundTasks:
(1)调整 LocationManager 配置
当前配置存在几个关键问题:
- 调用
locationManager.stopUpdatingLocation()会停止定位,后续无法自动唤醒应用; requestLocation()仅单次获取位置,无法持续触发后台更新;- 未正确使用持续定位的API。
修改授权通过后的逻辑:
case .authorizedAlways: // 若需要高精度定位,使用此方法(电量消耗较高) locationManager.startUpdatingLocation() // 若接受低精度但低功耗,使用此方法(仅位置显著变化时唤醒) // locationManager.startMonitoringSignificantLocationChanges()
(2)实现定时上传逻辑
不要依赖系统任务调度,而是在每次获取位置时自行判断是否到达上传间隔:
- 新增
lastUploadDate变量记录上次上传时间; - 在
didUpdateLocations中对比当前时间与lastUploadDate,满足间隔则执行上传; - 上传成功后更新
lastUploadDate。
修改后的 didUpdateLocations 示例:
// 在类中新增变量 private var lastUploadDate = Date() func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let location = locations.last else { return } let now = Date() // 检查是否达到上传间隔(LOCATION_INTERVAL 为分钟数) if now.timeIntervalSince(lastUploadDate) >= Double(EmployeeAttendanceTracker.LOCATION_INTERVAL * 60) { sendLocationToServer(location: location) { [weak self] success in guard let self = self else { return } if success { self.lastUploadDate = now Logger.s("位置上传成功") } } } }
(3)处理应用杀死状态下的唤醒
应用被杀死后,只有以下场景能唤醒应用触发定位:
startMonitoringSignificantLocationChanges()检测到位置显著变化;- 开启区域监测(Geofencing)后,用户进入/离开指定区域;
- 导航类应用的特殊权限(普通应用无法依赖)。
若需要更频繁的唤醒,只能依赖 startUpdatingLocation(),但iOS会严格限制后台定位频率,尤其在应用被杀死后,仅在用户移动时触发少量更新。
3. 权限与配置注意事项
- 必须在Info.plist中添加
NSLocationAlwaysAndWhenInUseUsageDescription和NSLocationWhenInUseUsageDescription权限描述; - Background Modes仅需勾选Location Updates,Background Fetch和Background Processing对定位场景无帮助;
- 确保
allowsBackgroundLocationUpdates = true且pausesLocationUpdatesAutomatically = false; - iOS 13+ 需确保用户授予始终允许位置权限,否则后台定位会被完全禁用。
4. 电量优化建议
- 无需高精度时,将
desiredAccuracy设为kCLLocationAccuracyKilometer或更低; - 设置
distanceFilter为合理值,避免频繁触发定位; - 正确设置
activityType(如.automotiveNavigation),让系统优化定位策略。
内容的提问来源于stack exchange,提问作者Android Geek
相关产品推荐
相关产品推荐

