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
相关产品推荐
相关产品推荐

