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

Jetpack Compose中DisposableEffect正确使用及传感器监听异常问题

问题分析

你遇到的核心问题是屏幕方向变更触发了Activity重建:当跳转至PresetsEditor时,设置的横屏方向会导致Activity重新创建,旧的Composable实例被销毁,进而触发DisposableEffect的onDispose方法,提前调用viewModel.stopListeningSensor()停止了传感器。而ViewModel是与导航目的地绑定的(hiltViewModel()默认作用域为导航图),重建后不会重新初始化,传感器也就无法自动重启,最终表现为刚进入界面传感器就停止工作。

另外,你当前在ViewModel的init块中直接启动传感器的逻辑,会让传感器生命周期与ViewModel绑定,而非与Composable的可见性绑定,不符合“返回上一界面才停止监听”的需求。

解决方案

步骤1:完善ViewModel的传感器控制方法

移除init块中的自动启动逻辑,添加明确的启动/停止方法,同时在ViewModel销毁时兜底停止传感器:

@HiltViewModel
class EditorViewModel @Inject constructor(
  private val sensor: AbstractSensor
) : ViewModel(){

  private val _sensorFlow = MutableStateFlow(0f)
  val sensorFlow = _sensorFlow.asStateFlow()

  // 启动传感器监听
  fun startListeningSensor() {
    sensor.startListening()
    sensor.setOnSensorValuesChangedListener {
      _sensorFlow.value = it
    }
  }

  // 停止传感器监听并清空回调
  fun stopListeningSensor() {
    sensor.stopListening()
    sensor.setOnSensorValuesChangedListener(null)
  }

  // ViewModel销毁时兜底停止传感器,避免内存泄漏
  override fun onCleared() {
    super.onCleared()
    stopListeningSensor()
  }
}

步骤2:调整DisposableEffect逻辑,绑定传感器生命周期到Composable

修改PresetsEditor中的DisposableEffect,将传感器的启动/停止与Composable的进入/离开组合事件绑定,同时处理屏幕方向变更的场景:

@OptIn(ExperimentalLifecycleComposeApi::class)
@Destination
@Composable
fun PresetsEditor(
  orientation: Int,
  presetsModel: PresetsModel,
  viewModel: EditorViewModel = hiltViewModel()
) {
  val context = LocalContext.current
  val systemUiController = LocalSystemUiController.current
  val dialogState by viewModel.dialogState.collectAsState()
  val steeringValue by viewModel.sensorFlow.collectAsStateWithLifecycle()

  systemUiController.systemBarsBehavior =
    WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE

  // 以orientation作为DisposableEffect的key,确保方向变更时重新执行逻辑
  DisposableEffect(orientation) {
    val activity = context.findActivity() ?: return@DisposableEffect onDispose {}
    val originalOrientation = activity.requestedOrientation
    
    // 1. 设置目标屏幕方向
    activity.requestedOrientation = orientation
    // 2. 隐藏系统状态栏
    systemUiController.isSystemBarsVisible = false
    // 3. 启动传感器监听
    viewModel.startListeningSensor()

    onDispose {
      // 恢复原始屏幕方向
      activity.requestedOrientation = originalOrientation
      // 显示系统状态栏
      systemUiController.isSystemBarsVisible = true
      // 停止传感器监听
      viewModel.stopListeningSensor()
    }
  }

  println("steeringValue: $steeringValue")

  EditorContent(
    presetsModel, 
    dialogState, 
    viewModel::onClickLabel, 
    viewModel::onDismissRequest
  )
}

关键说明

  • DisposableEffect的key设置:用orientation作为key,当屏幕方向变更导致Composable重组时,旧的DisposableEffect会执行onDispose,新的实例会重新执行effect块,自动重启传感器。
  • 传感器生命周期与Composable绑定:不再依赖ViewModel初始化逻辑启动传感器,而是在Composable进入组合时启动、离开时停止,完全匹配“进入界面监听,返回时停止”的需求。
  • 兜底处理:ViewModel的onCleared()方法确保即使出现异常场景,传感器也会被正确停止,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 04:20:26