Skip to content
Charles Shao
Go back

广告 SDK 接入实战:内部架构与 Mediation 决策机制

Updated:
–views

在移动端广告变现的实战中,开发者往往会面临一系列棘手的工程痛点:为什么接入多家广告源后 App 的崩溃率飙升?为什么预加载的素材总是过期导致计费异常?又该如何在复杂的网络环境下确保广告的高填充率与低延迟?要解决这些问题,仅仅调用几个 API 是远远不够的,我们需要深入理解 SDK 的内部流转机制。本篇作为 App 广告 SDK 深挖 的实战与实现侧续篇,将跳出纯粹的理论概念,直接聚焦于具体的工程落地。

本文是 端上变现 系列的第 3 篇(接入与 Mediation)。 全系列 6 篇:

  1. App 广告 SDK 深挖
  2. 广告 SDK 隐性成本
  3. 广告 SDK 接入实战
  4. OEM 系统广告
  5. CTV / OTT 与 SSAI
  6. Web 变现深挖

一句话定位:从开发者视角全景拆解 App 广告 SDK 的接入流水线、内部七层架构以及 Mediation 混合统一竞价的决策机制。

TL;DR

Table of contents

Open Table of contents

1. App 怎么接入广告 SDK:一条固定流水线

无论开发者选择接入 AdMob、AppLovin MAX 还是 Unity LevelPlay,其底层的接入步骤几乎都遵循着同一条标准化的流水线。为了更清晰地展示这一过程,我们将以「聚合 SDK 搭配多家广告源 adapter」的典型业务场景为例,完整梳理整个接入链路。

1.1 第一步:加依赖(聚合 SDK + adapter)

需要强调的是,接入广告 SDK 从来不是简单地引入一个依赖包,而是构建一个「聚合 SDK + N 个广告源 adapter」的生态矩阵。在这个矩阵中,聚合 SDK 负责提供统一的对外接口与全局竞价决策,而每一个 adapter 则扮演着翻译官的角色,将统一的标准接口精准转换为特定广告源的私有通信协议。

Android(Gradle):

dependencies {
    // 聚合 SDK(这里以 AppLovin MAX 为例)
    implementation 'com.applovin:applovin-sdk:13.0.0'

    // 广告源 adapter:每接一家就多一个
    implementation 'com.applovin.mediation:google-adapter:24.0.0.0'
    implementation 'com.applovin.mediation:facebook-adapter:6.18.0.0'
    implementation 'com.applovin.mediation:mintegral-adapter:16.9.51.0'
    implementation 'com.applovin.mediation:unityads-adapter:4.12.5.0'
}

iOS(CocoaPods):

pod 'AppLovinSDK'
pod 'AppLovinMediationGoogleAdapter'
pod 'AppLovinMediationFacebookAdapter'
pod 'AppLovinMediationMintegralAdapter'
pod 'AppLovinMediationUnityAdsAdapter'

正因如此,每增加一家广告源的 adapter,App 就会相应地增加一截二进制包体、一份外部依赖以及一组 ProGuard 规则或 SO 动态库。这不仅是引发多 SDK 依赖冲突的工程根源,更是开发者在权衡「究竟该接入多少家广告源」时必须算清楚的第一笔技术账。

1.2 第二步:权限与合规前置

在发起任何实质性的广告请求之前,必须先跨越系统权限与隐私合规这两道门槛:

1.3 第三步:App 启动时异步初始化

初始化(init)环节往往是整条接入链路中最容易踩坑的雷区。由于初始化过程是异步的且伴随一定的网络耗时,如果在 SDK 尚未完全就绪时就贸然发起广告请求,必然会导致请求失败。因此,工程上的最佳实践是:必须在初始化完成的回调函数中发起请求,或者在业务层实现一套可靠的请求排队机制。

// Android / Kotlin —— 启动时初始化,回调里再请求
AppLovinSdk.getInstance(this).mediationProvider = "max"
AppLovinSdk.initializeSdk(this) { config ->
    // 各 adapter 已就绪,这里再安全地预加载首个广告
    preloadInterstitial()
}

在初始化的底层流转中,SDK 实际上正在并发执行拉取远程配置与唤醒各个 adapter 的操作。为了保证用户体验,这一过程绝对不能阻塞 App 冷启动的主线程。特别是在开屏广告等冷启场景下,开发者必须设定一个严格的硬超时阈值(业内通常设为 3 秒左右);一旦超时,必须果断放行用户进入 App 主界面,绝不能让用户面对白屏苦等 SDK 的响应(详细的超时治理策略可参考 App 广告 SDK 深挖 §5)。

