【BUG反馈】关于部分ios用户看广告会白屏的反馈报告

04/13145 浏览
iOS 激励广告白屏问题报告
游戏: 全村最好的剑
报告日期: 2026-04-13
════════════════════════════════════════════════════════════════
【问题描述】
用户在 iOS 设备上观看激励广告后,游戏画面变为白屏,无法恢复操作,只能强制杀进程重启。问题在同一设备上稳定复现,单次会话中观看 1~4 次广告后均可能触发。
【环境信息】
  设备:         iPhone 13
  系统:         iOS 17.5.1
  引擎运行时:   SCE runtime 2.15.0
  游戏引擎:     UrhoX (NanoVG 渲染)
  广告调用方式:  sdk.ShowRewardVideoAd()
  广告场景:     shop_ad_short / shop_free
【复现步骤】
  1. 进入游戏 → 商店页面
  2. 点击观看广告获取奖励
  3. 单次会话中观看 1~4 次广告后触发(不固定)
  4. 广告结束后画面变白,无法操作,只能强制退出
════════════════════════════════════════════════════════════════
【排查过程】
我们通过在 Lua 层植入持久化诊断日志(写入文件,下次启动时上报),进行了 3 轮渐进式排查,共收集 5 份白屏反馈。
---- 第一轮:每帧 GL blend 状态自愈(version=1)----
  假设: NanoVG 渲染的 GL blend 状态被广告 SDK 破坏,导致后续绘制全白。
  措施:
    - 植入持久化诊断日志,记录 APP_START、AD_START、AD_CALLBACK 时间戳
    - 在 NanoVGRender 事件中增加每帧 glBlendFunc 重置
  结果: 白屏仍然发生(feedback_1236 证明),排除了 GL blend 状态假设。
---- 第二轮:墙钟超时 + 心跳诊断(version=3)----
  假设: 引擎主循环(HandleUpdate)在广告展示期间可能被暂停,导致基于帧 dt 累加的超时计时器无法工作。
  措施:
    - 使用 os.time() 记录广告开始的墙钟时间
    - HandleUpdate 中添加心跳日志 AD_HEARTBEAT(广告期间每 3 秒写入一条)
    - 墙钟超时 10 秒 + 帧累加超时 8 秒双重保护
  结果: feedback_1238 中无任何 AD_HEARTBEAT 日志,证明 HandleUpdate 在广告期间完全没有被调用。
════════════════════════════════════════════════════════════════
【5 份反馈日志详细分析】
---- feedback_1234 ——"白屏"(无诊断版本)----
  预诊断构建,无 version 标记。仅包含正常启动日志,无上次会话记录。
  确认白屏问题存在,但无诊断数据。
---- feedback_1235 ——"白屏02"(version=1)----
  上次会话日志:
    21:39:06 APP_START version=1
    21:39:20 AD_START scene=shop_ad_short state=mainmenu
    21:40:10 AD_CALLBACK scene=shop_ad_short success=true     ← 第1次广告成功
    21:40:16 AD_START scene=shop_ad_short state=mainmenu
    21:40:48 AD_CALLBACK scene=shop_ad_short success=true     ← 第2次广告成功
    21:40:59 AD_START scene=shop_ad_short state=mainmenu
    21:41:40 AD_CALLBACK scene=shop_ad_short success=true     ← 第3次广告成功
    21:41:52 AD_START scene=shop_ad_short state=mainmenu      ← 第4次广告开始
    (无 AD_CALLBACK —— 白屏,用户强退)
  结论: 前 3 次广告正常,第 4 次 AD_START 后无回调,广告 SDK 未触发 callback。
---- feedback_1236 ——"白屏03"(version=1,含每帧 GL 自愈)----
  上次会话日志:
    21:52:25 APP_START version=1
    21:52:33 AD_START scene=shop_ad_short state=mainmenu      ← 第1次广告开始
    (无 AD_CALLBACK —— 白屏,用户强退)
  结论: 首次广告即白屏。此版本已包含每帧 GL blend 修复,仍然无效。
        排除了 GL 状态被污染的假设。
---- feedback_1237 ——"白屏04"(version=1)----
  上次会话日志:
    22:05:29 APP_START version=1
    22:05:38 AD_START scene=shop_ad_short state=mainmenu
    22:06:17 AD_CALLBACK scene=shop_ad_short success=true     ← 第1次广告成功
    22:06:20 AD_START scene=shop_free state=mainmenu          ← 第2次广告开始
    (无 AD_CALLBACK —— 白屏,用户强退)
  结论: 第 1 次成功,第 2 次 AD_START 后无回调。
