Android日夜间切换黑一帧问题

1,881 阅读3分钟

最近在测试中发现使用了Android官方提供的暗黑模式方案会出现,切换日夜间模式时闪一帧黑色的问题,故研究一下

一、官方暗黑(深色)模式框架

1.说明

深色模式设置可以从三个层级设置,分别是系统层、Applcation层以及Activity层。底层的设置会覆盖上层的设置,例如系统设置了深色模式,但是Application设置了浅色模式,那么应用会显示浅色主题。

通过AppCompatDelegate.getDefaultNightMode()可以获取五种深色模式场景:

切换主题通过AppCompatDelegate.setDefaultNightMode(MODE)来实现

2. Force Dark

forceDark是Android提供的一键适配夜间模式方案,对于没有使用DayLight主题的应用,通过系统底层去更改颜色来实现夜间模式,

开启方式:

设置主题的android:forceDarkAllowed为true,则应用会随着系统的暗黑模式切换而变化

需要注意:

  1. 要强制深色模式生效必须开启硬件加速(默认开启)
  2. 主题设置的Force Dark仅对Light的主题有效,对非Light的主题不管是设置android:forceDarkAllowedtrue或者设置View.setForceDarkAllowed(true)都是无效的。
  3. 父节点设置了不支持Force Dark,那么子节点再设置支持Force Dark无效。例如主题设置了android:forceDarkAllowedfalse,则View设置View.setForceDarkAllowed(true)无效。同样的,如果View本身设置了支持Force Dark,但是其父layout设置了不支持,那么该View不会执行Force Dark
3. 重建应用

系统切换暗黑模式会触犯Activity的重建,如果不希望Activity重建,则可以指定 android:configChanges="uiMode",然后重写 onConfigurationChanged() 方法,自己来处理切换逻辑。

二、闪黑问题

经过线下测试:同样的Andorid11设备 32和64位处理器都会出现闪黑问题,所以跟arm架构无关

又试了下Android12和Android13正常,那recreate导致闪屏结论就是跟Android系统有关,Android11及以下的设备必定会出现

试了很多Google系列的 app效果也是如此

三、根本原因

其实从现象不难推测,这肯定又是google搞得bug

从issue上来看在20年年底已经修复,按时间线来看是合入了Android12的分支

issuetracker.google.com/issues/1327…

但是我反查了一圈Activity和ActivityThread中关于recreate调用栈的提交记录,并没有发现有20年左右的有关提交

猜测之前的方案应该是把view又重新附着在window上,导致漏出了底部的window颜色

四、解决方案

1. 切换暗黑模式实现:

抛弃系统的暗黑模式方案,自己实现更换主题功能,业内有以下方案:

1. Android-Skin-Loader框架解析

2.Android-skin-support解析

3.系统暗黑模式

三种方案的对比:

系统暗黑模式Android-Skin-LoaderAndroid-skin-suport
最新有效更新时间向后兼容20172020
侵入性较低相对较高相对较低
支持加载皮肤包
首次接入成本相对较低-较高
Target版本29+-30以上有兼容问题
2.设置uiMode,不自动重建

在AndroidManifest.xml中指定 android:configChanges="uiMode",然后重写 onConfigurationChanged() 方法

  • 手动重启Activity

本身就没有动画可以设置,亲测有效

val intent = Intent(this, BottomActivity::class.java)
startActivity(intent)
overridePendingTransition(0,0)
finish()
  • 更新界面所有背景色

因为一般暗色模式的设置界面都是没有数据请求的简单界面,所以可以直接改变Activity所有view的颜色,该方案比较简单可根据项目自行实现