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

Android应用后台无法持续更新定位问题求助

问题描述

开发一款可在后台持续更新定位的Android应用时遇到问题:应用最小化后定位更新暂停,回到前台才恢复。以下是当前的配置与代码:

Manifest文件

<service android:name=".LocationService" android:foregroundServiceType="location" />
...
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<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" />

启动定位服务的Activity代码

val serviceIntent = Intent(applicationContext, LocationService::class.java).apply {
    action = LocationService.ACTION_START
    startService(this)
}

applicationContext.startService(serviceIntent)

LocationService代码

import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
import com.google.android.gms.location.LocationServices
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

class LocationService: Service() {

    private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
    private lateinit var locationClient: LocationClient

    override fun onBind(p0: Intent?): IBinder? {
        return null
    }

    override fun onCreate() {
        super.onCreate()
        locationClient = LocationClient(
            applicationContext,
            LocationServices.getFusedLocationProviderClient(applicationContext)
        )
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        when(intent?.action) {
            ACTION_START -> start()
            ACTION_STOP -> stop()
            ACTION_PAUSE -> pause()
            ACTION_REFRESH -> refresh()
        }
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        super.onDestroy()
        serviceScope.cancel()
    }

    private fun start() {    
        locationClient
            .getLocationUpdates(1000L)
            .catch { e -> e.printStackTrace() }
            .onEach { location ->
                Log.i("LOCATION", DistanceTracker.getInstance().fullDistance.toString())
                DistanceTracker.getInstance().addDistance(location)
            }
            .launchIn(serviceScope)
    }

    private fun stop() {
        stopSelf()
        DistanceTracker.getInstance().resetDistances()
        refresh()
    }

    private fun pause() {
        stopSelf()
        DistanceTracker.getInstance().clearLocation()
        refresh()
    }

    companion object {
        const val ACTION_START = "ACTION_START"
        const val ACTION_STOP = "ACTION_STOP"
        const val ACTION_PAUSE = "ACTION_PAUSE"
        const val ACTION_REFRESH = "ACTION_REFRESH"
    }
}

LocationClient代码

import android.annotation.SuppressLint
import android.content.Context
import android.location.Location
import android.location.LocationManager
import android.os.Looper
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationRequest.PRIORITY_HIGH_ACCURACY
import com.google.android.gms.location.LocationResult
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.launch

class LocationClient(
    private val context: Context,
    private val client: FusedLocationProviderClient
): ILocationClient {
    @SuppressLint("MissingPermission")
    override fun getLocationUpdates(interval: Long): Flow<Location> {
        val locationFlow = callbackFlow {
            if(!context.hasLocationPermission()) {
                throw ILocationClient.LocationException("Missing location permission")
            }

            val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
            val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
            val isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
            if(!isGpsEnabled && !isNetworkEnabled) {
                throw ILocationClient.LocationException("GPS is disabled")
            }

            val request = LocationRequest.create()
                .setInterval(interval)
                .setFastestInterval(interval)
                .setPriority(PRIORITY_HIGH_ACCURACY)

            val locationCallback = object : LocationCallback() {
                override fun onLocationResult(result: LocationResult) {
                    val location = result.locations.minWith(Comparator.comparingDouble{it.accuracy as Double})

                    if(location.accuracy < 50) {
                        super.onLocationResult(result)
                        result.locations.lastOrNull()?.let { location ->
                            launch { send(location) }
                        }
                    }
                }
            }

            client.requestLocationUpdates(
                request, locationCallback, Looper.getMainLooper())

            awaitClose {
                client.removeLocationUpdates(locationCallback)
            }
        }

        return locationFlow
    }
}

需求:无需将应用保持在前台,实现后台持续追踪行驶距离。请问遗漏了什么配置或代码?


解决方案

1. 启动前台服务并显示通知

Android 12+要求定位类后台服务必须以前台服务运行并显示通知,否则系统会限制定位频率甚至停止服务。修改LocationService的start()方法:

private fun start() {
    // 创建通知通道(Android 8.0+要求,可在Application初始化时创建)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel = NotificationChannel(
            "LOCATION_TRACKING_CHANNEL",
            "位置追踪",
            NotificationManager.IMPORTANCE_LOW
        ).apply {
            description = "后台定位服务通知"
        }
        val notificationManager = getSystemService(NotificationManager::class.java)
        notificationManager.createNotificationChannel(channel)
    }

    // 创建前台通知
    val notification = NotificationCompat.Builder(this, "LOCATION_TRACKING_CHANNEL")
        .setContentTitle("正在追踪位置")
        .setContentText("后台持续定位中")
        .setSmallIcon(R.drawable.ic_location) // 替换为你的应用图标
        .setPriority(NotificationCompat.PRIORITY_LOW)
        .build()

    // 启动前台服务,ID需唯一
    startForeground(1, notification)

    // 原有定位逻辑
    locationClient
        .getLocationUpdates(5000L)
        .catch { e -> e.printStackTrace() }
        .onEach { location ->
            Log.i("LOCATION", DistanceTracker.getInstance().fullDistance.toString())
            DistanceTracker.getInstance().addDistance(location)
        }
        .launchIn(serviceScope)
}

2. 调整LocationRequest参数

后台定位时,过短的间隔(1000ms)会被系统节流,建议设置合理的更新间隔,同时添加setWaitForAccurateLocation(false)避免等待高精度位置导致更新延迟:

val request = LocationRequest.create()
    .setInterval(5000L) // 5秒更新一次,可根据需求调整
    .setFastestInterval(2000L) // 最快2秒更新一次
    .setPriority(PRIORITY_HIGH_ACCURACY)
    .setWaitForAccurateLocation(false) // 不等待高精度位置,立即返回可用位置

3. 设置服务重启策略

修改onStartCommand的返回值为START_STICKY,确保系统杀死服务后能自动重启:

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    when(intent?.action) {
        ACTION_START -> start()
        ACTION_STOP -> stop()
        ACTION_PAUSE -> pause()
        ACTION_REFRESH -> refresh()
    }
    return START_STICKY // 服务被系统杀死后自动重启
}

4. 优化暂停逻辑

当前pause()方法调用stopSelf()会直接停止服务,若只是暂停定位,应改为移除位置更新而非停止服务:

// 在LocationClient中添加停止更新的方法
class LocationClient(...) : ILocationClient {
    private var locationCallback: LocationCallback? = null

    @SuppressLint("MissingPermission")
    override fun getLocationUpdates(interval: Long): Flow<Location> {
        return callbackFlow {
            // ... 原有代码
            this@LocationClient.locationCallback = locationCallback
            // ... 原有代码
        }
    }

    fun stopLocationUpdates() {
        locationCallback?.let { client.removeLocationUpdates(it) }
    }
}

// 修改LocationService的pause()方法
private fun pause() {
    locationClient.stopLocationUpdates() // 仅停止定位更新
    // stopSelf() // 移除这行,避免停止服务
    DistanceTracker.getInstance().clearLocation()
    refresh()
}

5. 确保后台权限已授予

确认用户已授予ACCESS_BACKGROUND_LOCATION权限,在请求权限时需明确申请:

// 权限请求示例(在Activity中调用)
private fun requestLocationPermissions() {
    val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        arrayOf(
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_BACKGROUND_LOCATION
        )
    } else {
        arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
    }
    requestPermissions(permissions, LOCATION_PERMISSION_REQUEST_CODE)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 10:52:31