主题
更新日志
AdMix 广告聚合 SDK 的发布说明。每一版顶部的高亮框列出会影响接入方行为的改动,升级前先看那里。
两个平台各有自己的版本线:Android 交付三个 aar(com.admix:core / com.admix:adapter-csj / com.admix:adapter-gdt,版本号三者一致), iOS 通过私有 CocoaPods 源分发。两边会出现同一个版本号,但那是两个毫不相干的产物。
Android 版本一览
| 版本 | 发布日期 | 说明 |
|---|---|---|
| 1.2.0 | 2026-10-25 | 穿山甲 Banner 填满槽位宽度 |
| 1.1.0 | 2026-10-17 | Banner 规格读取 |
| 1.0.0 | 2026-09-20 | 首个正式发布版 |
iOS 版本一览
| 版本 | 发布日期 | 说明 |
|---|---|---|
| 1.0.0 | 2026-10-17 | 首个正式发布版 |
v1.2.0
Android · 发布日期 2026-10-25 · 穿山甲 Banner 填满槽位宽度
无需改动接入代码
本版没有需要接入方修改代码的变更。
变更
穿山甲 Banner 现在会等比缩放到槽位宽度,并在槽位里居中。
此前穿山甲按服务端下发的登记规格渲染素材,槽位比规格宽时广告贴在左上角、右侧留一条空白; 而优量汇的广告视图本来就按槽位宽度自适应。同一个广告位下中标的是哪一家要到竞价结束才知道, 于是同一个槽位会随中标方变换表现,宿主没有办法事先按某一家准备布局。本版把两家拉齐。
实现上是在槽位与广告视图之间加了一层,由它按缩放后的尺寸向上报告高度:
- 缩放用的是绘制期变换,不改广告视图的宽高。 模板广告的内部排版是按请求尺寸算好的, 直接改宽高会让素材错位。Android 派发触摸时会套用逆矩阵,所以点击热区跟着一起变
- 槽位高度写死时不会放大到被裁。 缩放倍数同时受槽位宽、高两个方向约束, 取较小者;缩不满宽度时居中,留白匀到两边
- 槽位高度是
wrap_content时会自动长到缩放后的高度
优量汇一侧没有任何改动。
升级注意
槽位的实际渲染高度会变。 槽位宽度大于登记规格宽度时,广告按比例放大,渲染高度随之变高 (例如规格 320×50 的广告放进 375dp 宽的槽位,渲染高度从 50dp 变成约 59dp)。
槽位高度写死的页面不受影响(缩放倍数会被高度约束住);高度是 wrap_content 的页面 会看到槽位变高。按登记规格的宽高比给槽位尺寸,就是本版之后的正确做法。
从 1.1.0 升级只需改版本号,对外 API 没有任何变化。
已知限制
缩放是整体等比放大,素材本身的像素密度不会提高 —— 放大倍数较大时素材会略微变虚。 规格与槽位宽度差距过大时,更好的做法仍然是让运营在广告平台后台登记一档更接近的规格。
v1.1.0
Android · 发布日期 2026-10-17 · Banner 规格读取
无需改动接入代码
本版没有需要接入方修改代码的变更。
新增
BannerAd.getAdWidthDp() / getAdHeightDp() —— 当前展示广告的请求尺寸(dp), 没有广告时返回 0,在 onAdLoaded 回调当下即可读取。
同一个 Banner 广告位会向多家广告平台请求,各家在自己后台登记的规格可能不同, 而中标方要到竞价结束才知道。槽位高度写死的页面(例如 uni-app 的 nvue 不支持按内容撑开) 此前只能遍历广告视图树去测量实际大小,那依赖广告平台的内部视图结构、对方调整布局即失效。 现在可直接读取。
升级注意
本版本只新增接口,没有删除或改变既有行为,从 1.0.0 升级只需改版本号。
SDK 不会代为调整容器大小,也不会把广告视图的尺寸固定为下发规格:两家广告平台的 渲染策略不同(穿山甲按下发尺寸请求素材、容器不参与;优量汇的广告视图按容器宽度自适应), 统一固定会破坏优量汇原本正确的行为 —— 实测 420dp 宽的容器里广告只占 320dp、右侧留白。 因此取到数值后如何使用由宿主决定。
已知限制
getAdWidthDp() / getAdHeightDp() 返回的是请求尺寸(服务端下发的 width_dp / height_dp),不是对广告视图的测量结果。广告平台返回的素材与登记规格不符时,两者会有出入。
v1.0.0
Android · 发布日期 2026-09-20 · 首个正式发布版
无需改动接入代码
本版没有需要接入方修改代码的变更。
交付内容
交付坐标发布在 https://admin.maemi.cn/maven/(公开只读)。推荐用「全家桶」聚合坐标,一行:
groovy
implementation "com.admix:sdk:1.0.0" // 核心 + 穿山甲 + 优量汇
// 只接一家:com.admix:sdk-csj:1.0.0(核心 + 穿山甲)/ com.admix:sdk-gdt:1.0.0(核心 + 优量汇)也可以逐个引(需要精细控制传递依赖时),三个版本号必须一致:
groovy
implementation "com.admix:core:1.0.0" // 必需
implementation "com.admix:adapter-csj:1.0.0" // 穿山甲(GroMore 通道),自动带上穿山甲 SDK 7.7.1.6
implementation "com.admix:adapter-gdt:1.0.0" // 优量汇,自动带上优量汇 SDK 4.680.1550离线包:https://admin.maemi.cn/maven/downloads/admix-android-sdk-1.0.0.zip (含全套文档,不含两家广告 SDK,两家的开发者协议不允许转发)。
环境要求:minSdk 25(Android 7.1.1 —— 7.0 不信任配置服务所用的 Let's Encrypt 根证书)、 compileSdk 34、JDK 17、AGP 8.x。仅 Android。
新增
- 三种广告位:开屏、激励视频、Banner。两家广告平台(穿山甲 GroMore 通道 / 优量汇) 在华为 ALN-AL10 / Android 12 上,Debug 与 Release 混淆包均真机跑通
- 客户端竞价 + 瀑布混合调度:并行竞价 → 取最高价 → 按预估价降序遍历瀑布层 (竞价价 ≥ 当前层预估价就不再往下请求)→ 回传竞胜 / 竞败。三种广告位共用一套引擎
- 接入方只配两个凭证:
AdMix.init(context, AppID, App密钥)。服务器地址内置; 各广告平台的应用 ID 与广告位 ID 全部由平台配置服务下发,SDK 侧没有本地写入口 —— 运营改价格、调顺序、开关广告位、停用一整家平台,接入方都不用发版 - 按需集成:只接一家就只引那一个适配器,另一家(连同它的广告 SDK)不进包。 适配器通过 Java SPI 自注册,服务端按上报的能力裁剪下发,不需要写任何
packagingOptions - 广告 SDK 版本锁定:两家广告 SDK 的版本用 Gradle 严格版本约束锁死, 工程里出现别的版本会直接构建失败并打出中文说明,而不是 Gradle 默认的「静默取高版本」。 另有一道运行时核对,只告警不阻断,并随配置请求与埋点上报给平台
- 预加载与缓存池:
AdMix.preload(),命中缓存时load()同步回调 0 等待; 自动补位、过期主动销毁、开屏超时后迟到的填充进池留给下一次 - 广告事件与埋点:SDK 自动采集广告维度事件并上报出报表; 另有可选的
AdMix.setEventListener()把同一批事件接进接入方自己的数据平台 - 激励视频服务端发奖验证:
RewardAd.setServerSideVerification(userId, customData), 平台验签后 HMAC 签名转发到接入方的接收地址 - 合规两段式初始化:
init()只读本地配置、不联网不采集;start()才初始化广告 SDK, 必须在用户同意隐私政策之后。未同意时任何load()直接失败(-1006),不会碰到广告 SDK - 混淆开箱可用:R8 规则随 aar 自动生效(
consumer-rules.pro), 接入方无需写任何-keep。minSdk 29 + R8 的外部最小工程实测零缺失类告警
对外 API 的两条约定
- 不返回真实 eCPM,也不返回平台广告位 ID。
onAdLoaded(adnType)只给平台名称,AdEvent里没有价格字段,release aar 的日志里这两项打*** - 只使用文档列出的类:
AdMix、AdMixConfig、AdError、AdnType、com.admix.api下的广告类与监听器、AdPreloader.PreloadListener、AdEvent/AdEventListener/AdEventType。 其余包是内部实现,标了@RestrictTo,随版本变化且不保证兼容
已知限制
- 机型覆盖:只在华为 ALN-AL10 / Android 12 上做过完整真机验证, 上架前建议在自有机型矩阵上回归一轮
- 优量汇仍会探测约 276 个指定包名:华为「读取已安装应用列表」弹窗已修复 (成因是优量汇不是穿山甲,
GlobalSetting.setEnableCollectAppInstallStatus(false)), 但关掉开关后优量汇的安全模块改为逐个getPackageInfo探测约 276 个指定包名(不弹窗)。 上架前必须知悉这条,处置口径见《合规与隐私》「Android 应用列表读取」,已向优量汇提交工单 - 缓存只在进程内存里:进程被杀后清空,真正的冷启动第一条开屏一定是实时请求
- 穿山甲加载阶段拿不到真实 eCPM(展示时才有),与优量汇竞价位比价用的是后台配置的预估价。 预估价填虚高会把优量汇的真实出价比下去 —— 这是运营侧要按近 7 日真实 eCPM 校准的事
- 未覆盖的竞价分支:优量汇竞价位价格低于某瀑布层预估价、而该层实际价格又更低的情况, 以及 Release 包下的竞败回传(Debug 已验)
iOS v1.0.0
iOS · 发布日期 2026-10-17 · 首个正式发布版
无需改动接入代码
本版没有需要接入方修改代码的变更。
交付内容
通过平台的私有 CocoaPods 源分发,源地址 https://admin.maemi.cn/cocoapods/(公开只读)。 推荐全家桶,一行:
ruby
source 'https://cdn.cocoapods.org/' # CocoaPods 官方源(两家广告平台的 SDK 在这里)
source 'https://admin.maemi.cn/cocoapods/' # AdMix
platform :ios, '12.0'
target 'YourApp' do
pod 'AdMix', '1.0.0' # 聚合核心 + 穿山甲 + 优量汇
end只接一家就只写对应的子模块,不写的那家连同它的广告 SDK 完全不进包:
ruby
pod 'AdMix', '1.0.0', :subspecs => ['Core', 'CSJ'] # 聚合核心 + 穿山甲
pod 'AdMix', '1.0.0', :subspecs => ['Core', 'GDT'] # 聚合核心 + 优量汇两家广告平台的 SDK 由 CocoaPods 从其官方源拉取,不随平台的包分发 (两家的开发者协议不允许第三方转发其 SDK)。子模块带入的版本: AdMix/CSJ → Ads-CN/BUAdSDK 7.8.0.8,AdMix/GDT → GDTMobSDK 4.16.11。
环境要求:最低 iOS 12.0。
新增
- 三种广告位:开屏、激励视频、Banner。两家广告平台(穿山甲 GroMore 通道 / 优量汇) 均已真机跑通,覆盖加载、展示、点击、关闭与激励发放
- 客户端竞价 + 瀑布混合调度:与 Android 版同一套设计 —— 并行竞价 → 取最高价 → 按预估价降序遍历瀑布层(竞价价不低于当前层预估价就不再往下请求)→ 回传竞胜 / 竞败。 三种广告位共用一套调度引擎
- 接入方只配两个凭证:
+[AdMix setupWithAppId:appKey:]。服务器地址内置; 各广告平台的应用 ID 与广告位 ID 全部由平台配置服务下发,SDK 侧没有本地写入口 - 按需集成:适配器以类名前缀自注册,
Core启动时扫描 App 里实际链接进来的类, 引了谁就发现谁。拉配置时把发现结果报给平台,服务端据此把未集成的广告平台从下发配置里摘掉 - 广告 SDK 版本锁定:两个子模块对广告 SDK 声明的是精确版本, 工程里出现别的版本时
pod install直接解析失败。另有一道运行时核对兜住手工集成这条绕过路径, 只告警不阻断,并随配置请求与埋点上报给平台 - 预加载与缓存池:命中缓存时
load同步回调 0 等待;自动补位、过期主动销毁、 开屏超时后迟到的填充进池留给下一次 - 广告事件与埋点:自动采集广告维度事件并上报出报表,埋点协议与 Android 版完全相同, 服务端与报表是同一套
- 激励视频服务端发奖验证:设置用户标识与自定义数据后,平台验签并 HMAC 签名转发到接入方的接收地址
- 合规两段式初始化:
setup只读本地配置、不联网不采集;startWithCompletion:才初始化广告 SDK, 必须在用户同意隐私政策之后。同意之前没有任何网络请求(真机验证) - 隐私清单:每个 xcframework 自带
PrivacyInfo.xcprivacy,App 的隐私报告自动汇总。 使用的 required reason API 已逐项申报
与 Android 版的差异
设计一一对应,配置接口与埋点协议一个字段都没改,服务端是同一套。按 iOS 惯例调整的部分:
| 项 | Android | iOS |
|---|---|---|
| 入口方法名 | AdMix.init / start | +setupWithAppId:appKey: / +startWithCompletion: |
| 回调形式 | Listener 接口 | delegate(weak)+ block |
| 错误 | AdError | NSError,domain com.admix.error / com.admix.adn,错误码与 Android 相同 |
| 开屏展示 | show(ViewGroup) 放进容器 | showInWindow:bottomView:,两家 iOS SDK 的开屏都盖在 window 上展示 |
| 适配器发现 | Java SPI | 按 App 自带镜像列类名,筛前缀并验协议 |
配置请求比 Android 多两个参数(都不进签名):os=ios,以及 att(ATT 授权状态, 直接影响填充率,不上报则线上波动无法归因)。
对外 API 的两条约定
与 Android 版一致:
- 不返回真实 eCPM,也不返回平台广告位 ID。加载成功只给平台名称, 事件对象里没有价格字段,Release 二进制的日志里这两项打
*** - 只使用文档列出的类。其余为内部实现,随版本变化且不保证兼容
已知限制
- 本版二进制未签名,这是既定结论而不是待办。Apple 的强制签名只针对它公布的第三方 SDK 名单, 本 SDK 及所聚合的穿山甲、优量汇 SDK 均不在该名单内(两家 SDK 自身也未签名)。 签名主体一旦定下不宜更换 —— 接入方的 Xcode 会记住,更换等于所有接入方重新信任一次, 因此在主体稳定之前不签。各 xcframework 均自带隐私清单
PrivacyInfo.xcprivacy, 隐私清单是另一回事,并不因不签名而缺失 - ATS 需要宿主全局放开(
NSAllowsArbitraryLoads)。两家广告平台的部分素材通过明文 http 下发。 只开NSAllowsArbitraryLoadsInWebContent仍被拦截(素材由广告 SDK 直接下载、不过 WebView); 只为广告平台素材域名配置例外也不够(同一批广告里还会出现广告主自有的素材域名,无法事先列全)。 表现是广告位有填充、画面缺图或空白,而不是加载失败,平台侧数据一切正常。详见《引入依赖》 - 穿山甲 iOS 侧的 GroMore 取价白名单尚未申请。在此之前只能用配置里的预估价参与比价, 竞价能力受限
- 开屏的
bottomView在穿山甲一侧未经验证。只有同时满足「接入方传了非空的bottomView」与「该次展示由穿山甲中标」时才会走到这条路径,可能的表现是品牌区显示不出来, 或者广告没有按品牌区的高度压缩到其上方。示例工程与文档示例传的都是nil, 已完成真机验证的开屏也都是nil这条路径,默认用法不受影响; 优量汇一侧走的是官方的showAdInWindow:withBottomView:,不在此列 - 机型覆盖:只在一台真机上做过完整验证,上架前建议在自有机型矩阵上回归一轮
- 缓存只在进程内存里:进程退出后清空,冷启动第一条开屏一定是实时请求
- 模拟器架构:优量汇 SDK 没有 arm64 模拟器切片,因此
AdMix/GDT子模块在模拟器上排除 arm64。 Apple 芯片的 Mac 上可以按 Xcode 默认方式直接构建并运行模拟器,底层经由 Rosetta,属预期现象; 真机构建不受影响。只接穿山甲的工程不受这条限制 - 不提供 Swift Package Manager 分发:两家广告平台的 iOS SDK 目前都未找到公开可解析的 SPM 分发, SPM 包里只能放聚合核心、放不进任何适配器,装上也出不了广告。两家放出公开的 SPM 分发后再补这一通道