5

我有这个媒体播放器,其他一切正常,我开始播放歌曲,它通过 2 个状态,最后一个是PlaybackStateCompat.STATE_PLAYING

因此我们可以说回调是注册的。问题是每当我触发 SeekBar 侦听器时,该方法seekTo都不会触发,onPlaybackStateChanged并且因为这没有发生,所以我无法将 SeekBar 更新为其新进度。这仅涉及 UI。音乐实际上是从修改 SeekBar 进度后的进度播放的。更重要的是,trackDurationTextView 也没有得到更新。当我在搜索栏上拖动拇指时,它会改变它的值,但是一旦我松开它,它就会进入它的初始进度,与搜索栏相同。

这是注册回调的地方。

@Override
    public void onServiceConnected(ComponentName componentName, IBinder service) {
        if (service instanceof MediaPlayerService.ServiceBinder) {
            try {
                mediaController = new MediaControllerCompat(this, ((MediaPlayerService.ServiceBinder) service).getService().getMediaSessionToken());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mediaController.registerCallback(mMediaControllerCallback);
        }
    }

这是回调的初始化方式:

 private MediaControllerCompat.Callback mMediaControllerCallback = new MediaControllerCompat.Callback() {
        @Override
        public void onPlaybackStateChanged(@NonNull PlaybackStateCompat state) {
            updatePlaybackState(state);
        }

        @Override
        public void onMetadataChanged(MediaMetadataCompat metadata) {
            if (metadata != null) {
                updateMediaDescription(metadata.getDescription());
                updateDuration(metadata);
            }
        }
    };

这是OnSeekBarChangeListener

trackSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                trackDurationTextView.setText(DateUtils.formatElapsedTime(progress / 1000));
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                stopSeekbarUpdate();
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                //TODO: look here! the seek bar position is not kept when the user slides it!
                mediaController.getTransportControls().seekTo(seekBar.getProgress());
                scheduleSeekBarUpdate();
            }
        });


 private void updateProgress() {
        if (mLastPlaybackState == null) {
            return;
        }
        long currentPosition = mLastPlaybackState.getPosition();
        if (mLastPlaybackState.getState() != PlaybackStateCompat.STATE_PAUSED) {
            // Calculate the elapsed time between the last position update and now and unless
            // paused, we can assume (delta * speed) + current position is approximately the
            // latest position. This ensure that we do not repeatedly call the getPlaybackState()
            // on MediaControllerCompat.
            long timeDelta = SystemClock.elapsedRealtime() -
                    mLastPlaybackState.getLastPositionUpdateTime();

            currentPosition += (int) timeDelta * mLastPlaybackState.getPlaybackSpeed();
        }
        trackSeekBar.setProgress((int) currentPosition);
    }

这是在搜索栏更新后 mLastPlayBackState.getPosition 应该返回新值的地方,但它始终返回 0,因为永远不会调用 onPlaybackStateChanged。任何想法为什么?

4

2 回答 2

2

似乎只有在'的变化MediaControllerCompat.Callback.onPlaybackStateChanged(PlaybackStateCompat state)时才会调用它。如果'改变而保持不变,则永远不会被调用。所以我假设通过“onPlaybackStateChanged”,API 更具体地指的是's而不是它本身。MediaSessionCompatPlaybackStateCompatstatePlaybackStateCompatpositionstateonPlaybackStateChanged(...)PlaybackStateCompatstatePlaybackStateCompat

为了解决这个问题,我暂时将PlaybackStateCompat's更新statePlaybackStateCompat.STATE_REWINDINGPlaybackStateCompat.STATE_FAST_FORWARDING在搜索命令之后触发onPlaybackStateChanged(...)UI,以便我可以将SeekBar' 位置和 elapsed timeTextView的文本更新到新的播放位置。

更新:我无法再重现此问题。我也不确定我到底做了什么来修复它。我对您的第一个建议是验证您正在调用setState(state)您的mediaSession对象,其中state包含PlaybackStateCompat您的更新position值。这不是我的解决方案,但这是我首先要看的地方。

于 2016-12-20T21:00:16.687 回答
0

我也遇到了这个问题并找到了一个可行的解决方案。基本上,正如 Ryan 所建议的,您需要调用 MediaSession 或 MediaSessionCompat 的 setPlaybackState()。我的项目基于Google 较旧的 Android 音乐播放器 Codelab Github 存储库,因此我使用了一个播放管理器,它跟踪 MediaPlayer 并且还具有状态更改时的回调。因此,当管理器的 seekTo() 被调用时,它会通过回调将状态发送到 MusicService(MediaBrowserServiceCompat 的子类),MusicService 会跟踪 MediaSessionCompat。session.setPlaybackState(state) 确实是下面 MusicService.kt 中的关键行。

播放管理器.kt:

class PlaybackManager(private val context: Context, private val callback: Callback)
    : AudioManager.OnAudioFocusChangeListener, MediaPlayer.OnCompletionListener {

   /* A bunch of stuff not important to this answer goes here */

   /* mediaPlayer gets set to a MediaPlayer at some point */

    fun seekTo(position: Long) {
        mediaPlayer?.seekTo(position, MediaPlayer.SEEK_CLOSEST_SYNC)
        updatePlaybackState()
    }

    private fun updatePlaybackState() {
        callback.onPlaybackStatusChanged(PlaybackStateCompat.Builder()
                .setActions(getAvailableActions())
                .setState(state, getCurrentStreamPosition().toLong(),
                        1.0f, SystemClock.elapsedRealtime())
                .build())
    }

    interface Callback {
        fun onPlaybackStatusChanged(state: PlaybackStateCompat)
    }
}

音乐服务.kt:

class MusicService : MediaBrowserServiceCompat() {

    private lateinit var session: MediaSessionCompat
    private lateinit var playback: PlaybackManager

    val callback = object: MediaSessionCompat.Callback() {

       /* Other methods like onPlay() and onPause() go here */

        override fun onSeekTo(pos: Long) {
            playback.seekTo(pos)
        }
    }

    override fun onCreate() {
        super.onCreate()

        session = MediaSessionCompat(this, "MusicService")
        session.setCallback(callback)
        sessionToken = session.sessionToken

        playback = PlaybackManager(this, object: PlaybackManager.Callback {
            override fun onPlaybackStatusChanged(state: PlaybackStateCompat) {
                session.setPlaybackState(state)
            }
        })
    }

    /* Other methods like onDestroy(), onGetRoot() and onLoadChildren() go here */
}

更有用的代码是Google Universal Android Music Player Sample Github repo

于 2018-06-09T01:38:31.563 回答