1.4 第四步:用广告位 ID 请求 + 监听回调

广告位(ad unit ID)本质上是开发者在聚合平台后台创建的一个抽象逻辑节点。这个节点背后动态挂载着复杂的业务策略:究竟接入哪几家广告源、各家分配什么参数、是走传统瀑布流还是实时竞价(In-App Bidding)。由于这些策略全部在服务端进行配置,端上代码只需认准这一个唯一的 ID 即可。这种设计实现了端上逻辑与服务端策略的关键解耦,使得后续调整广告源或竞价策略时,完全不需要重新发布 App 版本。

val interstitial = MaxInterstitialAd("YOUR_AD_UNIT_ID", this)
interstitial.setListener(object : MaxAdListener {
    override fun onAdLoaded(ad: MaxAd) { /* 就绪,可择时 show */ }
    override fun onAdLoadFailed(unitId: String, error: MaxError) { /* 退避重试 */ }
    override fun onAdDisplayed(ad: MaxAd) {}
    override fun onAdHidden(ad: MaxAd) { interstitial.loadAd() } // 关闭后预加载下一个
    override fun onAdClicked(ad: MaxAd) {}
    override fun onAdDisplayFailed(ad: MaxAd, error: MaxError) {}
})
interstitial.loadAd()

从 API 层面来看,SDK 对外暴露的其实是一组异步回调接口与内部状态机。通过将 loadAd() 与 showAd() 彻底解耦,开发者可以在后台静默完成素材的预加载;等到合适的用户交互时机(例如游戏关卡结束或页面切换时),再调用 show() 进行展示。这种机制成功地将网络请求的延迟从用户的核心等待路径中剥离了出去。

1.5 第五步:渲染、上报、关闭与奖励

当调用 show() 之后,SDK 便会全面接管后续的渲染流程(包括 Banner 的内嵌渲染以及插屏或激励视频的全屏接管),并自动完成广告曝光(Impression)与点击(CTR)的数据上报,这些数据正是后续计费与对账的核心依据。

需要特别警惕的是,对于激励视频广告,SDK 会额外触发奖励发放的回调。为了防止黑产通过 Hook 客户端逻辑进行刷量作弊,奖励的最终发放必须严格以服务端到服务端(S2S)的回传校验为准,端上的回调仅仅用于更新 UI 状态(防作弊细节详见 App 广告 SDK 深挖 §6)。

1.6 第六步:测试与上线前自检

在将 App 推向生产环境之前,必须经过严密的自检流程:

2. SDK 内部是怎么实现的:你进程里跑着的七层架构

如果我们将一个看似黑盒的广告 SDK 从对外的 API 一路向下拆解到端上的底层执行,大体可以剥离出七层核心架构。深刻理解这张分层架构图,是开发者在面对「广告无法填充、计费数据异常、宿主 App 崩溃」等复杂故障时,能够精准定责与排查的前提。

App 广告 SDK 的内部分层架构图,从上到下七层:① 对外 API 层(load / show / 回调,开发者只接触这层);② 异步状态机(IDLE→LOADING→READY→SHOWING→CLOSED,load 与 show 解耦、预加载与展示解耦);③ 配置中心(App 启动时从服务端拉广告位↔广告源映射、瀑布顺序、bidding 开关、超时预算、A/B 分组,端上缓存、可热更不发版);④ Mediation 决策层(按配置组织瀑布串行 / In-App Bidding 并行询价 / 混合统一竞价,按 eCPM 排序选胜者,带全局超时收口);⑤ adapter 适配层(把统一接口翻译成每家广告源的私有协议,N 家广告源 = N 个 adapter,调用边界 try/catch + 超时 + 崩溃归因做故障隔离);⑥ 网络与缓存层(询价 / 取素材的 HTTP、素材预下载与缓存 TTL、重试退避);⑦ 渲染与上报层(banner 内嵌 / 插屏 / 激励全屏渲染,impression / click 上报,奖励 S2S 回传)。右侧竖条标注:整个 SDK 寄生在宿主 App 进程内,共享内存 / 线程 / 包体,任何崩溃 / OOM / ANR 都算在 App 头上 广告 SDK 的内部七层:对外 API → 状态机 → 配置中心 → Mediation 决策 → adapter 适配 → 网络 / 缓存 → 渲染 / 上报。整套都寄生在宿主 App 进程里。

