PAYATHON 2026

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 在该环境中也不足以解决问题。

单个颜色值可以作为复现探针,但不能代表整个调色板。测试时应选择差异明显的系统配色,必要时同时记录多个系统颜色资源。

如何验证触发条件?

每组测试都先结束旧进程,再重新启动应用,避免上一组的初始化影响下一组。

  1. 保留 SDK 依赖和 Manifest 配置,移除显式的 MobileAds.initialize 调用,确认基线表现。
  2. 恢复初始化,但不主动请求或展示广告,确认是否仅初始化就足以触发。
  3. 在独立的最小测试项目中,不接入 AdMob,仅创建 WebView,再执行同样的调色板切换测试,验证 WebView 线索。
  4. 记录 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 中调用。也不要仅在检测到颜色值变化时才触发重建:当前问题可能让读取值一直不变,使这类判断无法生效。

重启应用可用于验证问题边界,但不宜通过自动杀进程实现配色刷新。发布临时方案前,应确认用户状态能够保留,并明确固定配色或关闭广告带来的功能取舍。

备注:内容仅供参考。