通过MediaBrowserService更新SeekBar会导致音频播放中断吗?
你的这种定时更新MediaSession extras的方式本身不会直接导致播放中断,但代码里的几个潜在问题,可能是少量用户播放突然停止的诱因:
潜在问题点
未及时清理Runnable导致内存泄漏
你在播放启动后启动了循环Runnable,但没有在播放停止或Service销毁时移除它。这个Runnable持有Service的引用,长期运行会造成内存泄漏,当系统内存紧张时,可能触发Service被回收,进而导致播放中断。而且即使Player已经停止,Runnable还会继续执行,调用player.getCurrentPosition()可能引发空指针异常。Player调用的线程安全问题
player.getCurrentPosition()并非线程安全方法,你的Runnable运行在主线程,而ExoPlayer的核心逻辑在后台线程,直接跨线程调用可能引发并发异常,导致Player崩溃。这种底层崩溃可能不会触发MediaSession的回调方法,所以日志里看不到onDestroy、onPause等记录。频繁跨进程通信的性能开销
每250毫秒创建新Bundle并调用setExtras,会触发MediaSession到MediaController的跨进程通信。频繁的IPC操作会增加系统负载,长期运行可能导致主线程阻塞,间接影响ExoPlayer的播放调度,引发播放中断。
优化方案
1. 改用ExoPlayer原生的进度监听
ExoPlayer自带进度监听接口,不需要通过MediaSession传递进度,更高效且安全:
// 在Service的PlayAudio方法中添加Player监听 player.addListener(new Player.Listener() { @Override public void onPositionDiscontinuity(@NonNull PositionInfo oldPosition, @NonNull PositionInfo newPosition, int reason) { Player.Listener.super.onPositionDiscontinuity(oldPosition, newPosition, reason); int currentPos = (int) player.getCurrentPosition(); // 可通过LocalBroadcast/LiveData等方式将进度传递给Activity } @Override public void onPlaybackStateChanged(int playbackState) { Player.Listener.super.onPlaybackStateChanged(playbackState); // 播放停止时清理Runnable if (playbackState == Player.STATE_ENDED || playbackState == Player.STATE_IDLE) { if (mMediaHandler != null) { mMediaHandler.removeCallbacks(mUpdateMediaPosition); } } } });
2. 及时清理资源
在Service销毁和播放停止时,务必移除Runnable并释放Player:
@Override public void onDestroy() { super.onDestroy(); if (mMediaHandler != null) { mMediaHandler.removeCallbacks(mUpdateMediaPosition); } if (player != null) { player.release(); player = null; } mMediaSessionCompat.release(); } // 停止播放时的清理逻辑 private void stopPlayback() { if (player != null) { player.stop(); player.release(); player = null; } if (mMediaHandler != null) { mMediaHandler.removeCallbacks(mUpdateMediaPosition); } mMediaSessionCompat.setActive(false); }
3. 用PlaybackState传递进度(推荐)
MediaSession的PlaybackState本身已经包含播放进度,不需要自定义extras。在Activity的MediaController回调中直接获取:
MediaControllerCompat.Callback mMediaControllerCompatCallback = new MediaControllerCompat.Callback() { @Override public void onPlaybackStateChanged(PlaybackStateCompat state) { super.onPlaybackStateChanged(state); if (state != null && state.getState() == PlaybackStateCompat.STATE_PLAYING) { seekBar.setProgress((int) state.getPosition()); // 若需实时更新,可通过Handler定时获取PlaybackState } } };
4. 确保Player操作的线程安全
如果一定要保留原有的Runnable方式,要确保Player调用在正确的线程:
// 创建Handler时使用Player的Looper mMediaHandler = new Handler(player.getApplicationLooper());
内容的提问来源于stack exchange,提问作者Kris B

