Android Kotlin:OEM设备Doze模式下获取Location为null求助
问题:Doze模式下FusedLocationProviderClient无法获取位置(多OEM设备)
- 使用
FusedLocationProviderClient结合JobScheduler持续获取位置,后台运行正常,但进入Doze模式后getCurrentLocation()返回null,LocationCallback的onLocationResult()也不再触发 - 问题出现在三星、小米、POCO、摩托罗拉、华为等OEM设备
- 三星设备进入Doze模式时的日志:
RequestManager_FLP com.sec.location.nsflp2 E [LocationManager] Location request 2C7EC95F(Listener) gps interval=0ms (min=0ms) from com.google.android.gms.persistent[WorkSource com....] (10200_FINE_fg_svc_true_foreground)
- FakeGPS、Google Maps、Uber Driver、Life360等应用在Doze模式下可正常获取位置
相关代码
LocationJob.kt
class LocationJob : JobService() { private var mLocationRequest: LocationRequest? = null private var mLocationRequestBuilder: LocationRequest.Builder? = null private var mFusedLocationClient: FusedLocationProviderClient? = null private var mLocationCallback: LocationCallback? = null private val workHandler: Handler = Handler(Looper.getMainLooper()) var workRunnable: Runnable? = null var handlerThread: HandlerThread? = null override fun onCreate() { super.onCreate() // TODO: Start a background thread to receive location result. handlerThread = HandlerThread("RequestLocation"); handlerThread!!.start(); mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this) mLocationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult) { //Note: this is not called in case of Doze mode in OEM device. if (locationResult.lastLocation == null) { Log.e(TAG, "Location missing in callback.") } else { Log.e(TAG, "Location callback. ${locationResult.lastLocation}") } super.onLocationResult(locationResult) } override fun onLocationAvailability(locationAvailability: LocationAvailability) { super.onLocationAvailability(locationAvailability) } } if (mLocationRequest == null) { if (mLocationRequestBuilder == null){ mLocationRequestBuilder = LocationRequest.Builder( Priority.PRIORITY_HIGH_ACCURACY, sessionManager?.moveOnTripInterval.makeLong() ) } mLocationRequestBuilder?.setIntervalMillis() mLocationRequestBuilder?.setMinUpdateIntervalMillis() mLocationRequest = mLocationRequestBuilder!!.build() } } override fun onStartJob(params: JobParameters?): Boolean { Log.e(TAG, "onStartJob") if (mFusedLocationClient != null) return false workRunnable = Runnable { try { mFusedLocationClient!!.requestLocationUpdates( mLocationRequest!!, mLocationCallback!!, handlerThread!!.looper ) Handler(Looper.getMainLooper()).postDelayed({ mFusedLocationClient!!.getCurrentLocation(PRIORITY_HIGH_ACCURACY, null) .addOnSuccessListener { Log.e("Loc: $it") if (it != null) { val loc = it println("$TAG: ${loc.latitude},${loc.longitude}") } else { Log.e("Loc: is null") } }.addOnFailureListener { println("cancel") } }, 10 * 1000) } catch (unlikely: SecurityException) { println("$TAG Lost location permission. Could not request updates. ") } jobFinished(params, true) } workHandler.post(workRunnable!!) return true } }
MainActivity.kt
class MainActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //trigger LocationJob service triggerJobService(true) } fun triggerJobService(start: Boolean) { try { val jobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler if (start) { val jobInfo = JobInfo.Builder(Constant.JOB_ID, ComponentName(this, LocationJob::class.java)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .setRequiresDeviceIdle(false).setRequiresCharging(false).setPersisted(true) // .setOverrideDeadline(3 * 60 * 1000) val job = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { jobInfo.setMinimumLatency(Constant.REFRESH_INTERVAL) .build() } else { jobInfo.setPeriodic(Constant.REFRESH_INTERVAL) .setMinimumLatency(1) .build() } //.setOverrideDeadline(3 * 60 * 1000) jobScheduler.schedule(job) } else { jobScheduler.cancel(Constant.JOB_ID) } } catch (e: Exception) { e.printStackTrace() return } } }
解决方案
1. 替换JobScheduler为WorkManager
JobScheduler在Doze模式下会被系统严格限制,WorkManager是Google官方推荐的后台任务管理方案,针对位置等特殊场景做了适配,支持Android 12+的精确周期性任务,能更好地绕过Doze限制。
2. 完善权限与位置请求配置
- 确保已申请
ACCESS_BACKGROUND_LOCATION权限(Android 10+),并在运行时动态申请 - 调整
LocationRequest配置:mLocationRequestBuilder = LocationRequest.Builder( Priority.PRIORITY_HIGH_ACCURACY, sessionManager?.moveOnTripInterval.makeLong() ) .setMinUpdateIntervalMillis(10000) // 设置最小更新间隔 .setWaitForAccurateLocation(true) // 等待高精度位置(可选)
3. 改用前台服务绑定位置更新
Doze模式下后台进程优先级极低,前台服务拥有更高的系统优先级:
- 创建前台服务,调用
startForeground()显示必要通知(系统强制要求) - 在前台服务中发起
requestLocationUpdates,保证位置回调能正常触发
4. 处理OEM厂商特殊限制
小米、华为、三星等厂商有自定义的后台管理机制,需引导用户手动配置:
- 跳转系统设置页面,让用户将应用加入“后台白名单”或“忽略电池优化”
- 使用
Intent(ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)快速跳转设置
5. 修正现有代码逻辑错误
onStartJob中if (mFusedLocationClient != null) return false逻辑错误,应改为if (mFusedLocationClient == null) return false,否则会直接终止任务- 调用
requestLocationUpdates后立即执行jobFinished(params, true)会导致JobService被销毁,无法接收回调,需在onLocationResult中获取到位置后再结束任务 getCurrentLocation依赖系统缓存,Doze模式下缓存可能失效,优先依赖LocationCallback的实时更新
关键代码调整示例
修改onStartJob逻辑:
override fun onStartJob(params: JobParameters?): Boolean { Log.e(TAG, "onStartJob") if (mFusedLocationClient == null) return false workRunnable = Runnable { try { mFusedLocationClient!!.requestLocationUpdates( mLocationRequest!!, object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult) { super.onLocationResult(locationResult) val location = locationResult.lastLocation if (location != null) { Log.e(TAG, "Location received: ${location.latitude},${location.longitude}") // 处理位置数据 } else { Log.e(TAG, "Location missing") } // 获取位置后停止更新并结束任务 mFusedLocationClient!!.removeLocationUpdates(this) jobFinished(params, true) } }, handlerThread!!.looper ) } catch (unlikely: SecurityException) { println("$TAG Lost location permission. Could not request updates. ") jobFinished(params, false) } } workHandler.post(workRunnable!!) return true }
内容的提问来源于stack exchange,提问作者falguni rana
相关产品推荐
相关产品推荐