沿着数据流转的方向逐层拆解,这七层架构分别承担着不同的使命:

  1. 对外 API 层:这是开发者唯一直接接触的边界,主要暴露 load、show 以及一组状态回调。这层接口被设计得极其轻薄,其目的就是将所有的异步复杂度深埋于底层。
  2. 异步状态机:内部维护着 IDLE → LOADING → READY → SHOWING → CLOSED 的核心流转,并辅以 FAILED 异常分支。预加载与展示的解耦正是由这台状态机驱动的;如果在 Activity 已销毁后依然强行调用 show 导致崩溃,其根源往往就是状态机未能与宿主 App 的生命周期严密对齐。
  3. 配置中心(remote config):在 App 启动时,该层会从服务端拉取一份包含「广告位映射、瀑布顺序、Bidding 开关、超时预算以及 A/B 分组」的策略清单,并缓存在端上。这构成了「调整广告源无需发版」的工程基石。
  4. Mediation 决策层:根据配置中心的策略,将候选广告源组织成传统的串行瀑布流或并行的 In-App Bidding,随后统一按 eCPM 排序决出胜者,并在此处执行全局的超时收口。
  5. adapter 适配层:针对每一家广告源部署一个专属的 adapter,负责将聚合 SDK 的标准指令翻译为对应网络的私有调用。更重要的是,核心的故障隔离机制就部署在这一层:通过调用边界的 try/catch 拦截、超时弃权策略以及崩溃面包屑归因,防止单一广告源的异常拖垮全局(详见 App 广告 SDK 深挖 §5 故障隔离)。
  6. 网络与缓存层:负责处理询价与素材拉取的 HTTP 请求,并管理素材的预下载、缓存生命周期(TTL)以及重试退避策略。实战中常见的「预加载素材过期导致计费异常」问题,往往就是这层的缓存治理出现了漏洞。
  7. 渲染与上报层:最终将广告素材(涉及 MRAID 或 VAST 协议)安全地渲染到屏幕上,并精准上报广告曝光(Impression)与点击(CTR)数据,同时处理激励视频的 S2S 奖励回传。

需要强调的是,这七层架构全部寄生于宿主 App 的进程之中,与 App 共享内存、线程池与包体空间。任何底层的 OOM 或 ANR,最终都会算在宿主 App 的头上。

2.1 配置中心:实现不发版换源的基石

在代码接入时,开发者仅仅填入了一个 ad unit ID,但这个 ID 背后却隐藏着庞大的服务端策略矩阵:究竟接入哪几家广告源、瀑布流的优先级顺序如何、是否开启 bidding 竞价、全局超时预算设为多少、以及流量的 A/B 分组规则。配置中心的存在,彻底将这套复杂的业务策略与端上的发版周期解耦:

正因如此,现代广告 SDK 绝非一个单纯的客户端代码库,而是一个深度融合了「端上执行引擎」与「服务端动态决策」的复杂系统。

3. Mediation 怎么对这些 SDK 做决策

当 App 成功接入了 AdMob、Meta、Mintegral 等多家广告源后,随之而来的核心挑战是:在每一次广告请求中,究竟应该向谁询价?谁又能给出最高的出价?这正是 Mediation(聚合层)需要解决的核心命题。其本质逻辑是将所有候选广告源拉到同一把 eCPM 的尺子上进行横向比对,确保价高者得。然而,工程上的难点在于:部分广告源支持返回实时竞价,而另一部分传统广告源却只能依赖后台的预设底价。

Mediation 决策的混合统一竞价流程图。输入:一次广告请求 + 该广告位配置好的候选集(分两类)。左路『瀑布候选』:广告源 A 预设 eCPM $10、B 预设 $6、C 预设 $3,这些是历史 / 后台配置的固定价,按价从高到低排成瀑布。右路『Bidding 候选』:广告源 X、Y、Z 支持 In-App Bidding,聚合层并行向它们实时询价,带全局超时预算(如 1s),到点用已收到的最高价收口,假设返回 X=$8、Y=$11、Z=$5。中间『统一竞价 / 排序』节点:把瀑布各档预设价($10/$6/$3)与 bidding 实时价($8/$11/$5)放进同一个有序列表按 eCPM 排序——$11(Y) > $10(A) > $8(X) > $6(B) > $5(Z) > $3(C)。决策:从最高价开始逐个尝试填充,谁先返回可用素材谁赢;若最高的 Y 这次无填充则顺延到 A。输出:胜出广告源的素材 → 渲染 → 上报。旁注:bidding 把『串行猜价』变成『并行真实竞价』,统一竞价让实时价和预设价在同一把尺子上比,是移动端的 Header Bidding Mediation 的混合统一竞价:瀑布的预设 eCPM 与 bidding 的实时价被拉进同一个有序列表,按 eCPM 排序、从高到低尝试填充。这就是移动端的 Header Bidding。

