You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的回调获取位置,它会监听位置变化,一旦位置服务恢复就会返回结果。


原问题代码的关键错误修正

  1. 逻辑错误:onStartJob中if (mFusedLocationClient != null) return false写反,应改为if (mFusedLocationClient == null) return false。
  2. 参数缺失:mLocationRequestBuilder?.setIntervalMillis()和setMinUpdateIntervalMillis()未传入具体数值,导致位置请求配置无效。
  3. 依赖getCurrentLocation:Doze模式下该方法无法获取实时位置,应改用回调方式。

内容的提问来源于stack exchange,提问作者Falguni Rana

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 00:09:54