Android MVVM LiveData屏幕旋转状态保持问题求助
屏幕旋转时音频重启问题的解决方案
你的问题核心在于屏幕旋转触发Activity重建后,代码重复执行了音频播放逻辑,且未保留播放状态。以下是具体的修复步骤和优化方案:
一、当前代码的问题点
- 屏幕旋转时Activity销毁重建,
onCreate方法重新执行,LiveData的观察者会再次收到已保存的歌曲列表数据,导致你重新创建ArgAudio并调用play,直接重启音频。 - 播放器的播放进度、是否正在播放等状态随Activity销毁丢失,无法在重建后恢复。
二、快速修复:避免重复播放+保存状态
修改MainActivity,添加播放状态标记和状态保存逻辑:
public class MainActivity extends AppCompatActivity { private SongViewModel songViewModel; private ArgPlayerSmallView argMusicPlayer; private boolean hasInitiatedPlay = false; // 标记是否已初始化过播放 @SuppressLint("NotifyDataSetChanged") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.single_song_item); argMusicPlayer = findViewById(R.id.argmusicplayer); songViewModel = new ViewModelProvider(this).get(SongViewModel.class); // 观察歌曲数据,仅第一次加载时初始化播放 songViewModel.getLiveSongData().observe(this, songList -> { if (!hasInitiatedPlay && !songList.isEmpty()) { String url = songList.get(0).getUrl(); ArgAudio audio = ArgAudio.createFromURL("test", "test", url); argMusicPlayer.play(audio); hasInitiatedPlay = true; } }); // 从 savedInstanceState 恢复播放状态 if (savedInstanceState != null) { boolean wasPlaying = savedInstanceState.getBoolean("isPlaying"); long playPosition = savedInstanceState.getLong("currentPosition"); if (wasPlaying && hasInitiatedPlay) { argMusicPlayer.seekTo((int) playPosition); argMusicPlayer.play(); } } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // 保存当前播放状态到Bundle if (argMusicPlayer != null) { outState.putBoolean("isPlaying", argMusicPlayer.isPlaying()); outState.putLong("currentPosition", argMusicPlayer.getCurrentPosition()); } } }
三、更可靠的方案:用ViewModel持久化播放状态
将播放状态(音频URL、进度、播放状态)存入ViewModel,利用ViewModel在屏幕旋转时不销毁的特性,更稳定地保留状态:
修改SongViewModel
public class SongViewModel extends ViewModel { MutableLiveData<List<Song>> songListMutableLiveData; SongRepository songRepository; // 持久化播放状态 private String currentAudioUrl; private long currentPlayPos = 0; private boolean isPlaying = false; public SongViewModel() { songRepository = new SongRepository(); songListMutableLiveData = songRepository.getSongListMutableLiveData(); } public MutableLiveData<List<Song>> getLiveSongData() { return songListMutableLiveData; } // 播放状态的getter和setter public String getCurrentAudioUrl() { return currentAudioUrl; } public void setCurrentAudioUrl(String url) { this.currentAudioUrl = url; } public long getCurrentPlayPos() { return currentPlayPos; } public void setCurrentPlayPos(long pos) { this.currentPlayPos = pos; } public boolean isPlaying() { return isPlaying; } public void setPlaying(boolean playing) { this.isPlaying = playing; } }
修改MainActivity
public class MainActivity extends AppCompatActivity { private SongViewModel songViewModel; private ArgPlayerSmallView argMusicPlayer; @SuppressLint("NotifyDataSetChanged") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.single_song_item); argMusicPlayer = findViewById(R.id.argmusicplayer); songViewModel = new ViewModelProvider(this).get(SongViewModel.class); // 优先从ViewModel恢复播放状态 String savedUrl = songViewModel.getCurrentAudioUrl(); if (savedUrl != null) { ArgAudio audio = ArgAudio.createFromURL("test", "test", savedUrl); argMusicPlayer.setAudio(audio); // 假设播放器支持设置已创建的音频实例 argMusicPlayer.seekTo((int) songViewModel.getCurrentPlayPos()); if (songViewModel.isPlaying()) { argMusicPlayer.play(); } } else { // 首次加载歌曲数据并初始化播放 songViewModel.getLiveSongData().observe(this, songList -> { if (!songList.isEmpty()) { String url = songList.get(0).getUrl(); ArgAudio audio = ArgAudio.createFromURL("test", "test", url); argMusicPlayer.play(audio); songViewModel.setCurrentAudioUrl(url); } }); } } @Override protected void onPause() { super.onPause(); // 暂停时保存状态到ViewModel if (argMusicPlayer != null) { songViewModel.setCurrentPlayPos(argMusicPlayer.getCurrentPosition()); songViewModel.setPlaying(argMusicPlayer.isPlaying()); // 可选:如果需要后台继续播放,不要调用pause,改用Service方案 argMusicPlayer.pause(); } } @Override protected void onResume() { super.onResume(); // 恢复播放状态 if (songViewModel.isPlaying()) { argMusicPlayer.seekTo((int) songViewModel.getCurrentPlayPos()); argMusicPlayer.play(); } } }
四、专业级方案:用Service托管音频播放
如果希望屏幕旋转时音频持续播放不中断,推荐使用Service(或MediaBrowserService)管理播放逻辑。Service的生命周期独立于Activity,即使Activity重建,Service仍在后台运行,音频不会中断。核心思路是:
- 创建一个播放Service,在内部处理音频加载、播放、暂停、进度管理。
- Activity通过绑定Service来发送控制指令(播放、暂停)和获取播放状态。
- 屏幕旋转后,Activity重新绑定Service,继续控制播放,全程音频不中断。
内容的提问来源于stack exchange,提问作者Bene
相关产品推荐
相关产品推荐

