Skip to content
Charles Shao
Go back

广告 SDK 实战:APP 怎么接入、SDK 怎么实现、Mediation 怎么决策、谁在做 SDK

Updated:
views

App 广告 SDK 深挖 那篇讲清了”广告 SDK 是什么、在端上的生命周期、瀑布 vs In-App Bidding 的机制”。这篇换一个视角——开发者视角,把四个最常被问、却很少被一次讲透的问题落到地上:

  1. 一个 App 到底怎么把广告 SDK 接进来?(依赖、初始化、广告位、adapter、同意、测试)
  2. SDK 内部是怎么实现的?(它在你进程里到底跑着哪几层)
  3. Mediation 怎么对这些 SDK 做决策?(瀑布、In-App Bidding、混合统一竞价、自动调价)
  4. 到底是哪些公司在做这些 SDK?(聚合平台 vs 广告源,厂商全景)

范围说明:本篇是 App 广告 SDK 深挖实战 / 实现侧续篇。概念与机制(生命周期、瀑布 vs bidding 的”为什么”)那篇已讲,这里聚焦”怎么接、怎么实现、谁在做”,重叠处只给指针。

TL;DR

Table of contents

Open Table of contents

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

不管接的是 AdMob、MAX 还是 LevelPlay,接入步骤几乎是同一条流水线。下面以”聚合 SDK + 几家广告源 adapter”的典型组合走一遍。

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

接入从来不是”加一个包”,而是一个聚合 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,就多一截二进制(包体)、一份依赖、一组 ProGuard / SO 库——这是 §1 末尾”多 SDK 冲突”的根源,也是”该接几家”要权衡的第一笔账。

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

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

init 是整条链路里最容易踩坑的一步:它异步、有耗时、且没就绪就请求广告会失败。正确姿势是”init 完成回调里再请求 / 或请求排队”。

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

init 在拉远程配置 + 初始化每个 adapter,不能阻塞冷启主线程。开屏 / 冷启场景要给一个硬超时(业内常设 ~3s),到点就放行进 App,绝不能白屏等 SDK(详见 App 广告 SDK 深挖 §5)。

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

广告位(ad unit ID)是你在聚合平台后台建的一个逻辑位,背后挂着”接哪几家广告源、各家什么参数、走瀑布还是 bidding”——这些都在服务端配置,端上只认一个 ID。这是关键解耦:调整广告源不用发版。

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()

SDK 对外就是一组异步回调 + 状态机loadAd()showAd() 解耦——先在后台备好(预加载),等到合适时机(关卡结束、页面切换)再 show,把网络延迟挪出用户等待路径。

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

show() 之后 SDK 接管渲染(banner 内嵌、插屏 / 激励全屏)、上报 impression / click(计费对账依据)、回调关闭;激励视频额外回调奖励发放——奖励必须以服务端回传(S2S)为准,端上回调只做 UI(防作弊,见 App 广告 SDK 深挖 §6)。

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


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

把”一个广告 SDK”从对外 API 一路拆到端上执行,大体是这七层。理解这张分层图,是排查”广告不出 / 不计费 / 崩溃归谁”的前提。

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 分支。load 与 show 解耦、预加载与展示解耦就由这台状态机维护;“show 在 Activity 已销毁后调用”这类崩溃,本质是状态机没和宿主生命周期对齐。
  3. 配置中心(remote config):App 启动时从服务端拉一份”广告位 → 广告源映射 / 瀑布顺序 / bidding 开关 / 超时预算 / A/B 分组”,端上缓存。这是”调整广告源不用发版”的工程基础——后台改配置,端上下次刷新即生效。
  4. Mediation 决策层:按配置把候选广告源组织成瀑布(串行)或 In-App Bidding(并行询价),按 eCPM 排序选胜者,带全局超时收口。这一层是第 3 节的主角。
  5. adapter 适配层:每家广告源一个 adapter,把聚合 SDK 的统一接口翻译成该网络的私有 SDK 调用。故障隔离就发生在这层——调用边界 try/catch、超时即弃权、崩溃打面包屑归因(详见 App 广告 SDK 深挖 §5 深挖二处)。
  6. 网络与缓存层:询价 / 取素材的 HTTP、素材预下载与缓存(含 TTL)、重试退避。“预加载素材过期导致计费异常”就出在这层的缓存治理。
  7. 渲染与上报层:把素材渲染到屏幕(涉及 MRAID / VAST 协议)、上报 impression / click、激励视频走 S2S 奖励回传。

一句话:广告 SDK 的实现难度不在任何单层,而在”七层全跑在别人进程里”——它的内存、线程、崩溃、包体都和宿主 App 共享。这把它从”前端活儿”变成了严肃的客户端组件工程(App 广告 SDK 深挖 §5 专讲这块)。

深挖:配置中心为什么是 SDK 实现的”隐藏主角”

接入时你只填一个 ad unit ID,但这个 ID 背后的”接哪几家、什么顺序、走不走 bidding、超时多少、流量怎么分 A/B”全在服务端。配置中心让这套与端上版本解耦

这也是为什么”广告 SDK”从来不是一个纯客户端库,而是端上 SDK + 服务端配置 / 决策的合体。


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

接进来好几家广告源(AdMob、Meta、Mintegral、Unity Ads…),“这次该问谁、谁的价高”就是 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 给广告源排序,从高到低串行问,“A 有没有?没有问 B……”命中即停。两个老毛病:价是『估』的不是实时的(排在后面的源这次也许愿出更高价,却没机会,钱漏了);串行 = 延迟高

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

把”串行猜价”改成”并行真实竞价”:聚合层同时向所有支持竞价的源询价,各自回一个真实 bid,价高者得。这正是 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

聚合平台的”决策”不止单次竞价,还有几个持续优化的旋钮:

一句话:Mediation 决策 = 候选归一到 eCPM 排序 + 超时收口 + 持续自动优化。In-App Bidding 决定”实时竞价里谁最高”,统一竞价决定”实时价 vs 瀑布预设价,整场谁最高”。


4. 哪些公司在做这些 SDK

市面上的广告 SDK 分两类角色,一个 App 通常同时集成一个聚合平台 + 多家广告源 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 就是”加个包、调个 load”是”聚合 SDK + N 个 adapter + 服务端配置 + 异步状态机”的合体
调整广告源要发版广告位映射在服务端配置中心,热更即生效,不用发版
Mediation 在”竞价”瀑布只是按预设 eCPM 串行问;真正实时竞价是 In-App Bidding
bidding 和瀑布二选一现实是混合统一竞价,实时价与预设价同排序
聚合平台是中立裁判Google / AppLovin / Unity 既当裁判又当运动员(自带需求)
多接几家广告源只会涨收益也会涨包体、内存、冲突与崩溃风险,要权衡
激励奖励看端上回调就行必须以 S2S 服务端回传为准,防作弊

7. 速查表

记成一句话:接广告 SDK = 把一个”端上状态机 + 服务端决策中心 + 一堆第三方 adapter”塞进你的进程里;接入是流水线,难度在端上工程,决策在 eCPM 排序,厂商分聚合与广告源两类。


延伸阅读

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

再是规范与一手资料:

附录:术语表


views
Share this post on:

Previous Post
零售媒体网络(RMN):零售商用第一方购买数据卖广告,与它为什么是搜索、社交之后的第三波
Next Post
后 Cookie 身份层:UID2 / RampID 的确定性 ID、Privacy Sandbox 的设备端方案,与第一方数据的三条路