# 串行加载广告逻辑说明 ## 🔄 新的加载逻辑 ### 之前的问题 - ❌ 一次性加载 3 个广告(并行) - ❌ 浪费资源和流量 - ❌ 可能导致频率限制 - ❌ 定时器轮询检查是否可以展示 ### 现在的改进 - ✅ 一个一个加载(串行) - ✅ 加载失败 → 自动加载下一个 - ✅ 加载成功 → 立即展示 - ✅ 不需要定时器 - ✅ 节省资源和流量 ## 📊 完整流程 ### 场景 1: 第一个广告就成功 ``` 1. 初始化队列: [A, B, C] ↓ 2. 加载广告 A ↓ 加载成功 (ecpm 满足条件) 3. 自动展示广告 A ✓ ↓ 4. 清空队列(不再加载 B 和 C) ↓ 5. 用户关闭广告 ↓ 6. 结束 ``` ### 场景 2: 第一个广告失败,第二个成功 ``` 1. 初始化队列: [A, B, C] ↓ 2. 加载广告 A ↓ 加载失败 ✗ 3. 从队列取出下一个: B ↓ 4. 加载广告 B ↓ 加载成功 (ecpm 满足条件) 5. 自动展示广告 B ✓ ↓ 6. 清空队列(不再加载 C) ↓ 7. 用户关闭广告 ↓ 8. 结束 ``` ### 场景 3: 广告 ecpm 不足 ``` 1. 初始化队列: [A, B, C] ↓ 2. 加载广告 A ↓ 加载成功,但 ecpm < 阈值 ✗ 3. 从队列取出下一个: B ↓ 4. 加载广告 B ↓ 加载成功 (ecpm 满足条件) 5. 自动展示广告 B ✓ ``` ### 场景 4: 展示失败 ``` 1. 加载广告 A → 加载成功 ↓ 2. 尝试展示 → didFailToDisplayAd ↓ "Cannot engage offer" 3. 触发 onAdClosed 回调 ↓ 4. 检查队列是否还有广告 ↓ 如果有 5. 1秒后加载下一个 (B) ↓ 6. 加载成功 → 自动展示 B ✓ ``` ### 场景 5: 所有广告都失败 ``` 1. 队列: [A, B, C] ↓ 2. 加载 A → 失败 → 加载 B → 失败 → 加载 C → 失败 ↓ 3. 队列为空 ↓ 4. 日志: "广告队列已空,所有广告都已尝试加载" ``` ## 🔑 关键实现 ### 1. 串行加载队列 ```swift // 广告 ID 队列 private var adIdQueue: [String] = [] // 当前正在加载的广告 private var currentLoadingAdId: String? // 是否正在加载 private var isLoadingAd = false ``` ### 2. 加载下一个广告 ```swift func loadNextAd() { // 防止重复加载 if isLoadingAd { return } // 队列为空,结束 if adIdQueue.isEmpty { return } // 取出下一个 let nextAdId = adIdQueue.removeFirst() isLoadingAd = true // 开始加载 adItems[nextAdId]?.load() } ``` ### 3. 状态回调自动处理 ```swift adManager.onStatusChange = { id, st, ecpm in if st == 2 { // 加载完成 → 自动展示 self.autoShowLoadedAd(adId: id) } else if st == 5 { // 加载失败 → 加载下一个 self.loadNextAd() } } ``` ### 4. 自动展示逻辑 ```swift private func autoShowLoadedAd(adId: String) { isLoadingAd = false // 检查 ecpm if ad.ecpm < adbrush_ecpm { loadNextAd() // 不满足,加载下一个 return } // 展示广告 let showResult = ad.show(viewController) { self.isshow = false // 关闭后,如果队列还有广告,继续加载 if !self.adIdQueue.isEmpty { self.loadNextAd() } } if showResult { self.isshow = true adIdQueue.removeAll() // 成功后清空队列 } else { loadNextAd() // 失败,加载下一个 } } ``` ## 📝 日志示例 ### 正常流程 ``` XS- 初始化广告队列: ["A", "B", "C"] XS- 添加广告位: A XS- 添加广告位: B XS- 添加广告位: C XS- 开始加载广告 [1/3]: A XS- ad load ok: A ecpm:0.05 XS- 广告加载完成,准备自动展示: A XS- 广告满足条件,准备展示: ecpm=0.05 >= 0.0005 XS- ✓ 广告展示成功 XS- ✓✓✓ didDisplayAd 回调被触发: A XS- 开始上报 uploadAD_Show: A ``` ### 第一个失败,加载第二个 ``` XS- 开始加载广告 [1/3]: A XS- load A err: timeout XS- 广告加载失败,尝试加载下一个 XS- 开始加载广告 [2/3]: B XS- ad load ok: B ecpm:0.05 XS- 广告加载完成,准备自动展示: B XS- ✓ 广告展示成功 ``` ### ecpm 不足 ``` XS- 开始加载广告 [1/3]: A XS- ad load ok: A ecpm:0.0003 XS- 广告 ecpm 不足: 0.0003 < 0.0005,尝试下一个 XS- 开始加载广告 [2/3]: B ``` ## 🎯 优势 | 对比项 | 旧逻辑(并行) | 新逻辑(串行) | |-------|-------------|-------------| | 加载方式 | 同时加载 3 个 | 一个一个加载 | | 资源消耗 | 高 | 低 | | 流量消耗 | 高 | 低 | | 触发展示 | 定时器轮询 | 加载完立即展示 | | 响应速度 | 慢(等待定时器) | 快(立即展示) | | 成功率 | 低(频率限制) | 高(按需加载) | | 失败处理 | 等待定时器重试 | 立即加载下一个 | ## ⚙️ 配置说明 ### 不再需要的配置 以下代码已经不再起作用(保留是为了兼容性): ```swift // YL_PlayVC.swift DispatchQueue.main.asyncAfter(deadline: .now() + 4) { BbbAdManager.shared.start() // 不再需要 } // bbbAdManager.swift func start() { // 新逻辑下不需要定时器 } func showAd(v:UIViewController) { // 新逻辑下由 autoShowLoadedAd 自动处理 } ``` ### 新的流程 ```swift // YL_PlayVC.swift BbbAdManager.shared.waitForSDKInitialization { BbbAdManager.shared.loadAd(view: self) // loadAd 会自动开始串行加载 // 加载完成后自动展示 // 不需要调用 start() } ``` ## 🔧 可选优化 ### 1. 调整展示失败后的延迟 ```swift // 在 autoShowLoadedAd 的关闭回调中 DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { // 可调整延迟时间 strongSelf.loadNextAd() } ``` ### 2. 添加最大重试次数 ```swift private var maxLoadAttempts = 3 private var currentAttempt = 0 func loadNextAd() { if currentAttempt >= maxLoadAttempts { NSLog("达到最大重试次数,停止加载") return } currentAttempt += 1 // ... 继续加载 } ``` ### 3. 记录失败原因 ```swift private var failedAds: [String: String] = [:] // adId -> error // 在加载失败时记录 failedAds[adId] = error.localizedDescription ``` ## ✅ 总结 新的串行加载逻辑: - ✅ 更节省资源(按需加载) - ✅ 响应更快(加载完立即展示) - ✅ 成功率更高(避免频率限制) - ✅ 逻辑更清晰(状态驱动) - ✅ 日志更完整(每一步都有记录) 重新编译运行后,你会看到广告一个一个地加载和展示! --- **修改时间:** 2025-01-04 **主要改进:** 并行 → 串行,定时器轮询 → 事件驱动 **向后兼容:** ✅ 保留了 start() 和 showAd() 方法