如何在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
相关产品推荐
相关产品推荐

