在程序化广告生态全景和媒体 Ad Server 里,“App 广告 SDK”反复作为”端上发起请求的那个扳机”出现——网页那侧是 GPT + Prebid.js,App 这侧就是广告 SDK。但它在端上到底做了什么、怎么在多家广告源之间挑价、又为什么是个棘手的客户端工程问题,一直没单独讲。这篇补上。
我们从一次广告的生命周期讲起,过一遍广告形式,重点拆聚合(Mediation)的瀑布 vs In-App Bidding(这就是移动端的 Header Bidding),再看它作为端上组件绕不开的工程内核,最后划清它和归因 / 隐私的边界。
范围说明:本篇聚焦变现 SDK 的端上行为与聚合机制。素材渲染协议(MRAID / VAST)、归因(SKAdNetwork / MMP)会各有专文展开,这里只点到为止、给出指针。
TL;DR
- App 广告 SDK = 移动端变现的”扳机”:发起广告请求 → 聚合多家广告源 → 缓存 / 渲染素材 → 上报曝光与点击。等价于网页的 GPT + Prebid.js,只是活在客户端进程里、约束多得多。
- 一次广告是一条端上生命周期:初始化 → 请求 → 加载 / 预缓存 → 渲染 → 展示 / 点击回调 → 计费与上报。每一步都有失败与超时分支。
- 聚合(Mediation)解决”找谁要广告”:传统瀑布(waterfall)按预设 eCPM 串行问,效率低、价被低估;In-App Bidding 让多家广告源并行实时竞价——这就是 Header Bidding 在 App 里的对应物。
- In-App Bidding 与瀑布常并存:竞价价和瀑布层一起进聚合层比价,原理和媒体 Ad Server 的统一竞价同源。
- 它本质是个棘手的客户端工程问题:初始化延迟、预加载与缓存、内存 / OOM、ANR / 主线程、多 SDK 共存冲突、包体积、版本碎片、弱网超时——比”发个 HTTP 请求”复杂得多。
Table of contents
Open Table of contents
1. 定位:端上的”变现扳机”
一个 App 想靠广告赚钱,得在自己的进程里塞进一段别人家的代码——广告 SDK。它对开发者暴露的接口很朴素(“给我一个插屏广告”),背后却要替你完成一整条链路:向服务器要广告、在多家广告源里挑价、把素材下载好、在合适时机渲染出来、再把”用户看了 / 点了”如实上报回去用于计费。
把它和网页那侧对齐就清楚了:
| 网页(Web) | App(移动端) | |
|---|---|---|
| 发起请求的”扳机” | GPT(Google Publisher Tag) | 广告 SDK |
| 实时竞价 | Header Bidding / Prebid.js | In-App Bidding(见 §4) |
| 聚合 / 决策 | 媒体 Ad Server(GAM) | Mediation(聚合层) |
| 运行环境 | 浏览器沙箱 | 宿主 App 进程(内存 / 线程 / 包体共享) |
关键差异:网页的广告标签跑在浏览器沙箱里,崩了顶多广告位空白;App 广告 SDK 直接跑在宿主 App 的进程里——它的内存泄漏、ANR、崩溃会拖垮整个 App。 这把”做一个广告 SDK”从前端活儿变成了严肃的客户端工程问题(§6)。
市面上的 SDK 大体两类,常常一个 App 同时集成好几家:
- 变现聚合 SDK:AdMob、AppLovin MAX、ironSource LevelPlay、TopOn / 穿山甲聚合等——负责”聚合 + 决策”。
- 广告源 / 网络 SDK(adapter):各家 ad network 的 SDK,被聚合 SDK 以 adapter 形式挂载、统一调度。
2. 一次广告的生命周期:SDK 在端上做什么
把”展示一个广告”拆成端上的一条生命周期——每一步都有成功、失败、超时三种走向:
一次广告的端上生命周期:初始化 → 请求 → 聚合挑价 → 加载 / 缓存 → 就绪回调 → 渲染 → 曝光 / 点击上报 → 关闭。每一步都要处理失败与超时。
逐步拆:
- 初始化(init):App 启动时调一次。SDK 拉远程配置、初始化各广告源 adapter。这一步异步且有耗时,没就绪就请求广告会失败——所以 SDK 通常要”初始化完成回调 + 请求排队”。
- 请求(load):App 在某个时机(如关卡结束)向 SDK 要某个广告位的广告。
- 聚合挑价:SDK 经聚合层在多家广告源里挑——走瀑布或 In-App Bidding(§4)。
- 加载 / 预缓存:选中后下载素材(图片 / 视频 / HTML)。激励视频、插屏这类”重”素材,几乎都提前预加载好,等到要展示时秒开。
- 就绪回调:素材备好,回调
onAdLoaded;失败则onAdFailedToLoad(错误码)。 - 渲染 / 展示(show):App 在合适时机调
show(),SDK 把素材渲染到屏幕(banner 内嵌、插屏 / 激励全屏 Activity)。 - 曝光 / 点击上报:展示达标即上报 impression,用户点击上报 click 并打开落地页。这些回传是计费与对账的依据,差一条就是钱的出入。
- 关闭 / 奖励:广告关闭回调
onAdClosed;激励视频额外回调”奖励发放”(必须看完才给)。
对开发者而言,SDK 是一组异步回调 + 状态机:load 与 show 解耦、预加载与展示解耦。理解这套回调时序,是排查”广告不出 / 不计费 / 奖励不发”的前提。
3. 广告形式:每种都有自己的工程脾气
同一套 SDK 要支持多种广告形式,它们在加载时机、渲染方式、变现效率上差别很大:
| 形式 | 渲染 | 典型场景 | 工程要点 |
|---|---|---|---|
| Banner(横幅) | 内嵌在页面里的小条 | 列表 / 底部常驻 | 自动刷新节奏、不阻塞滚动 |
| Interstitial(插屏) | 全屏、可关闭 | 关卡间、页面切换 | 必须预加载、控制频次防打扰 |
| Rewarded(激励视频) | 全屏视频、看完给奖励 | 游戏复活 / 解锁 | 预加载、奖励回调防作弊、必须看完 |
| Native(原生) | 素材拆成字段、由 App 用自有 UI 拼 | 信息流 | 渲染交给 App,要防”假原生”和误点 |
| Splash(开屏) | App 冷启时全屏 | 启动页 | 冷启超时预算极紧,超时必须秒放行进 App |
| App Open / 信息流 | 回前台 / 流内 | 切回 App、内容流 | 频次与体验平衡 |
变现效率上,激励视频与插屏的 eCPM 通常远高于 banner,所以它们是聚合与 In-App Bidding 竞争最激烈的战场。渲染细节(MRAID 可交互、VAST 视频、可见性测量)涉及具体协议,另文展开,这里不铺开。
4. 聚合(Mediation):瀑布 vs In-App Bidding
一个广告位通常接了好几家广告源(AdMob、Meta、Mintegral、Unity Ads…),“这次该问谁、谁的价高”就是**聚合层(Mediation)**要解决的。它和媒体 Ad Server 解决的是同一类”分配”问题,只是发生在端上、面向移动广告源。两种机制:
两种机制对比:传统瀑布按预设 eCPM 串行问、命中即停;In-App Bidding 并行实时竞价、价高者得。后者就是移动端的 Header Bidding(每栏图内自带标题)。
4.1 传统瀑布(Waterfall)
聚合层按预先配置的 eCPM 给广告源排个序,从高到低串行问:“A 有没有?没有问 B……”命中即停。两个老问题:
- 价是”估”的不是”实时”的:排序靠历史 eCPM,某家这次其实愿出更高价,却因为排在后面、前面有人填充了而没机会——钱漏了(和媒体 Ad Server 那篇”瀑布漏钱”是同一个病)。
- 串行 = 延迟高:一个个问,链路长、超时风险大。
4.2 In-App Bidding(移动端的 Header Bidding)
In-App Bidding 把”串行猜价”改成”并行真实竞价”:聚合层同时向所有支持竞价的广告源询价,各自回一个真实 bid,价高者得。这正是 Header Bidding 在 App 里的对应物——动机、收益、复杂度都一一对应:
- 更高收益:真实价 PK,不再被历史 eCPM 低估;
- 更公平:各家同台、同时出价;
- 代价:端上要并行管理多路网络请求与超时预算(和 Prebid 的 timeout budget 一个道理)。
用图里的数字算笔账(同一次曝光,瀑布配置 A $10 > B $6 > C $3,但这次各家真实愿出价是 A $7、B $9、C $4):
| 机制 | 发生了什么 | 媒体到手 |
|---|---|---|
| 瀑布 | 先问 A(配置最高)→ 这次 A 无填充 → 问 B → B 填充,命中即停 | $6 |
| In-App Bidding | A/B/C 并行实时出价 $7 / $9 / $4 → B 价高者得 | $9 |
差距 +50%:瀑布的静态排序(按历史 eCPM)根本不知道”B 这次其实值 $9”,命中 B 的 $6 档就停了;In-App Bidding 让真实价同台,才把这 $3 捞回来。这和媒体 Ad Server 那篇”纯瀑布漏掉高价竞价”是同一道题的两种载体。
4.3 两者并存:混合聚合
现实里 In-App Bidding 与瀑布常并存:竞价拿到的实时价,和瀑布层各档的预设 eCPM 一起丢进聚合层比较,价高者得。这套”实时价 + 固定层一起比”的混合,原理和媒体 Ad Server 的 dynamic allocation / 统一竞价同源——都是让”实时竞价”和”预设价”在同一把尺子上较量。
主流聚合平台:Google AdMob、AppLovin MAX、Unity LevelPlay(原 ironSource)、国内的 TopOn / GroMore 等。它们的核心竞争力就在于 In-App Bidding 的接入广度、决策质量与端上性能。
5. 工程内核:它是别人进程里的”房客”
广告 SDK 最难的地方不在”发请求”,而在它寄生在宿主 App 进程里——任何资源占用、崩溃、卡顿都算在 App 头上。几条硬约束:
- 初始化延迟:init 要拉配置、起多个 adapter,不能阻塞 App 冷启——目标是几百毫秒内不卡主线程。要异步初始化、请求排队,并给冷启 / 开屏一个硬超时(业内常设在 ~3s 上下,到点就放行进 App,绝不能白屏卡住用户)。
- 预加载与缓存:插屏 / 激励视频素材”重”,必须提前下好缓存,否则 show 时转圈。预加载又要权衡流量与存储,还要管缓存过期(拿到过期素材会计费异常)。
- 内存 / OOM:视频、大图素材是内存大户,多广告源 SDK 叠加更甚。渲染容器、WebView、播放器用完要及时释放,否则拖垮宿主 App、引发 OOM 崩溃。
- 主线程 / ANR:渲染、I/O、回调绝不能堵主线程,否则触发 ANR(Android)/ 卡顿掉帧。重活儿挪到工作线程,回调切回主线程。
- 多 SDK 共存冲突:一个 App 集成 N 家广告源 adapter,依赖版本、SO 库、权限、ProGuard 规则容易打架;还要防一家 adapter 崩溃带崩整个聚合层(故障隔离)。
- 包体积:每多一家 adapter 就多一截二进制,直接推高 App 体积——要支持按需裁剪。
- 版本碎片化:聚合 SDK 与各 adapter 版本组合爆炸,升级要保证向后兼容、灰度可控。
- 弱网与超时预算:移动网络抖动大,聚合的并行竞价要有全局超时预算(常在 ~1s 量级,和 Prebid 的 timeout budget 同理),到点就用已收到的最高价收口;banner 这类常驻广告的自动刷新一般 30–60s 一次。
- 安全与防作弊:奖励回调、点击 / 曝光上报要防伪造(IVT 的端上一环),常配服务端校验。
一句话给工程师:广告 SDK 是”高约束环境下的客户端组件工程”——它要在别人进程里,用最小的内存 / CPU / 包体 / 线程占用,稳定地跑完一条带并行竞价的异步状态机。这比”接个 HTTP API”难得多,也是移动广告 SDK 岗位的真实考点。
深挖一处:并行竞价的超时预算怎么收口
上面清单里最能体现”端上竞价工程”的,是 In-App Bidding 的超时收口——它和 Prebid Server 的 timeout budget 是同一道题,但跑在更恶劣的移动网络上。问题是:并行向 N 家广告源询价,有的快有的慢,什么时候『不等了』?
朴素做法”等所有人回来再比价”是错的——只要有一家慢 / 超时,整次广告就被拖死,填充率掉、用户等转圈。正确姿势是一套带兜底的并发收集:
- 设全局超时预算(如 1000ms)。发出所有询价后启动一个计时器。
- 谁先回收谁的价,边收边维护”当前最高价”。
- 到点即收口:超时一到,用此刻已收到的最高价决出胜者,忽略还没回来的——宁可少一两家报价,也不让慢响应拖垮整场。
- 预加载叠加:对激励视频 / 插屏,竞价 + 素材下载都在”展示前”提前做好;真正
show()时只是把缓存好的素材秒开,把网络延迟挪出了用户的等待路径。
这里的工程取舍和服务端竞价引擎惊人一致:延迟 vs 收益的对冲——超时设太短,慢但高价的广告源被丢掉,收益受损;设太长,填充延迟拖累体验与下一次请求。差别只在于,端上还要额外扛弱网、进程被系统杀、页面生命周期变化这些移动特有的不确定性。这也是为什么”会写竞价超时收口”在买方(DSP)和卖方(SDK / Prebid)两端都是硬通货。
深挖二处:多 SDK 的故障隔离怎么做
§5 清单里”一家 adapter 崩溃不能带崩整个聚合层”是端上最难、也最能体现工程功力的一条。聚合 SDK 同时挂着 N 家第三方 adapter,每一家的代码质量、依赖、native 库都不在你掌控之内——你必须假设其中任何一家会在任何时刻崩、卡、或泄漏。 生产里成体系的隔离手段有四层:
-
调用边界包裹(call-site guarding):每一次进入 adapter 的调用(
init/loadAd/show/ 回调)都套在try/catch(Android)或@try/@catch+NSException兜底(iOS)里。一家 adapter 在loadAd里抛异常,聚合层捕获后把它当作onAdFailedToLoad,竞价继续用其余几家的报价收口——一家挂掉退化成少一个 bidder,而不是整场失败。 -
超时即弃权(timeout = forfeit):上一节的全局超时预算同时是隔离机制——一家 adapter 卡死(死循环、同步网络、native 死锁),到点就被判弃权、它的结果被忽略。没有超时,一家卡死的 adapter 就是一次 ANR:Android 主线程阻塞 5s 触发系统 ANR 弹窗、阻塞 BroadcastReceiver 10s 同样触发,这些阈值直接写进 Android Vitals,会拉高你在 Play Console 的 ANR 率、进而影响商店排名。
-
线程边界(thread confinement):把 adapter 的重活儿(网络、解码、文件 I/O)赶到工作线程,只在最后一刻把 UI 回调切回主线程。一家 adapter 在工作线程里跑死,最坏是占一个线程,而不是冻住宿主 App 的渲染。
-
崩溃归因(crash attribution):native 崩溃(SIGSEGV / SIGABRT)穿透
try/catch,会直接带崩整个进程——这是聚合 SDK 真正的噩梦。业内的兜底是给每次 adapter 调用打一个**面包屑(breadcrumb)**写进崩溃上下文(Crashlytics / Sentry 的 custom key),这样一次 native 崩溃事后能被归因到”崩在哪家 adapter 的哪个调用里”,再对这家做版本回滚或熔断(circuit breaker:连续崩 N 次就本地禁用它,直到下次配置刷新)。
一句话:端上的故障隔离,本质是”把不可信的第三方代码,关进一个有超时、有线程边界、有崩溃归因的笼子里”。 这套思路和服务端把 bidder adapter 隔离(一个 adapter 崩了不影响整场扇出)完全同构——只是端上多了”崩溃会带走整个宿主进程”这个更高的赌注。
6. 工程实战:几个绕不开的坑
一个典型的”奖励作弊”事故:某游戏的激励视频”看一半就发奖励”被刷量——根因是奖励发放只依赖端上回调,而端上回调可被 hook / 重放伪造。修法是奖励以服务端回传(S2S server-side callback)为准:广告平台从服务器直接通知游戏服务器”这次观看有效”,端上回调只做 UI 提示。凡是和钱 / 奖励挂钩的信号,都不能只信客户端。
其余高频坑:
- 预加载素材过期:缓存太久,show 时素材已失效,导致展示失败或计费不一致——要管好缓存 TTL 与刷新。
- show 在 Activity 已销毁后调用:异步回调回来时页面没了,崩溃。要校验宿主生命周期。
- banner 自动刷新与可见性打架:页面不可见还在刷新计曝光 → 无效曝光、被风控。
- 某 adapter 拖慢整场竞价:没有全局超时,一家慢 SDK 拖垮整条链路的填充率。
- 多 SDK 权限 / 依赖冲突:升级一家 adapter 引入的依赖把另一家顶崩,需依赖对齐与隔离。
7. 与归因 / 隐私的边界
广告 SDK 在端上还顺手承担了标识采集与归因触点,但这块自成体系,本篇只划边界:
- 设备标识:iOS 的 IDFA(受 ATT 授权约束)、Android 的 GAID;都在收紧,SDK 要优雅降级到无标识场景。
- 归因:装机 / 转化的归因走 MMP + SKAdNetwork(iOS)/ Play Install Referrer(Android) 那套 postback 体系——这和 SDK 的”曝光 / 点击上报”是两条不同的回传链路,别混。
- 合规:是否可采集、可个性化,受 TCF / ATT / 各地法规约束,SDK 要带同意管理。
归因与 SKAdNetwork 的细节(postback、对账、隐私阈值)会有专文,这里只需记住:SDK 上报的 impression/click 是给计费对账用的;归因 postback 是给”这次安装/转化算谁的”用的——同在端上,目的不同。
8. 常见误解 ↔ 正解
| 常见误解 | 正解 |
|---|---|
| 广告 SDK 就是”发个请求拿广告” | 它是带并行竞价的异步状态机 + 端上渲染 + 上报,跑在宿主进程里 |
| Mediation 在”竞价” | 瀑布只是按预设 eCPM 串行问;真正的实时竞价是 In-App Bidding |
| In-App Bidding 和 Header Bidding 没关系 | 它就是 Header Bidding 的移动端对应物 |
| 激励视频奖励看端上回调就行 | 必须以**服务端回传(S2S)**为准,防作弊 |
| 多接几家广告源只会涨收益 | 也会涨包体、内存、冲突与崩溃风险,需权衡 |
| SDK 崩了只影响广告 | 它在宿主进程里,可能带崩整个 App |
| 曝光上报 = 归因 postback | 两条链路:上报为计费对账,postback 为安装/转化归因 |
9. 速查表
- 本质:移动端变现的”扳机”——请求 → 聚合挑价 → 加载 / 缓存 → 渲染 → 曝光 / 点击上报。
- 对齐网页:SDK ≈ GPT;In-App Bidding ≈ Header Bidding;Mediation ≈ 媒体 Ad Server。
- 生命周期:init(异步)→ load → 聚合 → 预缓存 → onAdLoaded → show → impression/click → onAdClosed/奖励。
- 聚合:瀑布(预设 eCPM 串行、漏价)vs In-App Bidding(并行实时竞价);现实常混合,原理同统一竞价。
- 工程内核:寄生宿主进程 → 初始化延迟、预加载、内存/OOM、ANR、多 SDK 冲突、包体、超时、防作弊。
- 钱相关信号别只信端上:奖励走 S2S,曝光/点击配服务端校验。
- 边界:上报 ≠ 归因 postback;标识受 ATT/TCF 约束。
把它记成一句话:App 广告 SDK 是把”网页那套 GPT + Header Bidding + Ad Server”塞进一个客户端进程里的产物——机制同源,但端上的内存、线程、崩溃、超时约束,把它变成了一道真正的客户端工程题。
先看同系列里和本篇相邻的几篇:
- 广告 SDK 实战:接入 / 实现 / Mediation 决策 / 厂商:本篇的实战续篇——怎么把 SDK 接进来、内部七层怎么实现、谁在做 SDK。
- OEM 系统广告:手机厂商怎么靠预装、预加载与系统位变现:厂商 SDK 作为一家特殊广告源,系统级预加载与预装分发变现怎么玩。
- 程序化广告生态全景:先看全局,再定位 SDK 在卖方侧端上的位置。
- Header Bidding:Prebid.js vs Prebid Server:In-App Bidding 的网页同源物,对照着读最快。
- 媒体 Ad Server 深挖:聚合层”挑价”和 Ad Server 的 decisioning / 统一竞价同源。
- last look 与拍卖透明度:竞价信息不对称的故事,在 App 侧同样适用。
- IAB TCF Explained:SDK 端上同意管理与合规的协议基础。
再是规范与一手资料:
- Google AdMob. Mediation overview:瀑布与 bidding 混合聚合、adapter 机制的官方实现参考。
- AppLovin. MAX Integration:主流 In-App Bidding 聚合平台的接入文档。
- Unity LevelPlay(原 ironSource). Developer docs:另一套主流聚合 / bidding 实现。
- IAB Tech Lab. MRAID (Mobile Rich Media Ad Interface Definitions):可交互素材在端上的渲染协议。
- IAB Tech Lab. Open Measurement SDK (OMID):曝光 / 可见性测量的标准接口。
- Apple Developer. SKAdNetwork:iOS 隐私归因协议——§7 归因边界的协议底座。
- Android Developers. ANR (Application Not Responding):主线程阻塞阈值与 Android Vitals 口径,§5 故障隔离的依据。
附录:术语表
- App 广告 SDK:嵌入宿主 App、负责发起广告请求 / 聚合 / 渲染 / 上报的客户端变现组件;网页侧的对应物是 GPT + Prebid.js。
- Mediation(聚合):在多家广告源之间决定”问谁、谁价高”的决策层;端上的”分配”问题,对应网页的媒体 Ad Server。
- Waterfall(瀑布):按预设 / 历史 eCPM 给广告源排序、串行询问、命中即停的旧聚合机制。
- In-App Bidding:让多家广告源并行实时竞价的机制,Header Bidding 在 App 端的对应物。
- adapter:聚合 SDK 用来对接某家具体广告源的适配层;把统一接口翻译成该网络的私有协议。
- eCPM(effective CPM):把不同计价归一成”每千次曝光的等效收入”,聚合层比价的核心指标。
- Banner / Interstitial / Rewarded / Native / Splash / App Open:常见广告形式——横幅 / 插屏 / 激励视频 / 原生 / 开屏 / App 回前台。
- 预加载(preload):在
show()之前提前下载并缓存素材,把网络延迟挪出用户等待路径。 - ANR(Application Not Responding):Android 主线程阻塞超阈值(通常 5s)触发的系统级无响应,计入 Android Vitals。
- OOM(Out Of Memory):内存耗尽导致的崩溃;视频 / 大图素材与多 adapter 叠加是主要诱因。
- 故障隔离(fault isolation):用调用边界、超时、线程边界、崩溃归因把不可信的第三方 adapter 关进”笼子”,防止一家崩溃带崩整个聚合层 / 宿主 App。
- 熔断(circuit breaker):某 adapter 连续失败 / 崩溃达到阈值后本地禁用它,直到下次配置刷新。
- S2S 服务端回传(server-side callback):广告平台从服务器直接通知 App 服务器”这次观看 / 转化有效”的回传,激励视频奖励发放以它为准、防端上作弊。
- MRAID / VAST:可交互富媒体 / 视频广告的素材渲染协议。
- OMID(Open Measurement Interface Definition):IAB Tech Lab 的可见性 / 曝光测量 SDK 接口。
- MMP(Mobile Measurement Partner):第三方移动归因服务商(AppsFlyer / Adjust / Singular 等)。
- SKAdNetwork / ATT:Apple 的隐私归因协议 / App 追踪透明度框架;约束 IDFA 访问与归因粒度。
- IDFA / GAID:iOS / Android 的广告标识符。
- IVT(Invalid Traffic):无效流量 / 作弊流量;端上曝光 / 点击 / 奖励上报需防伪造。