3.1 瀑布(Waterfall):按预设 eCPM 串行问

这是行业内最古老的竞价机制:聚合层完全依赖后台的人工配置或历史 eCPM 数据,为各家广告源排定一个静态的优先级顺序。在发起请求时,系统会从高到低进行串行询价,一旦某家广告源成功返回素材,整个流程便立即终止。

这种机制存在两个致命的工程痛点:首先,预设价格仅仅是历史估值而非实时出价,排在顺位靠后的广告源即便在某次请求中愿意给出更高的价格,也根本没有参竞的机会,导致媒体收益白白流失;其次,串行询价的模式不可避免地会拉长整体的网络延迟。

3.2 In-App Bidding:并行实时竞价

为了解决瀑布流的痛点,In-App Bidding 将传统的「串行猜价」彻底升级为「并行真实竞价」。在这种机制下,聚合层会同时向所有支持竞价的广告源发起询价,各家根据当前流量的价值返回一个真实的 bid,最终价高者得。这实际上就是 Web 端 Header Bidding 技术在移动 App 领域的完美复刻。

对比前一种方案,引入并行竞价的工程代价是:端上必须具备强大的并发控制能力,以管理多路异步的网络请求,并严格执行全局的超时预算。一旦到达设定的时间阈值,系统必须果断使用已收到的最高出价进行收口,无情地丢弃那些尚未返回的迟到者(关于超时收口的底层细节,请参阅 App 广告 SDK 深挖 §5)。

3.3 混合统一竞价:现实里两者并存

在真实的生产环境中,纯粹的 Bidding 或纯粹的瀑布流往往难以覆盖所有场景,两者几乎总是混合并存的。聚合层会将 Bidding 机制获取到的实时出价,与瀑布流中各档位的预设 eCPM 统一丢进一个全局的有序列表中,严格按照 eCPM 的高低进行重新排序,并从高到低逐一尝试填充。结合前文架构图中的具体数值,我们可以清晰地看到两者的收益落差:

机制这次发生了什么媒体到手
纯瀑布配置最高的 A($10)先问 → A 无填充 → 问 B($6)→ B 填充,命中即停$6
混合统一竞价bidding 实时价(X $8 / Y $11 / Z $5)与瀑布预设价($10/$6/$3)同排序 → 最高 Y $11 先填$11

这种巨大的收益差距源于:纯瀑布流的静态排序机制根本无法感知到「广告源 Y 在这一次请求中给出了高达 $11 的实时出价」,它在命中 B 的 $6 档位后就草草结束了交易。而混合统一竞价机制则打破了壁垒,让真实的实时出价与预设底价同台竞技,从而成功地将这部分流失的收益重新捞回。从底层逻辑来看,这与 媒体 Ad Server 的 dynamic allocation / 统一竞价 完全是同一道经典架构题在移动端的翻版。

3.4 决策之外:自动调价、实例与 A/B

除了针对单次请求的竞价比对之外,现代聚合平台还提供了一系列用于持续优化收益的工程旋钮:

总结而言,In-App Bidding 负责在实时竞价的候选池中决出最高出价,而混合统一竞价则负责将这个实时最高价与所有瀑布流的预设底价拉到同一把尺子上进行终极对决。

4. 哪些公司在做这些 SDK

在当前的程序化广告生态中,市面上的 SDK 厂商主要扮演着两类截然不同的角色。在实际的工程落地中,一个 App 通常会选择集成「一个聚合平台 SDK」加上「多家广告源 adapter」。

4.1 聚合平台(负责竞价决策)

这类平台是端上流量的调度中枢,负责制定竞价规则并分配流量。

