build-ipa/ironSource/PlayBTopOn/SERIAL_LOAD_LOGIC.md
2026-01-05 10:40:05 +08:00

6.6 KiB
Raw Blame History

串行加载广告逻辑说明

🔄 新的加载逻辑

之前的问题

  • 一次性加载 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() 方法