主题
数据与事件
文档版本 v1.0.0
本页对应 SDK 1.0.0,与离线包 docs/ 目录里的同一份文档内容一致。其他版本见文档中心。
SDK 会把广告调度和展示过程中的关键事件采集下来,上报给平台服务,用于出填充率 / 曝光率 / 点击率 / eCPM 估算等报表。同时提供一个可选的事件回调,接入方可以把同一批事件接进自己的 数据平台。
这两条路径互相独立:不注册回调不影响上报,上报失败回调也照常抛。
采集范围、逐字段清单、以及「明确不采集什么」见《合规与隐私》「广告埋点上报」。 采集和上报的总闸门是 AdMix.start():在它之前 SDK 不会产生任何埋点事件、不碰磁盘、 更不会发出任何上报请求。
价格和平台广告位 ID 不对接入方开放
内部上报带 eCPM 和平台广告位 ID(平台对账和统计要用),抛给接入方的事件对象里没有这两个字段。 SDK 的所有公开回调与方法(加载回调、预加载回调、事件回调)都只给出广告平台名称。
事件类型
| 事件 | 触发时机 | 关键字段 |
|---|---|---|
ad_request | 一轮聚合调度开始 | unit_id、ad_type |
adn_request | 向某个平台广告位发起请求 | adn、bidding |
adn_fill | 某个平台广告位返回可用广告 | adn、cost_ms |
adn_fail | 某个平台广告位失败或超时 | adn、err_code、err_msg、cost_ms |
fill | 决出中标方,回调宿主成功 | adn、cost_ms |
no_fill | 全部失败或整体超时 | err_code、cost_ms |
impression | 广告曝光 | adn |
click | 广告点击 | adn |
close | 广告关闭 | adn |
reward | 激励发放 | adn |
两条容易被误读的口径
adn_fill 与 fill 不是一回事
一轮里可能有多条 adn_fill(几个竞价位都填充了),但 fill 只有一条,指最终被采用的那一家。
- 算广告位填充率用
fill / ad_request - 算某家广告平台的填充率才用
adn_fill / adn_request
ad_request 按调度轮次记,不按 load() 调用次数记
request_id 把一轮调度的所有事件串起来。一次调度 = 一个 request_id, Banner 每次刷新都是一轮完整调度,所以每刷新一次就换一个新的。
预加载(AdMix.preload())本身就是一轮完整调度,会产生自己的 ad_request → fill; 之后 load() 命中缓存时不再发起任何请求,因此不会再产生一条 ad_request, 那条广告的曝光挂在当初把它调度出来的那个 request_id 上。
这样每个 request_id 的漏斗才是自洽的(一条请求 → 至多一条填充 → 至多一条曝光), 不会出现「曝光数大于填充数」这种没法解释的报表。
缓存与异常标记
缓存相关的区分都放在事件的 ext 里(AdEvent 上对应 loadSource / cacheHit):
| ext 字段 | 出现在 | 含义 |
|---|---|---|
load_src | 这一轮的全部事件 | preload(主动预加载)/ refill(自动补位)/ load_wait(开屏未命中时的入池请求);不带 = load() 的实时请求 |
refill_reason | load_src=refill 的事件 | take(缓存被取走)/ expire(缓存过期被丢弃) |
cache_hit / cache_age_s | 曝光、点击、关闭、激励 | 这条广告是 load() 从缓存直接取出的,以及取出时已缓存的秒数 |
cache_expired | ad_request | 自该广告位上一条 ad_request 以来因过期被丢弃的缓存条数 |
adn_ver_mismatch | 这一轮的全部事件,只在异常时出现 | 某家广告 SDK 的实际版本与适配器锁定的版本不一致,形如 csj:7.6.4.3!=7.7.1.6(实际 != 期望) |
adn_ver_mismatch 出现即需处理
正常工程里这个键根本不存在。出现了说明宿主工程里的广告 SDK 被换成了未经平台验证的版本, 按《引入依赖》「广告 SDK 的版本锁定」处理。
事件回调
java
// init() 之后任何时刻都能注册,它本身不产生采集或网络行为
AdMix.setEventListener(new AdEventListener() {
@Override public void onAdEvent(AdEvent event) {
// 同一次调度的事件 requestId 相同,可以直接拼成漏斗
myTracker.track(event.type, event.unitId, event.adn, event.requestId);
}
});Kotlin 可以用 lambda(AdEventListener 只有一个方法):
kotlin
AdMix.setEventListener { event ->
myTracker.track(event.type, event.unitId, event.adn, event.requestId)
}AdEvent 的字段全部 public final,直接读;不含价格和平台广告位 ID:
| 字段 | 类型 | 说明 |
|---|---|---|
type | String | 事件类型,取值见上表,常量在 AdEventType |
unitId / adType | String | 广告位标识 / splash·reward·banner |
adn | String | csj / gdt。与平台广告位无关的事件(ad_request、no_fill)为 null |
adnSource | String | 走 GroMore 时真正出广告的那一家(pangle / ks / baidu 等),单平台直连为 null |
bidding | Boolean | 该广告源是否竞价位。null = 本事件与广告源无关,不是 false |
costMs | int | 该阶段耗时 |
errCode / errMsg | int / String | 原样透传广告平台的错误码与信息,没有做任何映射 |
requestId | String | 一轮调度的唯一标识 |
sessionId | String | 单次启动随机串,不落盘、不跨启动复用,不是设备标识 |
eventTime | long | 事件发生时刻,毫秒时间戳 |
loadSource / cacheHit | String / boolean | 对应上表的 load_src / cache_hit |
三条边界:
- 回调固定在主线程,接入方不需要自己切线程
- 实现里不要做耗时操作;要落库或发请求请自行转子线程
- 实现里抛出的异常会被 SDK 吞掉并打日志,不影响广告业务,也不影响内部上报
上报行为
无需任何配置:上报地址内置,鉴权凭证与配置接口相同(AppID + App 密钥)。 攒够 50 条或每隔 30 秒发一批,本地队列上限 1000 条(超出丢最早的)。
AdMix.flushEvents() 可以立刻发一次,正常接入不需要调用 —— 它的用途是宿主明确知道马上要退出时抢一次上报,减少下次启动才补报的量。
不丢数据的保证
- 断网:上报失败不清队列,按 5 秒 → 10 秒 → 20 秒 …(封顶 5 分钟)退避重试; 连续失败 8 次后熔断 30 分钟,期间事件继续落盘。网络恢复后一条不少地补上去
- 进程被杀:每条事件在产生的同时就立刻追加落盘(SDK 私有目录), 下次启动读回来继续报。
am force-stop实测 10 条事件一条不丢 - 补报保留原始时间:补上去的事件带的是它当初发生那一刻的时间戳,不是补报那一刻的。 报表因此不会出现「昨天的数据凭空少一块、今天凭空多一块」
报表口径
平台后台的报表还有一组缓存指标,其中两条最容易被误读:
- 缓存命中率的分母是曝光,不是请求。 命中缓存时 SDK 不产生
ad_request, 请求侧没有「命中」这个计数 - 过期浪费数是下限。 过期丢弃没有独立事件,计数挂在下一条
ad_request上, 进程被杀时未报出的会丢
报表与各广告平台自己的统计口径不同(平台报表统计 SDK 侧的请求 / 曝光事件, 各广告平台按自己的计费口径统计),5~10% 的差异属正常。