Android定时Worker获取当前定位始终返回null的问题求助
问题:WorkManager中单次获取定位始终返回null的解决方法
问题背景
我有一个每10分钟运行一次的WorkManager Worker,每次启动需要获取当前经纬度,先后尝试了两种实现方式,但location始终返回null:
初始实现代码
private var fusedLocationClient: FusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(applicationContext) override fun doWork(): Result { val priority = Priority.PRIORITY_HIGH_ACCURACY val cancellationTokenSource = CancellationTokenSource() fusedLocationClient.getCurrentLocation(priority, cancellationTokenSource.token) .addOnSuccessListener { location -> Log.e("Location", "location is found: $location") curLat = location.latitude curLong = location.longitude } .addOnFailureListener { exception -> Log.e("Location", "Oops location failed with exception: $exception") } }
后续尝试(按建议使用CurrentLocationRequest)
val currentBuilder = CurrentLocationRequest.Builder().setDurationMillis(30000) val currentLocationRequest = currentBuilder.build() fusedLocationClient.getCurrentLocation(currentLocationRequest, null) .addOnSuccessListener { location -> Log.e("Location", "location is found: $location") curLat = location.latitude curLong = location.longitude } .addOnFailureListener { exception -> Log.e("Location", "Oops location failed with exception: $exception") }
已申请权限:android.permission.ACCESS_COARSE_LOCATION、android.permission.ACCESS_FINE_LOCATION和ACCESS_BACKGROUND_LOCATION,但仍无法获取有效定位。已知定位子系统未开启会返回null,但我仅需Worker启动时获取一次定位,不清楚如何正确"请求更新",且Worker没有onCreate方法,LocationRequest.create()已废弃,不知如何初始化定位服务。
解决方案
1. 先检查定位服务状态
在尝试获取定位前,必须确认设备的定位子系统已开启,否则无论如何都无法获取位置:
val locationManager = applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) val isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) if (!isGpsEnabled && !isNetworkEnabled) { Log.e("Location", "定位服务未开启,无法获取位置") return Result.failure() }
2. 结合缓存定位+单次主动更新
getCurrentLocation在无缓存定位且定位子系统未活跃时会返回null,因此建议先尝试获取缓存的最后位置,若缓存为空,再发起单次定位请求:
完整CoroutineWorker实现
class LocationWorker(appContext: Context, params: WorkerParameters) : CoroutineWorker(appContext, params) { private val fusedLocationClient: FusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(appContext) private var locationCallback: LocationCallback? = null override suspend fun doWork(): Result { // 检查定位服务状态 val locationManager = applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) val isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) if (!isGpsEnabled && !isNetworkEnabled) { Log.e("Location", "定位服务未开启") return Result.failure() } // 1. 尝试获取缓存的最后位置 val lastLocation = runCatching { fusedLocationClient.lastLocation.await() }.getOrNull() lastLocation?.let { Log.e("Location", "从缓存获取定位: $it") curLat = it.latitude curLong = it.longitude return Result.success() } // 2. 缓存为空,发起单次定位请求 return withContext(Dispatchers.IO) { val resultDeferred = CompletableDeferred<Result>() locationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult) { locationResult.lastLocation?.let { location -> Log.e("Location", "获取到最新定位: $location") curLat = location.latitude curLong = location.longitude resultDeferred.complete(Result.success()) } ?: run { Log.e("Location", "定位请求成功但未获取到位置数据") resultDeferred.complete(Result.failure()) } // 移除回调,避免内存泄漏 fusedLocationClient.removeLocationUpdates(this) } override fun onLocationAvailability(locationAvailability: LocationAvailability) { if (!locationAvailability.isLocationAvailable) { Log.e("Location", "当前定位不可用") resultDeferred.complete(Result.failure()) fusedLocationClient.removeLocationUpdates(this) } } } // 构建单次定位请求(替代废弃的LocationRequest.create()) val locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 0) .setMinUpdateIntervalMillis(0) .setMaxUpdateDelayMillis(0) .setNumUpdates(1) // 仅请求一次定位更新 .build() try { fusedLocationClient.requestLocationUpdates( locationRequest, locationCallback, Looper.getMainLooper() ) } catch (e: SecurityException) { Log.e("Location", "定位权限异常: ${e.message}") resultDeferred.complete(Result.failure()) } resultDeferred.await() } } override fun onStopped() { super.onStopped() // Worker停止时移除定位回调,防止内存泄漏 locationCallback?.let { fusedLocationClient.removeLocationUpdates(it) } } companion object { var curLat = 0.0 var curLong = 0.0 } }
3. 确保Worker在后台能正常执行
部分厂商的后台限制(如Doze模式)可能影响定位服务,建议给Worker设置setExpedited,让它在临时唤醒窗口中执行:
val periodicWorkRequest = PeriodicWorkRequestBuilder<LocationWorker>(10, TimeUnit.MINUTES) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .build() WorkManager.getInstance(applicationContext).enqueue(periodicWorkRequest)
内容的提问来源于stack exchange,提问作者Peter Chikov
相关产品推荐
相关产品推荐

