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

Android应用被杀死后WorkManager启动前台服务仍无法获取定位

问题根因与修复方案

1. 缺少后台定位必要权限

Android 10(API 29)及以上系统,应用退到后台后获取定位必须申请ACCESS_BACKGROUND_LOCATION权限,你当前的权限申请逻辑只申请了前台定位权限,应用被划走后属于后台场景,自然无法拿到定位结果。

  • 修复步骤:
    • 先在Manifest中添加权限声明:
      <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
      <!-- Android 12 及以上还需要添加前台服务定位权限 -->
      <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
      
    • 修改权限申请逻辑,先申请前台定位权限,用户同意后再引导用户授予后台定位权限:
      override fun onRequestPermissionsResult(
          requestCode: Int,
          permissions: Array<out String>,
          grantResults: IntArray
      ) {
          super.onRequestPermissionsResult(requestCode, permissions, grantResults)
          when(requestCode) {
              1 -> { // 前台定位权限申请结果
                  if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
                      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                          // 申请后台定位权限
                          ActivityCompat.requestPermissions(
                              this,
                              arrayOf(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION),
                              2
                          )
                      } else {
                          // 低版本直接初始化WorkManager
                          initLocationWorker()
                      }
                  }
              }
              2 -> { // 后台定位权限申请结果
                  if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED) {
                      initLocationWorker()
                  }
              }
          }
      }
      
      private fun initLocationWorker() {
          val request =
              PeriodicWorkRequestBuilder<GeoLocationWorker>(15, TimeUnit.MINUTES)
                  .build()
      
          WorkManager
              .getInstance(this)
              .enqueueUniquePeriodicWork(
                  WorkerConfig.LOCATION_WORKER,
                  ExistingPeriodicWorkPolicy.KEEP,
                  request
              )
          finish()
          startActivity(Intent(this, HomeActivity::class.java))
      }
      

2. Worker执行逻辑错误

你在启动前台服务后直接返回Result.success(),WorkManager会认为任务已经执行完成,会降低应用进程优先级,甚至直接杀掉进程,此时定位请求还没返回结果,自然拿不到回调。

  • 修复方案:将Worker改为CoroutineWorker,等待定位结果返回后再结束Worker,或者用ListenableWorker的异步回调机制:
    class GeoLocationWorker(context: Context, workerParameters: WorkerParameters) :
        CoroutineWorker(context, workerParameters) {
    
        override suspend fun doWork(): Result {
            Log.i("GLW", "location lookup start")
            // 启动前台服务
            applicationContext.startForegroundService(Intent(applicationContext, GeoService::class.java))
            // 等待定位结果返回,最多等待30秒
            var retryCount = 0
            while (retryCount < 30) {
                delay(1000)
                // 可以通过SharedPreference或者广播接收定位结果的标记,确认定位完成后再返回
                if (getLocationResultFlag()) {
                    break
                }
                retryCount++
            }
            return Result.success()
        }
    }
    

3. 定位Provider逻辑不完善

你直接固定使用GPS_PROVIDER,如果设备在室内没有GPS信号,会直接返回null。

  • 修复方案:优先使用可用的Provider,或者同时请求多个Provider的定位结果,取可用的:
    // 先判断可用的Provider
    val providers = locationManager.getProviders(true)
    val locationProvider = when {
        providers.contains(LocationManager.GPS_PROVIDER) -> LocationManager.GPS_PROVIDER
        providers.contains(LocationManager.NETWORK_PROVIDER) -> LocationManager.NETWORK_PROVIDER
        else -> {
            Log.e("location", "no available location provider")
            stopForeground(true)
            stopSelf()
            return
        }
    }
    // 增加超时机制,避免一直等待
    val executor = Executors.newSingleThreadExecutor()
    locationManager.getCurrentLocation(
        locationProvider,
        null,
        executor,
        { location ->
            if (location == null) {
                // 尝试用getLastKnownLocation兜底
                val lastKnown = locationManager.getLastKnownLocation(locationProvider)
                lastKnown?.let {
                    Log.i("location", "last known location: $it")
                } ?: Log.e("location", "location == null")
            } else {
                Log.i("location", location.toString())
            }
            // 保存定位结果标记,通知Worker任务完成
            saveLocationResultFlag()
            stopForeground(true)
            stopSelf()
        })
    // 10秒超时后直接结束
    Handler(Looper.getMainLooper()).postDelayed({
        executor.shutdownNow()
        stopForeground(true)
        stopSelf()
    }, 10000)
    

4. 服务逻辑优化

你当前的GeoService没有主动调用stopSelf(),即使定位完成后服务还会在后台运行,容易被系统杀掉,也不符合后台权限规范,定位完成后一定要主动调用stopSelf()销毁服务。

额外说明

如果是国内定制ROM,还需要引导用户开启应用的后台活动权限、自启动权限,关闭省电优化,否则系统会限制应用后台运行,导致定位失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 05:09:00