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

如何在Jetpack Compose中使用Android Media3?能否通过ViewModel传递MediaController?

在Jetpack Compose中管理MediaController的最佳实践

完全可以用ViewModel来持有和分发MediaController实例,这是比层层传递参数更优雅的方案,能彻底避免Compose树中的prop drilling问题。

1. 创建持有MediaController的ViewModel

编写一个ViewModel封装MediaController的初始化逻辑,用StateFlow暴露实例供Compose观察:

import android.content.ComponentName
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.media3.session.MediaController
import androidx.media3.session.SessionToken
import com.google.common.util.concurrent.MoreExecutors
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch

class PlaybackViewModel(private val context: Context) : ViewModel() {
    private val _mediaController = MutableStateFlow<MediaController?>(null)
    val mediaController: StateFlow<MediaController?> = _mediaController

    init {
        fetchMediaController()
    }

    private fun fetchMediaController() {
        val sessionToken = SessionToken(context, ComponentName(context, PlaybackService::class.java))
        val controllerFuture = MediaController.Builder(context, sessionToken).buildAsync()
        
        controllerFuture.addListener({
            viewModelScope.launch {
                _mediaController.value = controllerFuture.get()
            }
        }, MoreExecutors.directExecutor())
    }

    override fun onCleared() {
        super.onCleared()
        _mediaController.value?.release()
    }
}

2. 在Compose中获取并使用MediaController

在Compose界面中获取ViewModel实例,收集StateFlow拿到可用的MediaController后再渲染UI:

@Composable
fun PlaybackScreen() {
    val playbackViewModel: PlaybackViewModel = viewModel(factory = object : ViewModelProvider.Factory {
        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return PlaybackViewModel(LocalContext.current) as T
        }
    })
    
    val mediaController by playbackViewModel.mediaController.collectAsState()

    mediaController?.let { controller ->
        PlaybackControls(controller)
    } ?: run {
        CircularProgressIndicator()
    }
}

@Composable
fun PlaybackControls(controller: MediaController) {
    Row(horizontalArrangement = Arrangement.Center) {
        IconButton(onClick = { controller.play() }) {
            Icon(Icons.Filled.Play, contentDescription = "播放")
        }
        IconButton(onClick = { controller.pause() }) {
            Icon(Icons.Filled.Pause, contentDescription = "暂停")
        }
    }
}

3. 关键注意事项

  • 必须在ViewModel的onCleared()方法中释放MediaController,防止内存泄漏。
  • 若MediaSession存在断开重连场景,可在ViewModel中监听连接状态,触发重新获取控制器的逻辑。
  • 如需全局复用,可将ViewModel绑定到Activity级别,确保导航切换时实例不会重建。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 05:15:57