Skip to content
Charles Shao
Go back

App 广告 SDK 深挖:变现、聚合与 In-App Bidding

Updated:
views

程序化广告生态全景媒体 Ad Server 里,“App 广告 SDK”反复作为”端上发起请求的那个扳机”出现——网页那侧是 GPT + Prebid.js,App 这侧就是广告 SDK。但它在端上到底做了什么、怎么在多家广告源之间挑价、又为什么是个棘手的客户端工程问题,一直没单独讲。这篇补上。

我们从一次广告的生命周期讲起,过一遍广告形式,重点拆聚合(Mediation)的瀑布 vs In-App Bidding(这就是移动端的 Header Bidding),再看它作为端上组件绕不开的工程内核,最后划清它和归因 / 隐私的边界。

范围说明:本篇聚焦变现 SDK 的端上行为与聚合机制。素材渲染协议(MRAID / VAST)、归因(SKAdNetwork / MMP)会各有专文展开,这里只点到为止、给出指针。

TL;DR

Table of contents

Open Table of contents

1. 定位:端上的”变现扳机”

一个 App 想靠广告赚钱,得在自己的进程里塞进一段别人家的代码——广告 SDK。它对开发者暴露的接口很朴素(“给我一个插屏广告”),背后却要替你完成一整条链路:向服务器要广告、在多家广告源里挑价、把素材下载好、在合适时机渲染出来、再把”用户看了 / 点了”如实上报回去用于计费。

把它和网页那侧对齐就清楚了:

网页(Web)App(移动端)
发起请求的”扳机”GPT(Google Publisher Tag)广告 SDK
实时竞价Header Bidding / Prebid.jsIn-App Bidding(见 §4)
聚合 / 决策媒体 Ad Server(GAM)Mediation(聚合层)
运行环境浏览器沙箱宿主 App 进程(内存 / 线程 / 包体共享)

关键差异:网页的广告标签跑在浏览器沙箱里,崩了顶多广告位空白;App 广告 SDK 直接跑在宿主 App 的进程里——它的内存泄漏、ANR、崩溃会拖垮整个 App。 这把”做一个广告 SDK”从前端活儿变成了严肃的客户端工程问题(§6)。

市面上的 SDK 大体两类,常常一个 App 同时集成好几家:

2. 一次广告的生命周期:SDK 在端上做什么

把”展示一个广告”拆成端上的一条生命周期——每一步都有成功、失败、超时三种走向:

App 广告 SDK 一次广告生命周期的时序图。参与方:宿主 App、广告 SDK、聚合层 Mediation、广告源 Ad Network、广告服务器。流程:① App 启动时 SDK 初始化(拉配置、初始化各 adapter,异步、有耗时);② App 在需要时向 SDK 请求某广告位(如插屏);③ SDK 经聚合层在多家广告源间挑选(瀑布或 In-App Bidding);④ 选中的广告源返回素材地址;⑤ SDK 下载并缓存素材(常提前预加载);⑥ 广告就绪,回调通知 App(onAdLoaded);⑦ App 在合适时机调用 show,SDK 渲染广告;⑧ 广告展示,SDK 上报 impression;⑨ 用户点击,SDK 上报 click 并跳转落地页;⑩ 关闭广告,回调 onAdClosed,激励视频则回调发放奖励。旁注:每步都有失败/超时分支(onAdFailedToLoad 等),SDK 要兜底 一次广告的端上生命周期:初始化 → 请求 → 聚合挑价 → 加载 / 缓存 → 就绪回调 → 渲染 → 曝光 / 点击上报 → 关闭。每一步都要处理失败与超时。

逐步拆:

  1. 初始化(init):App 启动时调一次。SDK 拉远程配置、初始化各广告源 adapter。这一步异步且有耗时,没就绪就请求广告会失败——所以 SDK 通常要”初始化完成回调 + 请求排队”。
  2. 请求(load):App 在某个时机(如关卡结束)向 SDK 要某个广告位的广告。
  3. 聚合挑价:SDK 经聚合层在多家广告源里挑——走瀑布或 In-App Bidding(§4)。
  4. 加载 / 预缓存:选中后下载素材(图片 / 视频 / HTML)。激励视频、插屏这类”重”素材,几乎都提前预加载好,等到要展示时秒开。
  5. 就绪回调:素材备好,回调 onAdLoaded;失败则 onAdFailedToLoad(错误码)
  6. 渲染 / 展示(show):App 在合适时机调 show(),SDK 把素材渲染到屏幕(banner 内嵌、插屏 / 激励全屏 Activity)。
  7. 曝光 / 点击上报:展示达标即上报 impression,用户点击上报 click 并打开落地页。这些回传是计费与对账的依据,差一条就是钱的出入。
  8. 关闭 / 奖励:广告关闭回调 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 解决的是同一类”分配”问题,只是发生在端上、面向移动广告源。两种机制:

移动端聚合 Mediation 的两种机制对比图,分两栏、各自带标题。『传统瀑布 Waterfall』栏:聚合层按预先配置的 eCPM 从高到低串行询问广告源——先问 A(地板价 $10),无填充再问 B($6),再问 C($3),命中即停;问题是价为『预估/历史』而非实时,串行逐个问延迟高、且高价需求拿不到机会。『In-App Bidding』栏:聚合层并行向所有支持竞价的广告源实时询价,各自返回真实 bid(A=$7、B=$9、C=$4),价高者 B($9)胜出。结论:In-App Bidding 把『串行猜价』改成『并行真实竞价』,是移动端的 Header Bidding;两者现实中可混合,实时价与瀑布层预设 eCPM 一起比、价高者得 两种机制对比:传统瀑布按预设 eCPM 串行问、命中即停;In-App Bidding 并行实时竞价、价高者得。后者就是移动端的 Header Bidding(每栏图内自带标题)。

