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

Kotlin中ForegroundService内SensorManager在应用被杀后终止的问题求助

解决方案:让步数统计服务在应用杀死后持续运行

你的问题核心是没有真正将Service设置为前台服务,普通Service在应用进程被杀死后会被系统回收,必须调用startForeground()方法让服务进入前台模式,同时配合正确的权限和配置,才能保证在应用关闭后继续运行。

以下是完整的修改步骤和代码:

1. 配置必要权限(AndroidManifest.xml)

在AndroidManifest.xml中添加前台服务权限,以及Android 10+需要的活动识别权限(步数传感器依赖):

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />

<application ...>
    <!-- 注册你的服务 -->
    <service
        android:name=".MyService"
        android:foregroundServiceType="activity_recognition" /> <!-- 适配Android 12+的前台服务类型 -->
</application>

2. 修改MyService实现前台服务逻辑

更新你的MyService类,添加通知渠道创建、前台服务启动逻辑,同时完善步数统计和传感器监听的生命周期管理:

class MyService : Service(), SensorEventListener {

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

    private val db = Firebase.firestore
    private val userDB get() = db.collection("users")
    private val userId get() = Firebase.auth.currentUser?.uid

    companion object {
        const val FOREGROUND_NOTIFICATION_ID = 1001
        const val CHANNEL_ID = "StepCounter_Channel"
        var todayTotalStepCount: MutableLiveData<Int> = MutableLiveData(0)
    }

    override fun onCreate() {
        super.onCreate()
        // 初始化今日步数
        initTodayStepCount()
        // 初始化传感器
        setupSensor()
        // 启动前台服务
        startForegroundServiceWithNotification()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // 系统杀死服务后,资源充足时会尝试重启服务
        return START_STICKY
    }

    override fun onBind(intent: Intent?): IBinder? {
        throw UnsupportedOperationException("Not implemented")
    }

    private fun initTodayStepCount() {
        userId?.let { uid ->
            userDB.document(uid).get().addOnSuccessListener { document ->
                val savedStep = document.getLong("todayStepCount") ?: 0
                currentStepCount = savedStep.toInt()
                todayTotalStepCount.value = currentStepCount
            }
        }
    }

    private fun setupSensor() {
        sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        stepSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR)
        stepSensor?.let {
            sensorManager.registerListener(this, it, SensorManager.SENSOR_DELAY_UI)
        }
    }

    private fun startForegroundServiceWithNotification() {
        // 创建通知渠道(Android O及以上必须)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                CHANNEL_ID,
                "步数统计服务",
                NotificationManager.IMPORTANCE_LOW // 低优先级,减少用户打扰
            ).apply {
                description = "后台持续统计步数"
            }
            val notificationManager = getSystemService(NotificationManager::class.java)
            notificationManager.createNotificationChannel(channel)
        }

        // 构建前台通知
        val notification = NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("步数统计中")
            .setContentText("今日已走 ${todayTotalStepCount.value ?: 0} 步")
            .setSmallIcon(R.drawable.ic_step_counter) // 替换成你的应用图标资源
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .build()

        // 启动前台服务
        startForeground(FOREGROUND_NOTIFICATION_ID, notification)
    }

    override fun onSensorChanged(event: SensorEvent?) {
        event ?: return
        if (event.sensor.type == Sensor.TYPE_STEP_DETECTOR) {
            // 每触发一次代表走了一步
            currentStepCount++
            todayTotalStepCount.value = currentStepCount
            // 同步步数到Firebase
            userId?.let { uid ->
                userDB.document(uid).update("todayStepCount", currentStepCount)
            }
            // 更新通知内容
            updateNotification()
        }
    }

    override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
        // 无需处理精度变化
    }

    private fun updateNotification() {
        val notification = NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("步数统计中")
            .setContentText("今日已走 ${todayTotalStepCount.value ?: 0} 步")
            .setSmallIcon(R.drawable.ic_step_counter)
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .build()
        val notificationManager = getSystemService(NotificationManager::class.java)
        notificationManager.notify(FOREGROUND_NOTIFICATION_ID, notification)
    }

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

3. 适配服务启动逻辑(Fragment中)

Android 8.0(API 26)及以上版本,启动前台服务必须用startForegroundService(),否则会抛出IllegalStateException,修改启动代码:

val startServiceIntent = Intent(requireContext(), MyService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    requireContext().startForegroundService(startServiceIntent)
} else {
    requireContext().startService(startServiceIntent)
}

关键注意事项

  • 前台服务必须显示通知:Android系统要求前台服务必须在通知栏显示一条通知,低优先级通知可以减少对用户的打扰,但不能隐藏。
  • Android 12+前台服务启动限制:Android 12及以上版本,前台服务只能在用户交互场景下启动(比如按钮点击),不能在后台自动启动。
  • 步数传感器类型:Sensor.TYPE_STEP_DETECTOR每走一步触发一次事件,需要自己累加计数;若使用Sensor.TYPE_STEP_COUNTER,会返回设备开机以来的总步数,需自行计算当日差值。
  • 数据持久化优化:建议每天零点重置当日步数,避免跨天统计错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 09:45:33