6.6 KiB
6.6 KiB
串行加载广告逻辑说明
🔄 新的加载逻辑
之前的问题
- ❌ 一次性加载 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. 串行加载队列
// 广告 ID 队列
private var adIdQueue: [String] = []
// 当前正在加载的广告
private var currentLoadingAdId: String?
// 是否正在加载
private var isLoadingAd = false
2. 加载下一个广告
func loadNextAd() {
// 防止重复加载
if isLoadingAd { return }
// 队列为空,结束
if adIdQueue.isEmpty { return }
// 取出下一个
let nextAdId = adIdQueue.removeFirst()
isLoadingAd = true
// 开始加载
adItems[nextAdId]?.load()
}
3. 状态回调自动处理
adManager.onStatusChange = { id, st, ecpm in
if st == 2 {
// 加载完成 → 自动展示
self.autoShowLoadedAd(adId: id)
}
else if st == 5 {
// 加载失败 → 加载下一个
self.loadNextAd()
}
}
4. 自动展示逻辑
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 个 | 一个一个加载 |
| 资源消耗 | 高 | 低 |
| 流量消耗 | 高 | 低 |
| 触发展示 | 定时器轮询 | 加载完立即展示 |
| 响应速度 | 慢(等待定时器) | 快(立即展示) |
| 成功率 | 低(频率限制) | 高(按需加载) |
| 失败处理 | 等待定时器重试 | 立即加载下一个 |
⚙️ 配置说明
不再需要的配置
以下代码已经不再起作用(保留是为了兼容性):
// YL_PlayVC.swift
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
BbbAdManager.shared.start() // 不再需要
}
// bbbAdManager.swift
func start() {
// 新逻辑下不需要定时器
}
func showAd(v:UIViewController) {
// 新逻辑下由 autoShowLoadedAd 自动处理
}
新的流程
// YL_PlayVC.swift
BbbAdManager.shared.waitForSDKInitialization {
BbbAdManager.shared.loadAd(view: self)
// loadAd 会自动开始串行加载
// 加载完成后自动展示
// 不需要调用 start()
}
🔧 可选优化
1. 调整展示失败后的延迟
// 在 autoShowLoadedAd 的关闭回调中
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { // 可调整延迟时间
strongSelf.loadNextAd()
}
2. 添加最大重试次数
private var maxLoadAttempts = 3
private var currentAttempt = 0
func loadNextAd() {
if currentAttempt >= maxLoadAttempts {
NSLog("达到最大重试次数,停止加载")
return
}
currentAttempt += 1
// ... 继续加载
}
3. 记录失败原因
private var failedAds: [String: String] = [:] // adId -> error
// 在加载失败时记录
failedAds[adId] = error.localizedDescription
✅ 总结
新的串行加载逻辑:
- ✅ 更节省资源(按需加载)
- ✅ 响应更快(加载完立即展示)
- ✅ 成功率更高(避免频率限制)
- ✅ 逻辑更清晰(状态驱动)
- ✅ 日志更完整(每一步都有记录)
重新编译运行后,你会看到广告一个一个地加载和展示!
修改时间: 2025-01-04
主要改进: 并行 → 串行,定时器轮询 → 事件驱动
向后兼容: ✅ 保留了 start() 和 showAd() 方法