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

Android MVVM LiveData屏幕旋转状态保持问题求助

屏幕旋转时音频重启问题的解决方案

你的问题核心在于屏幕旋转触发Activity重建后,代码重复执行了音频播放逻辑,且未保留播放状态。以下是具体的修复步骤和优化方案:

一、当前代码的问题点

  1. 屏幕旋转时Activity销毁重建,onCreate方法重新执行,LiveData的观察者会再次收到已保存的歌曲列表数据,导致你重新创建ArgAudio并调用play,直接重启音频。
  2. 播放器的播放进度、是否正在播放等状态随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仍在后台运行,音频不会中断。核心思路是:

  1. 创建一个播放Service,在内部处理音频加载、播放、暂停、进度管理。
  2. Activity通过绑定Service来发送控制指令(播放、暂停)和获取播放状态。
  3. 屏幕旋转后,Activity重新绑定Service,继续控制播放,全程音频不中断。

内容的提问来源于stack exchange,提问作者Bene

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 12:35:53