Android 15 上初始化 AdMob 后 Material You 动态配色不更新,如何规避?
结论
从给出的复现结果看,应优先排查 Android 15 上广告初始化与动态颜色资源更新的兼容性问题。连 getColor(android.R.color.system_accent1_500) 读到的都是旧值,因此单纯重绘界面或重新应用主题未必有效。
现有证据不足以确定根因,也无法给出经过验证的修复版本。临时方案是在受影响环境中使用固定配色,或在允许关闭广告的场景中避免广告初始化。延迟初始化也可以尝试,但其缓解效果仍需验证。
为什么不能直接认定为主题配置错误?
复现中有三个现象:
- 相同项目移除
MobileAds.initialize(this)后恢复正常。 - 异常不仅出现在界面上,直接读取系统颜色资源也得到旧值。
- 重启应用后才能取得新颜色。
这些现象表明,有必要沿资源更新路径继续排查,但还无法确定是某个资源缓存或 AdMob 内部实现导致了问题。
Google Mobile Ads SDK 开发者讨论中有类似报告。支持回复称会将问题转交团队,也有人反馈问题可能与 WebView 有关。这些都是进一步复现的线索,不能视为官方确认的根因或修复方案。
题目给出的 Issue Tracker 记录在本次访问中未展示可核验的详情,因此不能据此判断修复状态。Android 14、Android 16 表现正常的结论也应限定在已测试的 emulator 环境内。
先区分资源未更新和界面未刷新
在现有 MainActivity 中加入以下诊断代码,并在原有 onCreate 中、调用 MobileAds.initialize(this) 之前增加一次 logDynamicColor("onCreate"):
private fun logDynamicColor(stage: String) {
val color = getColor(android.R.color.system_accent1_500)
val activityId = System.identityHashCode(this)
val colorHex = Integer.toHexString(color)
Log.d(
"DynamicColorCheck",
"$stage pid=${android.os.Process.myPid()} " +
"activity=$activityId color=0x$colorHex"
)
}
override fun onResume() {
super.onResume()
logDynamicColor("onResume")
}
需要同时观察进程、Activity 实例和颜色值。如果只在 onCreate 打印日志,就无法覆盖 Activity 没有重建、直接恢复到前台的情况。
切换系统调色板后,可以按以下方式判断:
- 颜色值已变化,但界面仍显示旧颜色:检查应用是否长期缓存了颜色、
ColorScheme或主题派生对象,以及界面是否重新读取这些值。 onResume仍读到旧颜色:仅刷新界面不足以解决问题,需要继续检查资源更新路径。- Activity 已重建,读取值仍旧,但结束进程后重新启动可以更新:说明只重建 Activity 在该环境中也不足以解决问题。
单个颜色值可以作为复现探针,但不能代表整个调色板。测试时应选择差异明显的系统配色,必要时同时记录多个系统颜色资源。
如何验证触发条件?
每组测试都先结束旧进程,再重新启动应用,避免上一组的初始化影响下一组。
- 保留 SDK 依赖和 Manifest 配置,移除显式的
MobileAds.initialize调用,确认基线表现。 - 恢复初始化,但不主动请求或展示广告,确认是否仅初始化就足以触发。
- 在独立的最小测试项目中,不接入 AdMob,仅创建 WebView,再执行同样的调色板切换测试,验证 WebView 线索。
- 记录 Android 系统构建、Android System WebView 提供程序及版本、Ads SDK 版本,并对比 Android 14、15、16。
如果仅创建 WebView 也能复现,就更有理由怀疑共用的平台路径,但仍需进一步的证据才能确定具体根因。
可以采用哪些临时方案?
在受影响环境中使用固定配色
如果必须保留广告功能,可以在已复现问题的环境中暂时关闭动态配色。若项目使用 Jetpack Compose Material 3,可在现有主题函数中采用类似判断:
val useDynamicColor = dynamicColor &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
Build.VERSION.SDK_INT != Build.VERSION_CODES.VANILLA_ICE_CREAM
val colorScheme = when {
useDynamicColor && darkTheme -> dynamicDarkColorScheme(context)
useDynamicColor -> dynamicLightColorScheme(context)
darkTheme -> darkColorScheme()
else -> lightColorScheme()
}
其中 dynamicColor、darkTheme 和 context 使用项目现有变量,固定色板可替换为项目已有的品牌配色。这段代码需要支持相应 API 常量的 compileSdk。
该方案会牺牲 Android 15 的动态配色能力,也不会修复系统颜色资源更新问题。
在允许关闭广告的模式中避免触发
题目已验证,移除初始化调用后表现恢复正常。因此,在不需要广告的模式中避免该调用,有当前复现结果作为依据。
如果要进一步验证“完全不接入广告 SDK”的情况,应使用移除相关依赖和配置的独立构建。仅移除一处调用,不能证明应用中没有其他初始化入口。
将延迟初始化作为实验
可以尝试等到真正需要广告时再初始化,以减少启动阶段的影响,但仍需测试初始化后切换调色板的行为。如果初始化后仍然失效,延迟初始化只是推迟了触发时间。
不要把 Activity 重建当作保证有效的修复
Android 通常通过配置变化后的 Activity 重建重新加载资源,具体机制见 Android 配置变化文档。但 recreate() 重建的是 Activity,不等同于结束进程后重新启动应用。
调试时可以手动执行一次 recreate(),观察资源是否恢复;不要未经验证就在每次 onResume 中调用。也不要仅在检测到颜色值变化时才触发重建:当前问题可能让读取值一直不变,使这类判断无法生效。
重启应用可用于验证问题边界,但不宜通过自动杀进程实现配色刷新。发布临时方案前,应确认用户状态能够保留,并明确固定配色或关闭广告带来的功能取舍。
备注:内容仅供参考。
