ExoPlayer对接MediaSessionCompat:让SimpleExoPlayerView按钮触发回调
我希望将自己实现的ExoPlayer与MediaSessionCompat对象对接,已设置SimpleExoPlayerView用于播放视频。期望每次点击该视图中的按钮时,MediaSessionCompat的回调方法能够触发,但目前仅在使用耳机等外部设备按键时才能触发回调。
以下是应用中最初使用的代码:
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE) public void setUp(LifecycleOwner lifecycleOwner){ // Create a MediaSessionCompat Log.i("Hoe8", "lco setup called"); mMediaSession = new MediaSessionCompat(activity, "this"); // Enable callbacks from MediaButtons and TransportControls mMediaSession.setFlags( MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); // Do not let MediaButtons restart the player when the app is not visible mMediaSession.setMediaButtonReceiver(null); // Set an initial PlaybackState with ACTION_PLAY, so media buttons can start the player mStateBuilder = new PlaybackStateCompat.Builder() .setActions( PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PLAY_PAUSE); mMediaSession.setPlaybackState(mStateBuilder.build()); // MySessionCallback has methods that handle callbacks from a media controller mMediaSession.setCallback(new MediaSessionCompat.Callback() { @Override public void onPlay() { super.onPlay(); Log.i("Hoe8", "MediaSession callback play called"); mMediaSession.setActive(true); ((JokesAdapter.VideoPostViewHolder) rv).setIsPlaying(true); ((JokesAdapter.VideoPostViewHolder) rv).setHasStarted(true); } @Override public void onPause() { super.onPause(); ((JokesAdapter.VideoPostViewHolder) rv).setIsPlaying(false); } @Override public void onStop() { super.onStop(); mMediaSession.setActive(false); ((JokesAdapter.VideoPostViewHolder) rv).setIsPlaying(false); ((JokesAdapter.VideoPostViewHolder) rv).setHasStarted(false); } }); // Create a MediaControllerCompat MediaControllerCompat mediaController = new MediaControllerCompat(activity, mMediaSession); MediaControllerCompat.setMediaController(activity, mediaController); //Handler mainHandler = new Handler(); BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); // 2. Create the player player = ExoPlayerFactory.newSimpleInstance(activity, trackSelector); playerView.setPlayer(player); MediaSessionConnector mediaSessionConnector = new MediaSessionConnector(mMediaSession); mediaSessionConnector.setPlayer(player, null,null ); }
随后修改后的代码:
public class VideoLifeCyclerObserver implements LifecycleObserver { MediaSessionCompat mMediaSession; PlaybackStateCompat.Builder mStateBuilder; AppCompatActivity activity; SimpleExoPlayerView playerView; SimpleExoPlayer player; ExoPlayer.ExoPlayerComponent rv; MediaSessionConnector mediaSessionConnector; public VideoLifeCyclerObserver(AppCompatActivity activity, SimpleExoPlayerView playerView, ExoPlayer.ExoPlayerComponent rv){ this.activity = activity; this.playerView = playerView; this.activity.getLifecycle().addObserver(this); this.rv = rv; Log.i("Hoe8","video lco created"); } @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) public void setUp(LifecycleOwner lifecycleOwner){ // Create a MediaSessionCompat Log.i("Hoe8", "lco setup called"); mMediaSession = new MediaSessionCompat(activity, "this"); // Create a MediaControllerCompat MediaControllerCompat mediaController = new MediaControllerCompat(activity, mMediaSession); MediaControllerCompat.setMediaController(activity, mediaController); mediaSessionConnector = new MediaSessionConnector(mMediaSession, new PlayBackController()); } @OnLifecycleEvent(Lifecycle.Event.ON_START) public void startPlayer(LifecycleOwner lifecycleOwner){ BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); player = ExoPlayerFactory.newSimpleInstance(activity, trackSelector); playerView.setPlayer(player); mediaSessionConnector.setPlayer(player, null,null ); } @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) public void tearDown(LifecycleOwner lifecycleOwner){ player.stop(); player.release(); player.sendMessages(new ExoPlayer.ExoPlayerMessage(rv,1,player.getContentPosition())); } public class PlayBackController extends DefaultPlaybackController{ @Override public void onPause(Player player) { Log.i("Hoe8", "onPause called"); ((JokesAdapter.VideoPostViewHolder) rv).setIsPlaying(false); super.onPause(player); } @Override public void onPlay(Player player) { Log.i("Hoe8", "MediaSession callback play called 2"); mMediaSession.setActive(true); ((JokesAdapter.VideoPostViewHolder) rv).setIsPlaying(true); ((JokesAdapter.VideoPostViewHolder) rv).setHasStarted(true); super.onPlay(player); } @Override public void onStop(Player player) { Log.i("Hoe8", "onStop called"); mMediaSession.setActive(false); ((JokesAdapter.VideoPostViewHolder) rv).setIsPlaying(false); ((JokesAdapter.VideoPostViewHolder) rv).setHasStarted(false); super.onStop(player); } } }
请问如何让SimpleExoPlayerView中的按钮触发MediaSessionCompat的回调方法?
其实SimpleExoPlayerView的默认控制按钮是直接和ExoPlayer实例绑定的,不会自动触发MediaSessionCompat的回调。咱们只需要做几个关键调整就能实现需求:
1. 让PlayerView通过MediaController发送控制指令
你已经创建了MediaControllerCompat并设置给了Activity,现在需要把PlayerView和这个MediaController关联起来,这样它的播放/暂停按钮点击时,会通过MediaController向MediaSession发送命令,进而触发你设置的MediaSession.Callback。
在初始化Player并绑定到PlayerView之后,添加这行代码:
playerView.setMediaController(MediaControllerCompat.getMediaController(activity));
这样PlayerView的所有控制按钮就会使用MediaController的TransportControls来发送指令,而不是直接操作Player实例。
2. 同步MediaSession回调与Player状态
在你的MediaSession.Callback实现里,需要实际控制Player的播放状态,同时更新MediaSession的PlaybackState,确保两者状态一致:
@Override public void onPlay() { super.onPlay(); Log.i("Hoe8", "MediaSession callback play called"); mMediaSession.setActive(true); // 同步Player的播放状态 player.setPlayWhenReady(true); ((JokesAdapter.VideoPostViewHolder) rv).setIsPlaying(true); ((JokesAdapter.VideoPostViewHolder) rv).setHasStarted(true); // 更新MediaSession的播放状态为正在播放 mStateBuilder.setState(PlaybackStateCompat.STATE_PLAYING, player.getCurrentPosition(), 1f); mMediaSession.setPlaybackState(mStateBuilder.build()); } @Override public void onPause() { super.onPause(); player.setPlayWhenReady(false); ((JokesAdapter.VideoPostViewHolder) rv).setIsPlaying(false); // 更新MediaSession的播放状态为暂停 mStateBuilder.setState(PlaybackStateCompat.STATE_PAUSED, player.getCurrentPosition(), 1f); mMediaSession.setPlaybackState(mStateBuilder.build()); }
3. 完善PlaybackState的动作配置
初始化PlaybackStateBuilder时,要确保包含所有需要的控制动作,比如ACTION_PAUSE,这样MediaController才能识别并发送对应的指令:
mStateBuilder = new PlaybackStateCompat.Builder() .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE);
4. 调整MediaSessionConnector的使用
你使用的MediaSessionConnector会自动同步Player和MediaSession的状态,但如果想让PlayerView的按钮触发MediaSession的回调,核心还是让PlayerView通过MediaController发送命令。另外,如果你保留了自定义的PlayBackController,可以让它和MediaSession的回调联动,比如在onPlay方法里主动调用MediaSession的回调逻辑,或者直接依赖MediaSession的回调来处理UI更新。
经过这些调整后,SimpleExoPlayerView的按钮点击就会触发MediaSessionCompat的回调方法,同时Player和MediaSession的状态也能保持同步,外部设备按键的功能也不会受影响。
内容的提问来源于stack exchange,提问作者Joel Robinson-Johnson

