Android SharedPreferences保存设置失效 跨Activity读取异常修复
问题排查
你的代码存在4个核心问题,直接导致SharedPreferences存储失效、开关状态不恢复:
loadSound()方法仅调用了SP的读取方法,没有把读取到的状态同步给SwitchCompat控件和对应的状态图标,进入设置页自然不会展示上次保存的状态- 错误将Activity组件(Settings)作为普通类通过构造方法
new Settings(this)创建实例。Activity由系统负责实例化,手动new出来的Activity不具备完整的Context能力,生命周期完全失控,后续调用SP、资源相关方法极易出现异常 - 存在多处拼写错误:SP文件名常量
PREFERRENCE多写了一个r,静音状态值PREFERENCE_SOUND_OFF对应的字符串写为sound_of漏了f,会直接导致读写key/值不匹配 - 设置SwitchCompat的状态监听前没有先初始化控件状态,设置监听的瞬间会触发一次回调,可能把默认值错误覆盖到SP中
正确实现代码
修复后的Settings Activity
注意:不要给Activity自定义带参数的构造方法,所有需要跨页面访问的常量直接定义为public static即可。
public class Settings extends AppCompatActivity { SwitchCompat switchButton; ImageView soundIcon; // 统一公共常量,修正拼写错误 public static final String SP_NAME = "game_settings"; public static final String KEY_SOUND_ENABLED = "sound_enabled"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); switchButton = findViewById(R.id.switchButton); soundIcon = findViewById(R.id.sound_on); // 先加载已保存状态,再设置监听,避免触发无效回调 loadSettings(); switchButton.setOnCheckedChangeListener((buttonView, isChecked) -> { // 直接存布尔值,不需要转字符串,逻辑更简洁 getSharedPreferences(SP_NAME, MODE_PRIVATE) .edit() .putBoolean(KEY_SOUND_ENABLED, isChecked) .apply(); updateSoundUI(isChecked); }); } private void loadSettings() { SharedPreferences sp = getSharedPreferences(SP_NAME, MODE_PRIVATE); // 默认声音开启 boolean soundEnabled = sp.getBoolean(KEY_SOUND_ENABLED, true); switchButton.setChecked(soundEnabled); updateSoundUI(soundEnabled); } private void updateSoundUI(boolean isSoundOn) { soundIcon.setImageResource(isSoundOn ? R.drawable.sound_on : R.drawable.sound_off); } }
修复后的Quiz Activity调用逻辑
不需要new Settings实例,直接引用Settings类中定义的公共静态常量读取SP即可:
private PlayAudioForAnswers playAudioForAnswers; private boolean isSoundEnabled; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_quiz); playAudioForAnswers = new PlayAudioForAnswers(this); loadGameSettings(); } private void loadGameSettings() { SharedPreferences sp = getSharedPreferences(Settings.SP_NAME, MODE_PRIVATE); isSoundEnabled = sp.getBoolean(Settings.KEY_SOUND_ENABLED, true); } // 需要播放答题音效时直接判断 private void playAnswerAudio(int flag) { if (isSoundEnabled) { playAudioForAnswers.setAudioforAnswer(flag); } }
开发注意事项
- 所有系统组件(Activity、Service、BroadcastReceiver等)绝对不要手动通过构造方法创建实例,必须通过系统提供的对应方式(如Intent启动Activity)获取实例,否则会出现Context失效、生命周期混乱等难以排查的问题
- 给SwitchCompat、CheckBox等带选中状态的控件设置监听器前,必须先给控件设置已保存的选中状态,否则设置监听器时会立刻触发一次选中状态变更回调,导致错误写入默认值
- 开关类的配置直接用SP的
putBoolean/getBoolean存取即可,不需要额外转成字符串,能避免字符串拼写错误、比对逻辑出错等问题 - SP的key、文件名尽量统一管理为常量,不要在各个页面硬编码字符串,避免拼写不一致导致读写失败
内容的提问来源于stack exchange,提问作者YakovMashin
相关产品推荐
相关产品推荐

