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

Android ForegroundService在部分设备后台运行一段时间后停止求助

解决Android后台持续步数统计停止的问题

一、优先处理设备电池优化限制

大部分国产厂商(小米、华为、OPPO等)的后台查杀机制会针对非白名单应用强制终止进程,哪怕是前台Service也难逃规则。

  • 代码中引导用户跳转至电池优化设置页面,将应用加入豁免列表:
    fun requestIgnoreBatteryOptimization(context: Context) {
        val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
            data = Uri.parse("package:${context.packageName}")
        }
        context.startActivity(intent)
    }
    
  • 同时在Manifest中声明对应权限:
    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
    
  • 额外针对问题设备的厂商规则,引导用户开启自启动权限「后台活动权限」,比如小米的“自启动管理”、华为的“受保护应用”。

二、优化Service的传感器注册与生命周期

你当前在onCreate、onStart、onStartCommand重复注册传感器,容易导致注册逻辑混乱,建议统一生命周期:

private lateinit var sensorManager: SensorManager
private var stepSensor: Sensor? = null

override fun onCreate() {
    super.onCreate()
    // 仅在onCreate中注册传感器
    sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
    stepSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
    stepSensor?.let {
        sensorManager.registerListener(this, it, SensorManager.SENSOR_DELAY_NORMAL)
    }
    // 启动前台服务
    startForeground(NOTIFICATION_ID, createNotification())
}

override fun onDestroy() {
    super.onDestroy()
    // 销毁时必须注销传感器,避免内存泄漏
    sensorManager.unregisterListener(this)
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    // 返回START_STICKY,确保Service被系统杀死后自动重启
    return START_STICKY
}

三、替换广播接收器为WorkManager实现每日重置

系统广播ACTION_DATE_CHANGED在后台限制下触发不可靠,改用WorkManager调度每日0点的步数重置任务,它能适配系统后台规则:

// 定义重置步数的Worker
class ResetStepWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
    override fun doWork(): Result {
        // 执行步数重置逻辑,比如更新本地存储的基准步数
        return Result.success()
    }
}

// 调度每日0点任务的方法
fun scheduleDailyReset(context: Context) {
    val constraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.NOT_REQUIRED)
        .build()

    val dailyResetRequest = PeriodicWorkRequestBuilder<ResetStepWorker>(1, TimeUnit.DAYS)
        .setConstraints(constraints)
        .setInitialDelay(calculateInitialDelay(), TimeUnit.MILLISECONDS)
        .build()

    WorkManager.getInstance(context).enqueueUniquePeriodicWork(
        "DailyStepReset",
        ExistingPeriodicWorkPolicy.REPLACE,
        dailyResetRequest
    )
}

// 计算距离下一个0点的延迟时间
private fun calculateInitialDelay(): Long {
    val calendar = Calendar.getInstance().apply {
        set(Calendar.HOUR_OF_DAY, 0)
        set(Calendar.MINUTE, 0)
        set(Calendar.SECOND, 0)
        add(Calendar.DAY_OF_YEAR, 1)
    }
    return calendar.timeInMillis - System.currentTimeMillis()
}

四、定期验证ACTIVITY_RECOGNITION权限

部分设备会在后台自动收回权限,建议在Service中定期检查权限有效性,若失效则通知用户重新授权:

fun checkActivityRecognitionPermission(context: Context): Boolean {
    return ContextCompat.checkSelfPermission(
        context,
        Manifest.permission.ACTIVITY_RECOGNITION
    ) == PackageManager.PERMISSION_GRANTED
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    if (!checkActivityRecognitionPermission(this)) {
        // 发送通知引导用户重新授权
        sendPermissionRequestNotification()
    }
    return START_STICKY
}

五、修正步数统计逻辑

Sensor.TYPE_STEP_COUNTER是系统累计的总步数,需记录基准值避免重复统计:

private var lastStepCount = 0L

override fun onSensorChanged(event: SensorEvent?) {
    event?.let {
        if (it.sensor.type == Sensor.TYPE_STEP_COUNTER) {
            val currentTotalSteps = it.values[0].toLong()
            val todaySteps = currentTotalSteps - lastStepCount
            // 更新今日步数到本地存储或UI
        }
    }
}

// 每日重置时更新基准值
fun resetStepBase(currentTotalSteps: Long) {
    lastStepCount = currentTotalSteps
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 12:17:37