4.1 传统瀑布(Waterfall)

聚合层按预先配置的 eCPM 给广告源排个序,从高到低串行问:“A 有没有?没有问 B……”命中即停。两个老问题:

4.2 In-App Bidding(移动端的 Header Bidding)

In-App Bidding 把”串行猜价”改成”并行真实竞价”:聚合层同时向所有支持竞价的广告源询价,各自回一个真实 bid价高者得。这正是 Header Bidding 在 App 里的对应物——动机、收益、复杂度都一一对应:

用图里的数字算笔账(同一次曝光,瀑布配置 A $10 > B $6 > C $3,但这次各家真实愿出价是 A $7、B $9、C $4):

机制发生了什么媒体到手
瀑布先问 A(配置最高)→ 这次 A 无填充 → 问 B → B 填充,命中即停$6
In-App BiddingA/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 头上。几条硬约束:

一句话给工程师:广告 SDK 是”高约束环境下的客户端组件工程”——它要在别人进程里,用最小的内存 / CPU / 包体 / 线程占用,稳定地跑完一条带并行竞价的异步状态机。这比”接个 HTTP API”难得多,也是移动广告 SDK 岗位的真实考点。

深挖一处:并行竞价的超时预算怎么收口

上面清单里最能体现”端上竞价工程”的,是 In-App Bidding 的超时收口——它和 Prebid Server 的 timeout budget 是同一道题,但跑在更恶劣的移动网络上。问题是:并行向 N 家广告源询价,有的快有的慢,什么时候『不等了』?

朴素做法”等所有人回来再比价”是错的——只要有一家慢 / 超时,整次广告就被拖死,填充率掉、用户等转圈。正确姿势是一套带兜底的并发收集

  1. 设全局超时预算(如 1000ms)。发出所有询价后启动一个计时器。
  2. 谁先回收谁的价,边收边维护”当前最高价”。
  3. 到点即收口:超时一到,用此刻已收到的最高价决出胜者,忽略还没回来的——宁可少一两家报价,也不让慢响应拖垮整场。
  4. 预加载叠加:对激励视频 / 插屏,竞价 + 素材下载都在”展示前”提前做好;真正 show() 时只是把缓存好的素材秒开,把网络延迟挪出了用户的等待路径。

这里的工程取舍和服务端竞价引擎惊人一致:延迟 vs 收益的对冲——超时设太短,慢但高价的广告源被丢掉,收益受损;设太长,填充延迟拖累体验与下一次请求。差别只在于,端上还要额外扛弱网、进程被系统杀、页面生命周期变化这些移动特有的不确定性。这也是为什么”会写竞价超时收口”在买方(DSP)和卖方(SDK / Prebid)两端都是硬通货。

深挖二处:多 SDK 的故障隔离怎么做

§5 清单里”一家 adapter 崩溃不能带崩整个聚合层”是端上最难、也最能体现工程功力的一条。聚合 SDK 同时挂着 N 家第三方 adapter,每一家的代码质量、依赖、native 库都不在你掌控之内——你必须假设其中任何一家会在任何时刻崩、卡、或泄漏。 生产里成体系的隔离手段有四层:

  1. 调用边界包裹(call-site guarding):每一次进入 adapter 的调用(init / loadAd / show / 回调)都套在 try/catch(Android)或 @try/@catch + NSException 兜底(iOS)里。一家 adapter 在 loadAd 里抛异常,聚合层捕获后把它当作 onAdFailedToLoad,竞价继续用其余几家的报价收口——一家挂掉退化成少一个 bidder,而不是整场失败

  2. 超时即弃权(timeout = forfeit):上一节的全局超时预算同时是隔离机制——一家 adapter 卡死(死循环、同步网络、native 死锁),到点就被判弃权、它的结果被忽略。没有超时,一家卡死的 adapter 就是一次 ANR:Android 主线程阻塞 5s 触发系统 ANR 弹窗、阻塞 BroadcastReceiver 10s 同样触发,这些阈值直接写进 Android Vitals,会拉高你在 Play Console 的 ANR 率、进而影响商店排名。

  3. 线程边界(thread confinement):把 adapter 的重活儿(网络、解码、文件 I/O)赶到工作线程,只在最后一刻把 UI 回调切回主线程。一家 adapter 在工作线程里跑死,最坏是占一个线程,而不是冻住宿主 App 的渲染。

  4. 崩溃归因(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 提示。凡是和钱 / 奖励挂钩的信号,都不能只信客户端。

其余高频坑:

7. 与归因 / 隐私的边界

广告 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. 速查表

把它记成一句话:App 广告 SDK 是把”网页那套 GPT + Header Bidding + Ad Server”塞进一个客户端进程里的产物——机制同源,但端上的内存、线程、崩溃、超时约束,把它变成了一道真正的客户端工程题。


先看同系列里和本篇相邻的几篇:

再是规范与一手资料:

附录:术语表


views
Share this post on:

Previous Post
后 Cookie 身份层:UID2 / RampID 的确定性 ID、Privacy Sandbox 的设备端方案,与第一方数据的三条路
Next Post
程序化策展(Curation / Marketplace 2.0):一个把「数据 + 库存」在卖方侧打包成 deal 的新中间层