平台母公司特点
AdMob / GAMGoogle装机量最大、广告源最广;与 Google 需求(AdX)深度打通
AppLovin MAXAppLovin游戏变现霸主,In-App Bidding 接入广度领先
Unity LevelPlay(原 ironSource)Unity游戏引擎自带,与 Unity Ads 一体;ironSource 合并后整合
Digital Turbine FairBid(原 Fyber)Digital Turbine老牌聚合,欧洲背景
TopOn江平智能国内主流聚合,聚合海内外广告源,开发者侧透明度高
GroMore字节跳动(穿山甲)国内聚合,与穿山甲 / Pangle 需求打通

4.2 广告源与网络(提供真实库存)

这类厂商是真正的买方代表,它们以 adapter 的形式被聚合平台挂载,为媒体提供真实的广告预算。

广告源母公司备注
Google Ads / AdMob 需求Google既是聚合平台也是最大需求源
Meta Audience NetworkMeta社交流量需求,强 bidding 支持
Mintegral汇量科技(Mobvista)国内出海主力广告源
Pangle / 穿山甲字节跳动抖音 / TikTok 系流量,出海 + 国内
Unity AdsUnity游戏内激励 / 插屏强势
Liftoff(含 Vungle)Liftoff视频 / 激励见长(Vungle 已并入 Liftoff)
InMobiInMobi老牌移动广告网络,亚洲背景
ChartboostZynga / Take-Two游戏起家的网络 + 轻量聚合
Moloco / Bigo / Kuaishou 等各自新兴或区域性 ML 驱动 / 短视频系需求

需要强调的是,在真实的商业生态中,这两类角色往往是高度重叠的。例如,Google、AppLovin 和 Unity 既是制定规则的聚合平台,又是手握海量预算的广告源。它们既在打造「决策的尺子」,又在不断地往这把尺子上输送自己的需求。这正是 程序化广告生态全景 中「一家公司戴多顶帽子」现象在移动端的真实翻版。正因如此,聚合平台作为「裁判兼运动员」的中立性时常受到行业的严厉审视(关于拍卖机制的公平性探讨,可参见 last look 与拍卖透明度)。

5. 接入实战:几个绕不开的坑

在实际的工程落地中,开发者往往会遭遇以下几个绕不开的技术暗礁:

关于更系统的端上工程取舍(涵盖内存治理、OOM / ANR 防范、故障隔离与超时收口等深度话题),请参阅 App 广告 SDK 深挖 §5–§6。

6. 常见误解 ↔ 正解

常见误解正解
接广告 SDK 就是简单加个包、调个 API实际上是接入聚合 SDK、多个 adapter、服务端配置与异步状态机的复杂合体
调整广告源策略必须依赖 App 发版广告位映射策略托管在服务端的配置中心,端上拉取后热更即生效
Mediation 聚合层一定在进行实时竞价传统瀑布流仅按预设 eCPM 串行询价;真正的实时竞价依赖 In-App Bidding
Bidding 竞价与瀑布流必须二选一生产环境中普遍采用混合统一竞价,实时出价与预设底价同台排序
聚合平台是绝对中立的流量裁判头部平台往往既当裁判又当运动员,向自己的聚合生态中输送自有需求
多接几家广告源必定能带来收益增长盲目增加广告源会导致包体膨胀、内存飙升以及崩溃风险加剧,需谨慎权衡
激励视频的奖励发放只需依赖端上回调为防范刷量作弊,奖励的发放必须严格以 S2S 服务端回传校验为准

行业黑话总结:聚合平台(Mediation)居中调度;adapter 翻译私有协议;广告源(Ad Network)提供真实预算;ad unit ID 解耦端上与服务端;配置中心(remote config)支撑热更不发版;Waterfall 按预设 eCPM 串行询价;In-App Bidding 驱动并行实时竞价;混合统一竞价合并排序价高者得;eCPM 衡量千次曝光收益;多实例(instance)占据多个瀑布档位;超时收口(timeout budget)斩断长尾延迟;故障隔离(fault isolation)兜底崩溃风险;S2S 服务端回传防范刷量作弊;SKAdNetwork 与 ATT 构筑隐私归因底座;CMP 采集用户合规同意;Mediation Debugger 端上排障兜底。

在彻底掌握了移动端 SDK 的接入流水线与内部流转机制后,为了更宏观地理解这些端上组件在整个广告交易链路中的位置,建议继续阅读本系列的母篇:App 广告 SDK 深挖。

延伸阅读


–views
Share this post on:

Previous Post
零售媒体网络(RMN):第一方购买数据驱动的第三波广告浪潮
Next Post
后 Cookie 身份层:确定性 ID、隐私沙盒与数据洁净室