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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:57:38