315 lines
6.6 KiB
Markdown
315 lines
6.6 KiB
Markdown
# 串行加载广告逻辑说明
|
||
|
||
## 🔄 新的加载逻辑
|
||
|
||
### 之前的问题
|
||
- ❌ 一次性加载 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() 方法
|