Android ExoPlayer退出Fragment10秒后均衡器停止工作问题
问题根因
两个异常本质是同一个问题:你把系统音频效果(Equalizer、BassBoost、PresetReverb)的生命周期完全绑定在了均衡器Fragment上,且没有处理音频会话ID变更的场景:
- 返回退出Fragment后,Fragment被销毁,你创建的效果实例没有任何全局对象持有引用,系统会在10秒左右的资源回收周期内释放这些效果实例,均衡器自然失效。
- ExoPlayer切换音频资源时会重新生成音频会话ID,你之前绑定在旧ID上的效果实例不会作用到新会话,切换音频后就会失效。
另外你在onPause、onResume里反复调用newBuild()重建Fragment是完全无效的补丁逻辑,反而会引发重复创建实例、内存泄漏的问题,必须删掉。
修复方案
核心思路是把音频效果的生命周期和播放器绑定,和UI层(Fragment)解耦:
- 写一个全局单例的均衡器管理类,统一持有所有音频效果实例,不要随Fragment销毁释放
- 监听ExoPlayer的音频会话ID变更事件,每次ID更新时重新绑定效果实例到新会话,同时加载已保存的均衡器配置
- Fragment只负责UI渲染和接收用户操作,用户调整参数时直接调用管理类的方法更新效果,不要在Fragment里创建/销毁效果实例
- 删掉onPause、onResume里无意义的newBuild()调用
核心实现代码
1. 全局均衡器管理类
public class EqualizerManager { private static volatile EqualizerManager instance; private Equalizer mEqualizer; private BassBoost bassBoost; private PresetReverb presetReverb; private int currentAudioSessionId = -1; private EqualizerManager() {} public static EqualizerManager getInstance() { if (instance == null) { synchronized (EqualizerManager.class) { if (instance == null) { instance = new EqualizerManager(); } } } return instance; } // 绑定到指定音频会话,初始化效果 public void attachToAudioSession(int audioSessionId) { if (audioSessionId <= 0) return; // 同会话无需重复初始化 if (currentAudioSessionId == audioSessionId && mEqualizer != null) { applySavedConfig(); return; } // 先释放旧实例 release(); currentAudioSessionId = audioSessionId; // 初始化音频效果 mEqualizer = new Equalizer(0, audioSessionId); bassBoost = new BassBoost(0, audioSessionId); presetReverb = new PresetReverb(0, audioSessionId); // 启用效果 mEqualizer.setEnabled(true); bassBoost.setEnabled(true); presetReverb.setEnabled(true); // 加载已保存的配置 applySavedConfig(); } // 应用本地存储的均衡器配置 private void applySavedConfig() { if (mEqualizer == null || bassBoost == null || presetReverb == null) return; // 应用低音增强配置 BassBoost.Settings bassSettings = new BassBoost.Settings(bassBoost.getProperties().toString()); bassSettings.strength = Settings.bassStrength; bassBoost.setProperties(bassSettings); // 应用混响配置 presetReverb.setPreset(Settings.reverbPreset); // 应用均衡器频段配置 if (Settings.presetPos == 0) { for (short bandIdx = 0; bandIdx < mEqualizer.getNumberOfBands(); bandIdx++) { mEqualizer.setBandLevel(bandIdx, (short) Settings.seekbarpos[bandIdx]); } } else { mEqualizer.usePreset((short) Settings.presetPos); } } // 供UI层调用更新频段增益 public void updateBandLevel(short band, short level) { if (mEqualizer != null) mEqualizer.setBandLevel(band, level); } // 供UI层调用更新低音强度 public void updateBassStrength(short strength) { if (bassBoost != null) { BassBoost.Settings settings = new BassBoost.Settings(bassBoost.getProperties().toString()); settings.strength = strength; bassBoost.setProperties(settings); } } // 供UI层调用更新混响预设 public void updateReverbPreset(short preset) { if (presetReverb != null) presetReverb.setPreset(preset); } // 供UI层调用更新均衡器预设 public void updateEqPreset(short presetPos) { if (mEqualizer != null) mEqualizer.usePreset(presetPos); } // 释放所有效果资源 public void release() { if (mEqualizer != null) { mEqualizer.release(); mEqualizer = null; } if (bassBoost != null) { bassBoost.release(); bassBoost = null; } if (presetReverb != null) { presetReverb.release(); presetReverb = null; } currentAudioSessionId = -1; } }
2. 播放器侧绑定监听
在你初始化ExoPlayer的位置添加如下代码,不要在Fragment里做绑定:
// 监听音频会话ID变化 MyConstants.exoPlayer.addAnalyticsListener(new AnalyticsListener() { @Override public void onAudioSessionIdChanged(@NonNull EventTime eventTime, int audioSessionId) { EqualizerManager.getInstance().attachToAudioSession(audioSessionId); } }); // 首次初始化完成后绑定 EqualizerManager.getInstance().attachToAudioSession(MyConstants.exoPlayer.getAudioSessionId());
3. 改造现有代码
- 删掉EqualizerFragment的
onCreate方法里创建mEqualizer、bassBoost、presetReverb实例的所有逻辑,用户操作UI调整参数时,直接调用EqualizerManager对应的update方法即可。 - 删掉MainActivityEqualizer里
onPause、onResume方法中调用newBuild()的代码,不需要反复重建Fragment。 - 播放器退出销毁时,调用
EqualizerManager.getInstance().release()释放资源避免泄漏。
内容的提问来源于stack exchange,提问作者Sarmad
相关产品推荐
相关产品推荐

