Jetpack Media3结合Compose:Service与ViewModel状态不同步求助
解决Media3播放状态与UI不同步的问题
问题根源
你当前的ViewModel自行维护_isPlaying状态,仅在调用内部play()/pause()方法时更新,但通知栏操作直接通过MediaSession控制Player,ViewModel未监听这些外部操作带来的状态变更,导致UI显示与实际播放状态脱节。
解决方案
核心思路是让ViewModel监听MediaController的播放状态回调,实时同步真实状态到UI,而非手动维护状态。
步骤1:在ViewModel中注册MediaController状态监听器
修改RadioViewModel,添加对MediaController的播放状态监听,自动同步状态到LiveData:
class RadioViewModel(application: Application) : AndroidViewModel(application) { private val _isPlaying = MutableLiveData(false) val isPlaying: LiveData<Boolean> = _isPlaying private val playerReady = MutableStateFlow(false) private var _player: MediaController? = null private val _playbackState = MutableLiveData(0) val playbackState: LiveData<Int> = _playbackState // 播放状态监听器,实时同步状态 private val playbackStateListener = object : MediaController.PlaybackStateListener { override fun onPlaybackStateChanged(state: PlaybackState) { _isPlaying.postValue(state.isPlaying) _playbackState.postValue(state.state) } } init { val sessionToken = SessionToken(application, ComponentName(application, PlaybackService::class.java)) val controllerFuture = MediaController.Builder(application, sessionToken).buildAsync() controllerFuture.addListener( { _player = controllerFuture.get().apply { // 注册监听器 addPlaybackStateListener(playbackStateListener) // 初始化时同步一次当前状态 val currentState = playbackState _isPlaying.postValue(currentState.isPlaying) _playbackState.postValue(currentState.state) } playerReady.value = true }, MoreExecutors.directExecutor() ) } fun setUri(uri: Uri) = viewModelScope.launch { playerReady.first { it } val mediaItem = MediaItem.fromUri(uri) _player?.let { player -> player.setMediaItem(mediaItem) player.prepare() } _player?.play() } fun togglePlay() = _player?.let { player -> if (player.playbackState.isPlaying) player.pause() else player.play() } override fun onCleared() { super.onCleared() _player?.apply { removePlaybackStateListener(playbackStateListener) release() } val intent = Intent(getApplication(), PlaybackService::class.java) getApplication<Application>().stopService(intent) } }
步骤2:移除手动维护状态的逻辑
删除原ViewModel中play()/pause()方法里手动设置_isPlaying.value的代码,所有状态更新完全依赖MediaController的监听器回调,避免手动状态与真实状态冲突。
步骤3:完善PlaybackService的通知栏支持(可选但必要)
你的PlaybackService目前未配置通知栏,Media3需要通过MediaNotificationProvider才能让通知栏控制生效,否则通知栏操作可能无法触发状态回调。在Service的onCreate中补充配置:
override fun onCreate() { super.onCreate() val player = ExoPlayer.Builder(this).build() val mediaSession = MediaSession.Builder(this, player).build() this.mediaSession = mediaSession // 配置通知栏 val notificationProvider = DefaultMediaNotificationProvider(this) val notificationManager = getSystemService(NotificationManager::class.java) mediaSession.setNotificationProvider( notificationProvider, notificationManager, R.drawable.ic_notification // 替换为你的通知图标资源ID ) // 设置点击通知跳转的Activity mediaSession.setSessionActivity(PendingIntent.getActivity( this, 0, Intent(this, MainActivity::class.java), // 替换为你的主Activity PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT )) }
关键原理
MediaController是ViewModel与MediaSessionService的通信桥梁,所有播放操作都应通过MediaController执行,状态变化通过其监听器同步。放弃手动维护状态,完全依赖MediaController提供的真实状态,即可保证UI与通知栏、Service的状态一致。
内容的提问来源于stack exchange,提问作者IdanB
相关产品推荐
相关产品推荐

