Android切换深浅色模式报ClassCastException且退出后模式状态丢失
Android深色模式切换异常与状态丢失修复
现存问题
- 点击深色模式切换按钮时,浅色到深色的切换效果可正常生效,但会抛出运行时异常
- 退出应用(进程被杀)后,已设置的深色模式自动失效,默认切回浅色模式
原有实现代码
binding.darkModeBtn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked){ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); }else{ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); } } });
报错日志
E/ActivityInjector: get life cycle exception java.lang.ClassCastException: android.os.BinderProxy cannot be cast to android.app.servertransaction.ClientTransaction at android.app.ActivityInjector.checkAccessControl(ActivityInjector.java:24) at android.app.Activity.onResume(Activity.java:2011) at androidx.fragment.app.FragmentActivity.onResume(FragmentActivity.java:434) at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1456) at android.app.Activity.performResume(Activity.java:8297) at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4591) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4633) at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) at android.app.ClientTransactionHandler.executeTransaction(ClientTransactionHandler.java:58) at android.app.ActivityThread.handleRelaunchActivityLocally(ActivityThread.java:5595) at android.app.ActivityThread.access$3600(ActivityThread.java:263) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2197) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:236) at android.app.ActivityThread.main(ActivityThread.java:8057) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:620) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1011)
问题根因
- 运行时异常:调用
AppCompatDelegate.setDefaultNightMode()后系统会自动触发Activity本地重建,部分定制ROM(日志中带ActivityInjector的国产定制系统)修改了原生Activity重建流程,在本地重建逻辑中存在类型转换bug,触发系统级异常,该异常并非业务代码语法错误导致。 - 重启失效:
AppCompatDelegate.setDefaultNightMode()的配置仅在当前应用进程生命周期内生效,没有做持久化存储,进程被杀重启后会默认加载系统主题配置,自动回到浅色模式。
修复方案
- 新增主题配置持久化逻辑
切换主题时将选中的模式存入SharedPreferences,避免进程重启后配置丢失:
binding.darkModeBtn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { int targetMode = isChecked ? AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO; // 持久化存储主题配置 getSharedPreferences("app_config", MODE_PRIVATE) .edit() .putInt("key_night_mode", targetMode) .apply(); AppCompatDelegate.setDefaultNightMode(targetMode); } });
- 应用启动时提前加载主题配置
在自定义Application的onCreate方法中读取已存储的主题配置,在所有Activity创建前完成主题设置,避免启动时闪浅色:
public class BaseApplication extends Application { @Override public void onCreate() { super.onCreate(); // 提前读取存储的主题配置 int savedMode = getSharedPreferences("app_config", MODE_PRIVATE) .getInt("key_night_mode", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); AppCompatDelegate.setDefaultNightMode(savedMode); } }
需要在AndroidManifest.xml的application标签下配置name属性,指向自定义的BaseApplication类
- 手动控制Activity重建时机绕开系统bug
给切换按钮增加初始化状态判断,跳过页面首次加载时触发的默认回调,同时在设置主题后手动调用recreate()重建页面,避开系统自动重建流程的ROM bug:
binding.darkModeBtn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { // 标记是否为初始化阶段,避免首次进入页面触发不必要的重建 private boolean isInitializing = true; @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isInitializing) { isInitializing = false; return; } int targetMode = isChecked ? AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO; getSharedPreferences("app_config", MODE_PRIVATE) .edit() .putInt("key_night_mode", targetMode) .apply(); AppCompatDelegate.setDefaultNightMode(targetMode); // 延迟到视图状态更新完成后手动重建,避开系统本地重建的异常逻辑 buttonView.post(() -> recreate()); } });
多Activity架构建议在基类Activity的onCreate阶段增加主题一致性校验,如果当前页面主题和存储的配置不一致,及时调整避免页面主题错乱。
内容的提问来源于stack exchange,提问作者Praveen Kumar
相关产品推荐
相关产品推荐

