Android ListPreference深色模式短暂生效后切回亮色问题求解
深色模式短暂生效后自动回退亮色是3个配置错误共同导致的,仅在正式Release包出现是因为Debug包默认关闭代码混淆,不会触发混淆导致的逻辑失效:
- Key值不匹配,主题设置逻辑实际未正确触发
你定义的ListPreference使用的配置key为@string/theme,但onSharedPreferenceChanged回调中判断的key为R.string.dark_mode,两个字符串资源不相等时,选中选项后回调内的主题切换逻辑根本不会执行。你看到的1秒左右深色效果是Preference组件切换选项时的临时重建效果,并非持久化配置生效的结果。 - 未在应用启动入口应用已保存的主题配置
你仅在SettingsActivity中注册了偏好变化监听,当主题切换触发Activity重建、或是用户冷启动应用时,没有代码在界面渲染前读取SharedPreferences中存储的主题值并调用AppCompatDelegate.setDefaultNightMode(),系统会默认加载亮色主题覆盖临时生效的深色效果。 - 正式包混淆规则缺失
Release构建时开启的R8代码压缩会混淆AppCompat、Preference相关类,未添加对应keep规则时,夜间模式设置逻辑会在正式包中失效。
统一主题配置的Key值
对齐ListPreference和代码逻辑中使用的key,避免资源名不匹配,修正后的ListPreference配置如下:<androidx.preference.ListPreference app:defaultValue="MODE_NIGHT_FOLLOW_SYSTEM" app:entries="@array/dark_mode_entries" app:entryValues="@array/dark_mode_values" android:icon="@drawable/ic_settings_themes" app:key="dark_mode" app:title="@string/theme" app:useSimpleSummaryProvider="true"/>同时将SettingsActivity中
onSharedPreferenceChanged方法内的key判断改为匹配dark_mode,确保选中选项后逻辑能正确进入。在自定义Application类中初始化全局主题
新建自定义Application类,在onCreate最开头(任何布局加载操作之前)读取已保存的主题配置并应用,保证冷启动、Activity重建时主题不会被重置:class App : Application() { override fun onCreate() { super.onCreate() val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this) val darkModeKey = "dark_mode" val darkModeValues = resources.getStringArray(R.array.dark_mode_values) when (sharedPrefs.getString(darkModeKey, darkModeValues[0])) { darkModeValues[0] -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) darkModeValues[1] -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) darkModeValues[2] -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) darkModeValues[3] -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY) } } }在AndroidManifest.xml的
<application>标签下添加android:name=".App"属性,注册自定义Application类。补全Release包混淆规则
在app模块的proguard-rules.pro文件中添加以下规则,避免主题相关类被R8混淆:-keep class androidx.appcompat.app.AppCompatDelegate { *; } -keep class androidx.preference.** { *; }补全监听注销逻辑
在SettingsActivity的onDestroy生命周期方法中注销偏好变化监听,避免内存泄漏:override fun onDestroy() { super.onDestroy() PreferenceManager.getDefaultSharedPreferences(this) .unregisterOnSharedPreferenceChangeListener(this) }
注意:
AppCompatDelegate.setDefaultNightMode()是全局生效的配置,仅需要在应用启动时执行一次、用户修改主题选项时执行一次即可,不需要在每个Activity的onCreate中重复调用。
内容的提问来源于stack exchange,提问作者Mihai-Cristian Condrea

