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

前台服务被Doze模式终止,无法持续上传位置求助

问题描述

我在应用进入后台时启动前台服务,每10秒向服务器发送一次经纬度。但该服务仅在应用处于前台或后台(屏幕开启状态)时能正常发送数据,当屏幕关闭一段时间后便停止工作,前台服务也被清除。我已将应用加入电池优化白名单,但问题仍未解决。

位置回调中启动服务的代码

locationCallback = object : LocationCallback() {
        override fun onLocationResult(location: LocationResult) {
            super.onLocationResult(location)
            val latitude = location.lastLocation.latitude
            val longitude = location.lastLocation.longitude
            val serviceIntent = Intent(this@MainActivity,LocationService::class.java)
            serviceIntent.putExtra("latitude",latitude)
            serviceIntent.putExtra("longitude",longitude)
            startService(serviceIntent)

        }

前台服务及数据上报代码

@AndroidEntryPoint
class LocationService : Service() {

    @Inject
    lateinit var sharedPreferences  : SharedPreferences
    override fun onBind(intent: Intent?): IBinder? = null

    @SuppressLint("NewApi")
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val latitude  = intent?.getDoubleExtra("latitude",0.0)
        val longitude = intent?.getDoubleExtra("longitude",0.0)
        postValuesToServer(latitude!!, longitude!!)
        showNotification(latitude,longitude)

       
        return START_STICKY
    }

    @RequiresApi(Build.VERSION_CODES.M)
    private fun showNotification(latitude: Double, longitude: Double) {
        val intent = Intent(this,MainActivity::class.java)
        val pendingIntent   = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_IMMUTABLE)
        val notificationCompat = NotificationCompat.Builder(this,Utils.NOTIFICATION_ID)
            .setContentTitle("经度 : $longitude")
            .setContentText("纬度 : $latitude")
            .setSmallIcon(R.drawable.ic_baseline_circle_notifications_24)
            .setContentIntent(pendingIntent)
            .build()


        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            startForeground(1,notificationCompat)
        }
        else {
            NotificationManagerCompat.from(this).notify(1,notificationCompat)
        }
    }

清单文件中服务注册配置

<service android:name=".services.LocationService"
     android:foregroundServiceType="location|dataSync"/>
解决方案

1. 修正前台服务启动逻辑

当前showNotification方法仅在Android O+版本将服务设为前台,低版本只是发送普通通知,导致服务未真正进入前台模式,系统仍可能在后台杀死它。修改为所有版本都调用startForeground:

@RequiresApi(Build.VERSION_CODES.M)
private fun showNotification(latitude: Double, longitude: Double) {
    val intent = Intent(this,MainActivity::class.java)
    val pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_IMMUTABLE)
    val notificationCompat = NotificationCompat.Builder(this,Utils.NOTIFICATION_ID)
        .setContentTitle("经度 : $longitude")
        .setContentText("纬度 : $latitude")
        .setSmallIcon(R.drawable.ic_baseline_circle_notifications_24)
        .setContentIntent(pendingIntent)
        .build()

    // 所有版本统一调用startForeground,确保服务处于前台状态
    startForeground(1, notificationCompat)
}

2. 确保屏幕关闭时位置更新不被节流

当前依赖LocationCallback触发服务启动,但屏幕关闭后系统可能对位置更新进行节流。需要配置更高优先级的位置请求参数,并确保申请后台位置权限:

// 创建位置请求时设置高优先级和短间隔
val locationRequest = LocationRequest.create().apply {
    interval = 10000 // 10秒间隔
    fastestInterval = 5000 // 最快5秒更新一次
    priority = LocationRequest.PRIORITY_HIGH_ACCURACY
    // 适配Android 12+的后台位置更新限制
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        setMinUpdateIntervalMillis(10000)
    }
}
// 请求持续位置更新
LocationServices.getFusedLocationProviderClient(this)
    .requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())

注意:Android 10+需在清单文件中声明ACCESS_BACKGROUND_LOCATION权限,并在运行时引导用户授予。

3. 修复服务重启时的空指针问题

START_STICKY会在服务被系统杀死后重启,但重启时intent为null,当前代码强制解包latitude!!会导致崩溃。修改onStartCommand逻辑:

@SuppressLint("NewApi")
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    val latitude = intent?.getDoubleExtra("latitude", 0.0)
    val longitude = intent?.getDoubleExtra("longitude", 0.0)
    
    // 有位置数据则直接上报,否则读取本地缓存
    if (latitude != null && longitude != null) {
        postValuesToServer(latitude, longitude)
        // 保存位置到本地缓存
        sharedPreferences.edit()
            .putFloat("last_lat", latitude.toFloat())
            .putFloat("last_lng", longitude.toFloat())
            .apply()
        showNotification(latitude,longitude)
    } else {
        // 读取本地缓存的最后位置
        val lastLat = sharedPreferences.getFloat("last_lat", 0f).toDouble()
        val lastLng = sharedPreferences.getFloat("last_lng", 0f).toDouble()
        if (lastLat != 0.0 && lastLng != 0.0) {
            postValuesToServer(lastLat, lastLng)
            showNotification(lastLat, lastLng)
        }
    }

    return START_STICKY
}

4. 适配厂商后台管控

即使加入电池优化白名单,小米、华为、OPPO等国产厂商仍有独立的后台管控机制,需要引导用户手动开启后台运行权限或自启动权限。可在应用内添加引导页面,提示用户前往系统设置开启对应权限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 10:30:59