Android Kotlin:三星、小米等OEM设备Doze模式下Location获取为null
问题解决:Doze模式下OEM设备获取位置返回null的问题
核心原因
- Doze模式会限制后台应用的位置服务、网络等资源访问,三星、小米、华为等OEM厂商的自定义系统会进一步强化这种限制,导致
getCurrentLocation()无法获取位置。 JobScheduler在Doze的维护窗口外不会触发任务,即使触发,位置服务也可能处于休眠状态。- 原代码中
getCurrentLocation()依赖实时位置更新,但Doze下位置服务被暂停,因此返回null;同时requestLocationUpdates的回调也无法被触发。
解决方案
1. 改用前台服务提升优先级
前台服务拥有更高的系统优先级,能在Doze模式下正常获取位置。在LocationJob的onStartJob中启动前台服务:
override fun onStartJob(params: JobParameters?): Boolean { Log.e(TAG, "onStartJob") if (mFusedLocationClient == null) return false // 修正原代码逻辑错误 // 启动前台服务 val notification = NotificationCompat.Builder(this, "LOCATION_CHANNEL") .setContentTitle("位置更新中") .setContentText("正在获取当前位置") .setSmallIcon(R.drawable.ic_location) .build() startForeground(1001, notification) workRunnable = Runnable { try { mFusedLocationClient!!.requestLocationUpdates( mLocationRequest!!, mLocationCallback!!, handlerThread!!.looper ) // 移除getCurrentLocation的延迟调用,依赖callback获取位置 } catch (unlikely: SecurityException) { Log.e(TAG, "Lost location permission. Could not request updates. ") jobFinished(params, true) } } workHandler.post(workRunnable!!) return true }
同时需要在Manifest中声明前台服务权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
2. 确保获取必要的位置权限
除常规位置权限外,需申请后台位置权限(Android 10+),并引导用户开启:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
在代码中动态申请这些权限,尤其是后台位置权限,需用户手动授权。
3. 优化LocationRequest配置
确保位置请求参数适配Doze模式,设置高优先级和合理更新间隔:
if (mLocationRequest == null) { mLocationRequestBuilder = LocationRequest.Builder( Priority.PRIORITY_HIGH_ACCURACY, 30 * 1000 // 30秒间隔,按需调整 ) mLocationRequestBuilder?.setMinUpdateIntervalMillis(15 * 1000) // 最小更新间隔 mLocationRequestBuilder?.setWaitForAccurateLocation(true) // 可选:等待高精度位置 mLocationRequest = mLocationRequestBuilder!!.build() }
4. 引导用户加入电池优化白名单
OEM厂商的电池优化会严格限制后台应用,需引导用户将应用加入白名单:
fun checkBatteryOptimization() { val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager if (!powerManager.isIgnoringBatteryOptimizations(packageName)) { val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply { data = Uri.parse("package:$packageName") } startActivity(intent) } }
5. 替换JobScheduler为WorkManager
WorkManager是Jetpack组件,专门适配Doze模式和OEM限制,推荐替代JobScheduler:
// 定义Worker类 class LocationWorker(context: Context, params: WorkerParameters) : Worker(context, params) { private val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context) private val locationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult) { locationResult.lastLocation?.let { Log.e("LocationWorker", "获取到位置:${it.latitude},${it.longitude}") } ?: Log.e("LocationWorker", "位置为空") } } override fun doWork(): Result { val locationRequest = LocationRequest.Builder( Priority.PRIORITY_HIGH_ACCURACY, 30 * 1000 ).build() try { fusedLocationClient.requestLocationUpdates( locationRequest, locationCallback, Looper.getMainLooper() ) // 延迟停止更新,确保获取到位置 Handler(Looper.getMainLooper()).postDelayed({ fusedLocationClient.removeLocationUpdates(locationCallback) }, 10 * 1000) } catch (e: SecurityException) { return Result.failure() } return Result.success() } } // 启动周期性任务 fun startPeriodicLocationTask() { val workRequest = PeriodicWorkRequestBuilder<LocationWorker>(15, TimeUnit.MINUTES) .build() WorkManager.getInstance(this).enqueueUniquePeriodicWork( "LOCATION_TASK", ExistingPeriodicWorkPolicy.REPLACE, workRequest ) }
6. 避免依赖getCurrentLocation
getCurrentLocation()在Doze下容易返回null,建议直接依赖requestLocationUpdates的回调获取位置,它会监听位置变化,一旦位置服务恢复就会返回结果。
原问题代码的关键错误修正
- 逻辑错误:
onStartJob中if (mFusedLocationClient != null) return false写反,应改为if (mFusedLocationClient == null) return false。 - 参数缺失:
mLocationRequestBuilder?.setIntervalMillis()和setMinUpdateIntervalMillis()未传入具体数值,导致位置请求配置无效。 - 依赖getCurrentLocation:Doze模式下该方法无法获取实时位置,应改用回调方式。
内容的提问来源于stack exchange,提问作者Falguni Rana
相关产品推荐
相关产品推荐

