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

Wear OS环境模式下运动计时停止失效问题求助

问题分析

你遇到的核心问题是进入环境模式后,运动时长检测逻辑完全停止运行:

  • 进入环境模式时调用了unbindViewsFromService(),这个方法会取消uiBindingJob,直接终止了对service.latestMetrics的监听。
  • 你的sixMinTrain()是绑定在latestMetrics.collect回调里触发的,环境模式下这个监听停了,自然不会执行时长检测,更不会触发停止运动的操作。
  • 另外,用totalSecond == second做判断有精度风险——如果系统调度延迟导致某次循环跳过了目标秒数,就永远触发不了停止逻辑。
解决方案

方案1:把时长检测移到ExerciseService内部(推荐)

将自动停止的逻辑放到后台Service中,不受UI层环境模式影响,可靠性更高:

// 在ExerciseService内部实现
private val autoStopJob = Job()

fun startExerciseWithAutoStop(targetSeconds: Int) {
    // 启动原有运动流程
    startExercise()
    // 启动自动停止检测协程
    CoroutineScope(Dispatchers.Default + autoStopJob).launch {
        while (exerciseState.value == ExerciseState.ACTIVE) {
            val duration = activeDurationCheckpoint.displayDuration(Instant.now(), exerciseState.value)
            if (duration.seconds >= targetSeconds) {
                endExercise()
                // 可通过广播通知UI层执行唤醒、播放声音等操作
                sendBroadcast(Intent("com.your.app.AUTO_STOP_TRIGGERED"))
                break
            }
            delay(1000) // 每秒检测一次,可根据需求调整精度
        }
    }
}

// 停止运动时记得取消检测协程
override fun endExercise() {
    super.endExercise()
    autoStopJob.cancel()
}

UI层只需调用这个带自动停止参数的启动方法,无需再处理检测逻辑。

方案2:在UI层拆分逻辑,保留检测协程

修改环境模式处理逻辑,只暂停UI更新,保留时长检测的独立协程:

  1. 拆分UI更新和时长检测的Job:
private var uiUpdateJob: Job? = null
private var durationCheckJob: Job? = null

private fun bindViewsToService() {
    if (uiUpdateJob != null && durationCheckJob != null) return

    val scope = viewLifecycleOwner.lifecycleScope
    // UI更新Job,环境模式下可取消
    uiUpdateJob = scope.launch {
        serviceConnection.repeatWhenConnected { service ->
            launch { service.exerciseState.collect { updateExerciseStatus(it) } }
            launch { service.latestMetrics.collect { it?.let { updateMetrics(it) } } }
        }
    }
    // 时长检测Job,不受环境模式影响
    durationCheckJob = scope.launch {
        serviceConnection.repeatWhenConnected { service ->
            while (isActive) {
                sixMinTrain(30)
                delay(1000)
            }
        }
    }
}

private fun unbindViewsFromService() {
    uiUpdateJob?.cancel()
    uiUpdateJob = null
    // 不取消durationCheckJob
}
  1. 修改sixMinTrain()的判断条件,避免错过触发:
private suspend fun sixMinTrain(second: Int = 30) {
    if (cachedExerciseState != ExerciseState.ACTIVE) return
    val duration = activeDurationCheckpoint.displayDuration(Instant.now(), cachedExerciseState)
    val totalSecond = duration.seconds.toInt()
    Log.d(TAG, "sec = $totalSecond")
    if (totalSecond >= second) { // 用>=代替==,避免精度问题
        Log.d(TAG, "触发自动停止")
        val service = checkNotNull(serviceConnection.exerciseService)
        service.endExercise()
        wakeUpScreen()
        updateChronometer()
        playSoundAsync()
    }
}
  1. 环境模式切换时只处理UI更新Job:
private fun onEnterAmbient() {
    unbindViewsFromService() // 只取消UI更新
    setAmbientUiState(true)
    performOneTimeUiUpdate()
    Log.d(TAG, "onEnterAmbient")
}

private fun onExitAmbient() {
    performOneTimeUiUpdate()
    setAmbientUiState(false)
    bindViewsToService() // 重新启动UI更新和检测(检测Job如果没取消会继续运行)
    Log.d(TAG, "onExitAmbient")
}

额外注意事项

  • 环境模式下系统会限制资源,playSoundAsync()需要确保设备被唤醒,检查wakeUpScreen()是否正确获取了唤醒锁。
  • 用固定间隔delay(1000)触发检测,比依赖latestMetrics更稳定——因为 metrics 的更新频率不一定是每秒一次,可能导致检测延迟。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 13:08:18