---- feedback_1238 ——"白屏05"(version=3,关键证据)----
  上次会话日志:
    22:18:26 APP_START version=3                              ← 确认最新诊断版本
    22:18:34 AD_START scene=shop_free state=mainmenu          ← 第1次广告开始
    (无 AD_HEARTBEAT —— HandleUpdate 未运行)
    (无 AD_RECOVER  —— 墙钟超时未触发)
    (无 AD_TIMEOUT  —— 帧累加超时未触发)
    (无 AD_CALLBACK —— 白屏,用户 22:19:09 强退重启)
  关键分析:
    - 从 AD_START(22:18:34) 到用户重启(22:19:09),间隔 35 秒,远超 10 秒墙钟超时阈值
    - version=3 的心跳诊断设计:AD_START 后每 3 秒在 HandleUpdate 中写入 AD_HEARTBEAT
      35 秒内应有约 11 条心跳日志
    - 实际结果:0 条心跳日志
    - 结论:AD_START 之后 HandleUpdate 完全没有被调用,引擎主循环被广告 SDK 阻塞
════════════════════════════════════════════════════════════════
【证据链总结】
  feedback_1234 → 确认白屏问题存在(无诊断数据)
        ↓
  feedback_1235 → AD_START 后无 AD_CALLBACK,确认广告回调丢失
        ↓
  feedback_1236 → 每帧 GL blend 修复无效,排除 GL 状态假设
        ↓
  feedback_1237 → 再次确认 AD_START 后回调丢失模式
        ↓
  feedback_1238 → 墙钟超时 + 心跳诊断均未触发
               → 确认 HandleUpdate 在广告期间完全被阻塞
               → 确认问题在引擎/SDK 原生层,非 Lua 层可修
════════════════════════════════════════════════════════════════
【根因结论】
调用 sdk.ShowRewardVideoAd() 后,广告 SDK 在 iOS 上弹出原生 UIViewController(全屏覆盖游戏画面)。此时:
  1. 引擎的主循环(HandleUpdate / NanoVGRender 等事件)完全停止执行
  2. 广告 SDK 的 ViewController 在某些情况下未能正确 dismiss
  3. SDK 的 reward callback(success/fail)未被触发
  4. 游戏画面被广告 SDK 的白色 ViewController 覆盖,用户看到白屏
  5. 由于引擎主循环被阻塞,Lua 层的所有超时检测、心跳诊断、GL 恢复逻辑均无法执行
已排除的方案:
  每帧 GL blend 状态重置
    → 失败原因: 问题不是 GL 状态被污染,而是引擎主循环整体被阻塞
  帧累加超时(HandleUpdate 中 dt 累加)
    → 失败原因: HandleUpdate 不运行,timer 不推进
  墙钟超时(os.time 检测)
    → 失败原因: HandleUpdate 不运行,检测代码不执行
  心跳诊断
    → 失败原因: 确认了 HandleUpdate 不运行,但无法作为解决方案
此问题无法从 Lua 游戏代码层面解决。所有 Lua 层的恢复机制都依赖于引擎主循环(HandleUpdate)被调用,而在白屏发生时主循环已被完全阻塞。
════════════════════════════════════════════════════════════════
【建议排查方向】
1. 广告 SDK 的 ViewController 生命周期管理
   iOS 上 ShowRewardVideoAd 弹出的 ViewController 是否正确处理了 dismiss?
   ViewController dismiss 失败时是否有 fallback 机制?
2. 引擎主循环与广告 ViewController 的关系
   广告 ViewController 展示期间,引擎的 CADisplayLink / RunLoop 是否被暂停?
   是否可以在原生层(ObjC/Swift)增加 watchdog 计时器,独立于引擎主循环检测广告超时?
3. 广告回调的可靠性
   SDK 的 reward callback 是否存在已知的丢失场景?
   是否存在广告加载失败但未触发 failure callback 的边界情况?
4. 建议的原生层解决方案
   在原生层(独立于引擎主循环)启动 dispatch_after 或 NSTimer,
   检测广告 ViewController 是否在合理时间内 dismiss。
   如果超时,原生层主动 dismiss ViewController 并触发失败回调,
   确保引擎主循环在广告结束后能正确恢复。
以上分析来自塔拉拉的排查报告,希望天哥能帮忙看看!
对话链接:TapTap 制造 (测试时间在4/12号晚上,对话比较长需要往上翻一些)
9
1
4