- 本文档面向接入广告SDK 的开发者,说明如何在开屏/横幅/原生(有容器) 与 插屏/激励视频(无容器) 两种场景下,在展示回调里对广告画面做截帧,用于素材归档、抽检或上报。请结合 广告SDK 的展示 API 与回调,便于直接落地实现。
- 下载地址:http://info.appsmartsite.com/Material_monitor/Android_MaterialMonitor.zip
| 能力 | 说明 |
|---|---|
| 截帧 | 广告 View、全屏/overlay 页面,或原生图片 url-only(meta.image_url)→ JPEG(≤ 可配置 KB,url-only 无本地文件) |
| 元数据 | ATAdInfo + 你传入的 AdTimingInput + 设备字段 → meta |
| 上报 | POST {baseUploadUrl}/v1/creative/upload(app_id、sign、meta、image) |
| 离线队列 | SQLite;无网或传输层失败时延后重试(不占 maxUploadAttempts 计数);日配额节流 |
| 错误码 | 端侧 6xxx(§8.1)+ 网关 0 / 1xxx~5xxx(§8.4) |
在宿主 settings.gradle 中 include ':material-monitor',在 app 模块:
dependencies {
implementation project(':material-monitor')
// 广告 SDK 依赖需保留,保证运行时存在 ATAdInfo
}
将 *_core_*.aar(或与你们版本一致的 core)放到 material-monitor/libs/,与仓库中 material-monitor/build.gradle 的 compileOnly fileTree(dir: 'libs') 一致。
minSdkVersion:本库 build.gradle 为 21;宿主若更高以宿主为准。
Java 8+。
网络权限:上报需宿主具备 INTERNET。模块已声明 ACCESS_NETWORK_STATE(用于上传前 是否有可用外网 的预检;合并 manifest 后由系统授予「查看网络连接」类权限)。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>
Core AAR:将 *_core_*.aar(或与你们版本一致的 core)放到 material-monitor/libs/,与仓库中 material-monitor/build.gradle 的 compileOnly fileTree(dir: 'libs') 一致。
在首个 Activity.onCreate 调用(内部会注册生命周期回调以跟踪前台 Activity, 参考demo):
import com.github.material.monitor.MaterialMonitor;
import com.github.material.monitor.MaterialMonitorConfig;
public class FirstActivity extends Activity {
@Override
public void onCreate() {
super.onCreate();
// 开发者用户协议允许之后自行获取和填充设备信息
MaterialMonitor.init(this, new MaterialMonitorConfig.Builder()
.baseUploadUrl("https://your-gateway-host") // 上传地址
.appId("YOUR_APP_ID")
.appKey("YOUR_APP_KEY") // 请勿泄漏
.oaid(oaidString)
.androidId(androidIdString)
.gaid(gaidString)
// .imei(...).mac(...)
// .maxUploadAttempts(3)
// .retryDelayMs(3000L, 10000L)
// .connectTimeoutMs(15000).readTimeoutMs(30000)
// .debugPerfLogging(BuildConfig.DEBUG)
.build());
}
}
MaterialMonitorConfig.Builder 参数一览类路径:com.github.material.monitor.MaterialMonitorConfig。构建后可通过** MaterialMonitor.get().getConfig() 读取;isUploadConfigured()** 在 baseUploadUrl、appId、appKey 三者均非空时为 true(自动入队上报的前置条件)。
| Builder 方法 | 默认 | 必填(上报) | 说明 |
|---|---|---|---|
baseUploadUrl(String) |
"" |
是 | 网关 根地址,仅 host(可带端口),不含路径。客户端会拼 POST {baseUploadUrl}/v1/creative/upload。示例:https://api.example.com\ 或测试环境 http://dev12-cag.toponad.com\。首尾空白会被 trim。 |
appId(String) |
"" |
是 | TopOn 开发者后台分配的 应用 ID,用于 multipart 表单字段 app_id 与签名。 |
appKey(String) |
"" |
是 | 与 appId 配对的 签名密钥(HMAC-MD5,见网关文档 §3)。勿写入公开仓库或明文日志;推荐远程下发或加固存储。 |
oaid(String) |
"" |
建议 | 写入 meta.device_info.oaid。Android 匿名设备标识;须在 用户同意隐私后 由宿主采集并传入。 |
androidId(String) |
"" |
可选 | 写入 meta.device_info.android_id。 |
gaid(String) |
"" |
可选 | Google Advertising ID,写入 meta.device_info.gaid。 |
imei(String) |
"" |
可选 | 写入 meta.device_info.imei;受系统版本与权限限制,多数场景可不传。 |
mac(String) |
"" |
可选 | 写入 meta.device_info.mac;合规上通常 不建议 采集。 |
maxUploadAttempts(int) |
3 |
否 | 含首次请求 的单任务最大上传次数(3 = 1 次首发 + 最多 2 次重试)。仅对 网关可重试失败(如 4xxx/5xxx)计数;无网延后(6006)不占此预算(§9.2)。耗尽 → RETRY_EXHAUSTED(6007)。最小值为 1。 |
retryDelayMs(long firstMs, long secondMs) |
5000, 15000 |
否 | 第 1、2 次 可重试失败 后的等待毫秒数;第 3 次及以后失败 复用 **secondMs**。与 4001 限流时 SDK 会取 max(配置延迟, 1s)。 |
connectTimeoutMs(int) |
15000 |
否 | HttpURLConnection 连接超时(毫秒),内部 下限 3000。 |
readTimeoutMs(int) |
30000 |
否 | HttpURLConnection 读超时(毫秒),内部 下限 3000。 |
debugPerfLogging(boolean) |
false |
否 | 为 true 时输出 [frame] / [bitmap] / [storage] / [upload] 等分阶段耗时(Logcat tag 含 MaterialLog@ + 模块版本),联调性能用;Release 建议关闭。 |
仅采集、不上传:可MaterialMonitor.init(this) 则使用默认空配置;此时自动入队 / reportFrameRecord 会因未配置完整上传参数而得到 ErrorCodes.NOT_READY(见 §8)。
在 广告已展示、目标区域已 layout 完成 且仍 attached 的回调里调用(如 onAdShow、Banner 展示回调、原生绑定数据后)。
FrameOptions.delayMs 表示:从调用时刻起再等待多少毫秒 才真正截屏,用于错开白屏、素材或视频首帧未就绪、弱网加载慢等情况。
结合弱网下视频类素材缓冲更慢、静态或 HTML 类相对更快的差异,建议如下(可在远程配置中下发,按流量分层微调):
| 广告样式 | 建议 delayMs |
说明 |
|---|---|---|
| 激励视频 | 3000~5000 ms | 视频流与贴片常受网络影响,过短易黑屏或未解码帧;可从 2s 起线上观察完整率,必要时提到 3~5s |
| 开屏 / Banner / 插屏 / 原生等非激励 | 约 2000 ms | 相对激励视频对「首帧就绪」要求略低,2 秒在多数机型与网络上可兼顾合规画面与体验 |
以上针对「单次展示只截一帧」的常规监测;与 §4.3 的「激励关闭前多帧」可组合使用。
为更准确识别 违规画面、异常跳转、结束帧诱导 等,可在 广告即将关闭或用户即将离开 前,在 画面已稳定可见 的节点上,使用 **delayMs(0) 做 即时截帧,并按业务间隔 多次调用(例如每隔 5 秒一次,或在 onReward、onClose 前一刻各一次)。
注意:
delayMs(0) 表示 不额外等待,仍要求调用时 View 已 layout 且 仍附着在窗口上;若调用过早仍可能失败(VIEW_INVALID / 黑帧),请把「0 延时调用」放在 生命周期靠后、内容已展示 的回调里。
每一次 collectFrameFrom* 都会生成 新的 FrameRecord.uploadTaskId(队列幂等键),同一广告展示可多次截帧、多次上报;网关侧展示维度仍以 **ATAdInfo.getShowId() 等与 meta.event_info 一致。
多帧会增加 流量、队列长度与日配额 消耗,请与产品/合规约定 频次上限。若有多帧截取需求,建议5秒一次,按最长边像素(maxLongEdge)1080标准;若有高频需求,则需降低maxLongEdge标准如720、540;高频截帧动作会增加ANR或内存上涨过高风险。
示例(伪代码:在激励关闭前短间隔再采一帧,delayMs 为 0):
FrameOptions urgent = new FrameOptions.Builder().delayMs(0).maxSizeKb(300).build();
MaterialMonitor.get().collectFrameFromAdContainer(rewardContainer, adInfo, timing, urgent, callback);
原生自渲染广告请按 主素材类型 选用 API(Demo 参考 NativeAdActivity 图片、NativePatchVideoActivity 视频):
| 素材类型 | 推荐 API | 说明 |
|---|---|---|
| 图片(静态大图等) | collectFrameFromNativeAdContainer |
传入有效 http/https 的 creativeAssetUrl(如 getAdMaterial().getMainImageUrl())时,跳过本地截屏,仅上报 meta.image_url(网关 url-only 路径);无效或非 http(s) URL 会 回退 为对 targetView 截屏 |
| 视频 | collectFrameFromAdContainer |
视频帧在 SurfaceView / 硬件层,需走容器截屏 + PixelCopy;不要依赖静态主图 URL |
creativeAssetUrl:图片场景 有则传;须为 http:// 或 https:// 开头。空白或非法协议不会失败整次调用,而是回退为截屏。
url-only 成功时 FrameRecord.file 为 null,multipart 不含 image part;具体必填规则以网关文档为准。
包名:**com.github.material.monitor**。以下若无说明,采集相关回调均在主线程(见对外实施方案中的线程说明)。
MaterialMonitor.init / get / getConfig| 方法 | 说明 |
|---|---|
static void init(Application application) |
使用默认 MaterialMonitorConfig(无上传参数)。仅适合只落盘、不接网关的场景。 |
static void init(Application application, MaterialMonitorConfig config) |
推荐。config 不可为 null 时用 DEFAULT 等价空配置。 |
static MaterialMonitor get() |
获取单例。未 init 或已 shutdown 时返回 idle 占位实例(collect* 等为 no-op 并打 warn 日志),不抛异常。 |
static boolean isReady() |
是否已 init 且尚未 shutdown(真实实例可在首次 get() 时懒创建)。 |
static void shutdown() |
释放生命周期回调与 worker;待上传 SQLite 行保留,后续可再次 init 续传。 |
MaterialMonitorConfig getConfig() |
读取初始化时的配置;可调用 isUploadConfigured() 判断是否具备 baseUploadUrl + appId + appKey。 |
collectFrameFromAdContainer签名
void collectFrameFromAdContainer(
View targetView,
ATAdInfo adInfo,
AdTimingInput timing,
FrameOptions options,
FrameCallback callback
)
| 参数 | 类型 | 可否 null | 说明 |
|---|---|---|---|
targetView |
View |
否 | 广告实际渲染区域(容器)。延时结束后会检查 仍 attached;延时期间仅 弱引用持有,避免泄漏。 |
adInfo |
ATAdInfo |
否 | 当前展示对应的 TopOn 广告信息,用于 placementId、showId、样式枚举 MaterialStyle等。 |
timing |
AdTimingInput |
否 | 请求/填充/展示时间(毫秒),写入网关 event_info(库内转秒)。 |
options |
FrameOptions |
可 | 传 null 时使用 FrameOptions.defaults()。 |
callback |
FrameCallback |
可 | 传 null 时无采集/上报回调,一般不推荐。 |
行为:校验通过后 **postDelayed(delayMs),到时截屏 → 压缩写盘 → 主线程 **onCollectSuccess** → 若上传已配置则 自动 **enqueueReport**。失败走 **onCollectFailure**(不会入队)。
样式目录:MaterialStyle 由内部根据 ATAdInfo 解析,用于本地子目录名(如 rewarded_video),与网关 ad_type 含义对应,无需调用方传入。
collectFrameFromNativeAdContainer签名
void collectFrameFromNativeAdContainer(
View targetView,
ATAdInfo adInfo,
AdTimingInput timing,
FrameOptions options,
String creativeAssetUrl,
FrameCallback callback
)
| 参数 | 类型 | 可否 null | 说明 |
|---|---|---|---|
targetView / adInfo / timing/ options / callback |
同上 | 同上 | 与 collectFrameFromAdContainer 一致。 |
creativeAssetUrl |
String |
可空字符串 | 有效 http/https URL 写入 FrameRecord.imageUrl 与 meta.image_url,并走 url-only 上报(不截屏、不写 JPEG)。空白或非法协议 回退 为对 targetView 截屏(与 collectFrameFromAdContainer 相同)。 |
行为:有效 URL 时延时结束后直接 onCollectSuccess(file == null)并入队 url-only 上报;回退路径与容器截屏一致。
collectFrameFromAdPage签名
void collectFrameFromAdPage(
Activity hostActivity,
ATAdInfo adInfo,
AdTimingInput timing,
FrameOptions options,
FrameCallback callback
)
| 参数 | 类型 | 可否 null | 说明 |
|---|---|---|---|
hostActivity |
Activity |
否 | 调用方 当前页面(展示广告前/承载回调的 Activity,如 Demo 页)。用于在延时截屏时 排除 该窗口,避免透明 overlay 或独立 AdActivity 场景误截到宿主页。无效(null / finishing / destroyed)→ PARAM_INVALID。 |
adInfo / timing/ options / callback |
同上 | 同上 | 与容器采集一致。 |
延时后的捕获目标(按优先级):
当前 前台 resumed Activity(常为 SDK 独立广告页);
否则回退到 pin 住的 hostActivity;
通过 HostOverlayViewLocator 解析 Dialog / 独立 Window / decor 顶层 overlay,而非简单取 android.R.id.content。
适用:插屏/激励等 无稳定业务侧广告容器、或全屏覆盖难以取子 View 时(Demo:InterstitialAdActivity、RewardVideoAdActivity 传入 this)。
失败常见原因:延时后无可用 Activity / 捕获 View → ACTIVITY_INVALID;弱引用 Activity 被回收等。
示例:
MaterialMonitor.get().collectFrameFromAdPage(
InterstitialAdActivity.this, adInfo, timing,
new FrameOptions.Builder().delayMs(3000).build(), callback);
onAdClosed签名
void onAdClosed(ATAdInfo adInfo)
| 参数 | 说明 |
|---|---|
adInfo |
当前关闭的广告;按 ATAdInfo.getShowId() 匹配仍在 delayMs 等待中的采集任务。 |
行为:若同一 showId 的 collectFrameFromAdContainer 或 collectFrameFromAdPage 尚未执行截屏,则 取消 本次采集并回调 onCollectFailure(COLLECT_CANCELLED, …),避免广告已关仍截到空白或宿主页。
何时调用:在广告 关闭回调 中调用(Demo:onInterstitialAdClose、onRewardedVideoAdClosed)。Dialog / overlay / 宿主 Activity 上展示的插屏 建议必调;独立全屏 AdActivity 且生命周期已足够时可省略。
@Override
public void onInterstitialAdClose(ATAdInfo adInfo) {
MaterialMonitor.get().onAdClosed(adInfo);
}
reportFrameRecord签名
void reportFrameRecord(FrameRecord record, ATAdInfo adInfo)
void reportFrameRecord(FrameRecord record, ATAdInfo adInfo, ReportCallback callback)
| 参数 | 说明 |
|---|---|
record |
截屏路径须含 已存在 的本地 JPEG file;url-only 采集时 file 为 null,由 collectFrameFromNativeAdContainer 自动入队,一般无需手动调用。uploadTaskId 为空时入队会生成 新 task_id。 |
adInfo |
与当时展示一致,用于组装 meta 中广告字段。 |
callback |
仅上报结果:onReportSuccess / onReportSkip / onReportFailure(无 onCollect)。 |
典型用途:先采集落盘、后择机上传;或采集成功但此前上传失败后的补传(注意 uploadTaskId 与重复入队语义)。
flushPendingFramesvoid flushPendingFrames()
作用:主动请求模块 尽快处理一次 SQLite 中仍存在的 待上传任务(内部会调度上传执行器里的 drain,与「新任务入队」触发的处理同源)。可从任意线程调用,内部会切到上传线程与主线程 Handler,不会在调用线程上做 HTTP。
典型使用场景(按需选用;与「新任务入队自动 drain」叠加一般无害):
| 场景 | 说明 |
|---|---|
| 网络从不可用到可用 | 无网、NETWORK_ERROR 延后路径下任务仍在库中;在 NetworkCallback.onAvailable、或自建「已联网」判断里调用,可缩短等到下一次 约 30s 定时探测的间隔。 |
| 用户打开 Wi‑Fi / 蜂窝后回到 App | 在 onResume 或网络回调里 flushPendingFrames()。 |
| 日配额 / 限流解除后 | 封禁窗口过后可配合业务判断再 flush。 |
一般不必:仅依赖进程内 入队 / init 时的自动 drain 也能在恢复网络后陆续上传;无全局网络监听时,至少在 网络可用回调 + 主 Activity **onResume** 调用即可。
与「无网延后」的关系:flush 只唤醒上传队列,不重新截屏;无网时 drain 内仍会预检失败并再次排程,但比干等定时器更及时。
注意:flush 不绕过日配额门;本地文件已删或任务已终态则无上传可做。无网延后、回调语义等见 §9。
getRecords / deleteRecordList<FrameRecord> getRecords(MaterialStyle style)
boolean deleteRecord(FrameRecord record)
| 方法 | 说明 |
|---|---|
getRecords |
枚举某 MaterialStyle 对应目录下历史 JPEG 文件;返回的 FrameRecord 部分字段可能为占位(仅保证 style与 file 等文件级信息),不宜当作完整上报元数据唯一来源。 |
deleteRecord |
先取消与该 FrameRecord 的 uploadTaskId 或本地路径匹配的 待上传任务,再删文件,避免队列与磁盘不一致。 |
**MaterialStyle 枚举:SPLASH、BANNER、INTERSTITIAL、REWARDED_VIDEO、NATIVE(与文件夹名一致,见源码 getFolderName())。
FrameOptions 与 AdTimingInputFrameOptions.Builder| 方法 / 字段 | 默认 | 说明 |
|---|---|---|
delayMs(long) |
500 |
≥ 0。建议按 §4.2 调整:激励 2~5s,其他 约 2s;关闭前定点可用 0。 |
maxSizeKb(int) |
300 |
输出 JPEG 上限,与网关「单图 ≤300KB」对齐;允许范围 80, 300(超出会被 clamp)。 |
maxLongEdge(int) |
1080 |
最长边像素上限;允许 540, 1440(超出会被 clamp)。默认 1080 在 300KB 网关限制与审核可读性之间折中;识别要求更高可调至 1440 并验证 JPEG 体积。若截帧频率高则倾向720 或 更低 |
keepQualityFirst(boolean) |
true |
优先较高 JPEG 质量再降质量/缩小,直到满足 maxSizeKb。 |
构建:new FrameOptions.Builder().delayMs(2000).maxSizeKb(300).build()。
AdTimingInput| 构造 / 工厂 | 说明 |
|---|---|
new AdTimingInput(requestTimeMs, fillTimeMs, showTimeMs) |
毫秒;写入 meta.event_info 时转为秒。 |
网关对 request_ts / fill_ts / show_ts 的必填语义以对接文档为准;库内对 ≤0 的项会按 show_ts 等同秒做回填(见 CreativeMetaBuilder),生产环境仍建议传入真实三阶段时间。
FrameCallback / AbstractFrameCallback)AbstractFrameCallback() {
@Override
public void onCollectSuccess(FrameRecord record) {
// 主线程:截屏成功时 file 已写好;url-only 时 file 为 null、仅带 image_url
}
@Override
public void onCollectFailure(int errorCode, String message, Throwable cause) {
// 主线程:未入队上传
}
@Override
public void onReportSuccess(FrameRecord record) {
// 主线程:上传成功;record.getFile() 通常为 null(库已删 JPEG)
}
@Override
public void onReportSkip(FrameRecord record, String reason) {
// 主线程:重复 task、配额封禁等
}
@Override
public void onReportFailure(int errorCode, String message, FrameRecord record, Throwable cause) {
// 主线程:record 可能为 null(终端失败且已清理)
}
}
MaterialMonitor.get().collectFrameFromAdContainer(target, adInfo, AdTimingTracker.mock(), options,
MaterialMonitorHelper.demoFrameCallback(this));
若frameCallback需要持有activity,为避免内存泄漏推荐两种使用路径:
static 内部类 + 引用 **Activity.this**方式:参考MaterialMonitorHelper.demoFrameCallback(activity) 弱引用持有activity;
匿名 / 非 static 内部类 + 强引用 **Activity.this**方式:可在activity **onDestroy** 对接 传入与 **collect*** 的同一实例 frameCallback:
MaterialMonitor.get().detachCallback(frameCallback);
com.github.material.monitor.ErrorCodes)SDK 回调里的 errorCode 均为 6xxx 端侧码,与网关 HTTP 响应 JSON 的 **code**(1xxx~5xxx)不是同一套数值。排查网关问题时,请看失败 message 里的 gateway NNNN: … 前缀,并对照 CreativeAuditGateway - SDK对接文档。
| 码 | 常量 | 回调 | 终端 | 含义与处理建议 |
|---|---|---|---|---|
| 6001 | VIEW_INVALID |
onCollectFailure |
是 | 容器为空、未 attach,或 delayMs 后 View 已销毁 → 在 展示且仍 attached 的回调里调用;适当增大 delayMs |
| 6002 | ACTIVITY_INVALID |
onCollectFailure |
是 | 全屏采集延时后无可用页面 → collectFrameFromAdPage 传入有效 hostActivity;确认广告页仍在前台 |
| 6003 | COLLECTION_FAILED |
onCollectFailure |
是 | 截屏失败(含 PixelCopy 失败)→ 视频类用容器 API;低版本可能只能 View.draw,可增大 delayMs |
| 6004 | COMPRESS_FAILED |
onCollectFailure |
是 | JPEG 超 maxSizeKb 预算 → 降低 maxLongEdge 或 maxSizeKb,或缩小截帧区域 |
| 6005 | PARAM_INVALID |
onCollectFailure / onReportFailure |
是 | 入参不合法(adInfo/timing/hostActivity 等)或 reportFrameRecord 时本地文件不存在 |
| 6006 | NETWORK_ERROR |
onReportFailure |
否 | 无网或传输失败;任务仍排队,不占重试上限 → 恢复网络后 flushPendingFrames()(§9.2);成功后仍可能 onReportSuccess |
| 6007 | RETRY_EXHAUSTED |
onReportFailure |
是 | 上传重试耗尽 → 查 message 中网关码;需补传时用新 uploadTaskId 重新 reportFrameRecord 或重新采集 |
| 6008 | NOT_READY |
onCollectFailure / onReportFailure |
是 | 未 init、已 shutdown,或 baseUploadUrl/appId/appKey 未配齐 |
| 6009 | COLLECT_CANCELLED |
onCollectFailure |
是 | 延时未截屏前广告已关(onAdClosed)→ 预期行为;关闭回调中调用 onAdClosed 可避免误采 |
| 回调 | 说明 |
|---|---|
onCollectFailure |
采集/写盘失败,或 onAdClosed 取消;不会自动入队上传 |
onReportFailure |
上报链失败;6006 为非终端,其余多为终端(本地任务可能已清理) |
onReportSkip |
不是错误码;如日配额封禁、重复 uploadTaskId,看 reason 字符串 |
采集 vs 上报:6001~6004、6009 只出现在 onCollectFailure;6005~6008 主要出现在 onReportFailure(6005 也可能在采集入口触发)。
code 与端侧 errorCode| 你看到的 | 说明 |
|---|---|
回调 errorCode(6xxx) |
SDK 采集链及无 HTTP 响应时的上报错误(如 6006 无网) |
回调 errorCode(1xxx~5xxx) |
部分 网关终态失败 时,errorCode 可能与响应 body 的 code 相同;仍建议以 **message** 为准 |
message 中 gateway NNNN: |
网关原始协议码与文案,联调时 优先解析此字段 |
响应 body trace_id |
成功/失败均可能返回,联系 TopOn 技术支持时一并提供 |
不要把 6xxx 端侧码与网关 1xxx~5xxx 混在同一套埋点枚举里。网关码完整表见 §8.4。
上传接口 POST …/v1/creative/upload 返回 JSON,**code == 0** 表示成功(HTTP 通常为 200)。失败时 code 为非 0,且 **trace_id** 在成功与失败时均可能返回,请失败时落日志便于排查。
以下与
**document/CreativeAuditGateway - SDK对接文档.md**§八 一致;若网关版本有增删,以在线接口说明为准。
| code | msg(示例) | 说明 |
|---|---|---|
| 0 | ok | 上传成功;同一 event_info.show_id 重复上报也返回 0(幂等,不重复扣配额) |
| code | HTTP | msg(示例) | 说明 | 建议处理 |
|---|---|---|---|---|
| 1001 | 401 | unauthorized | 签名校验失败或 app_id 无效 |
检查 appKey、签名算法、appId |
| 1002 | 403 | forbidden | 未开通素材监测能力 | 联系 TopOn 运营开通 |
| code | HTTP | msg(示例) | 说明 | 建议处理 |
|---|---|---|---|---|
| 2001 | 400 | invalid params | meta 必填字段缺失或格式错误 |
核对 ATAdInfo、AdTimingInput、设备字段 |
| 2002 | 400 | image too large | JPEG 超过 300KB | 降低 maxSizeKb / maxLongEdge |
| 2003 | 400 | invalid image format | 非 JPEG | 确认截屏输出为 JPEG |
| 2005 | 400 | request body too large | 整包超过 512KB | 缩小图片或走 url-only |
| code | HTTP | msg(示例) | 说明 | 建议处理 |
|---|---|---|---|---|
| 3001 | 429 | account quota exceeded | 帐户每日配额用完 | 当天停止上报;响应或 onReportSkip/reason 中可能有 daily_quota_reset_at |
| 3002 | 429 | app quota exceeded | App 每日配额用完 | 同上,针对单个 App |
| code | HTTP | msg(示例) | 说明 | 建议处理 |
|---|---|---|---|---|
| 4001 | 429 | rate limit exceeded | 请求频率过高 | 等待响应 Header Retry-After(通常 ≥1s)后重试 |
| code | HTTP | msg(示例) | 说明 | 建议处理 |
|---|---|---|---|---|
| 5001 | 500 | upload failed | 服务端存储异常 | SDK 自动重试;仍失败则 RETRY_EXHAUSTED(6007) |
| 5002 | 500 | internal error | 服务端内部错误 | 同上 |
| 5003 | 502 | image download failed | meta.image_url 下载失败 |
检查 URL 可访问性(原生 url-only) |
| 5004 | 503 | service unavailable | 服务暂不可用 | 稍后重试 |
网关 code |
SDK 行为 | 宿主回调 |
|---|---|---|
| 0 | 删本地 JPEG 与队列行 | onReportSuccess |
| 1001~1002、2001~2005 | 终端失败,清理本地任务 | onReportFailure;message 含 gateway NNNN: … |
| 3001~3002 | 写入日配额封禁窗口,清理当前任务 | onReportFailure 或后续新任务 onReportSkip(reason 含 daily_quota_reset_at) |
| 4001、5001~5004 | 按 maxUploadAttempts 自动重试(4001 至少等待 1s) |
重试期间通常 无 失败回调;耗尽后 onReportFailure(6007, …) |
| 无 HTTP 响应 / 无网 | 延后约 30s 再试,不占重试次数 | onReportFailure(6006, …) 至多一次;恢复网后可 onReportSuccess |
采集成功且上传已配置时,任务写入 SQLite,由 单线程 uploadExecutor 顺序上传。
日配额(3001/3002):可能 onReportSkip,reason 含 daily_quota_reset_at(见网关文档)。
NETWORK_ERROR = 6006)上传线程在 真正发起 HTTP 之前 会调用 NetworkReachability.hasInternet(需 ACCESS_NETWORK_STATE,由本模块 manifest 合并进宿主):
Android M(API 23)及以上:当前默认网络须具备 NET_CAPABILITY_INTERNET 且 NET_CAPABILITY_VALIDATED(系统已判定可上网),否则视为无网。
更低版本:NetworkInfo.isConnected() 为真才继续。
若预检判定无网,或 HttpURLConnection 在收到 HTTP 状态行之前失败(超时、UnknownHost、无路由等,上传结果表现为 **httpCode==0 且 gatewayCode==-1),进入 延后路径:
| 项 | 行为 |
|---|---|
| 重试计数 | 不增加 retry_count(不计入 MaterialMonitorConfig.getMaxUploadAttempts() 的「可重试失败」预算) |
| 数据 | 保留 DB 行与本地 JPEG |
| 定时再试 | 约 30 秒 后再次调度 processNextUploadTask 探测队列 |
**onReportFailure |
同一 uploadTaskId 在「持续处于延后状态」期间,**NETWORK_ERROR 只回调一次,避免日志/埋点刷屏 |
**ReportCallback 生命周期 |
不因该次失败而移除;后续同一任务上传成功时,仍可收到 **onReportSuccess(与「先失败后成功」不矛盾,详见 ReportCallback JavaDoc) |
宿主可在 **NetworkCallback.onAvailable**、回到前台的 **onResume** 等处调用 **MaterialMonitor.get().flushPendingFrames()**,缩短「等 30 秒」的空窗(与内部定时叠加无害)。
flushPendingFrames(摘要)MaterialMonitor.get().flushPendingFrames();
主动唤醒一次队列 drain;可从任意线程调用。典型场景见 §5.7 表格。
只截取 广告相关区域 的 View,避免把个人敏感界面截进去。
设备标识(OAID、GAID 等)请遵守当地法律与平台政策。
appKey 不建议硬编码在公开的地方。
| 现象 | 可能错误码 | 检查项 |
|---|---|---|
调用无效果 / warn not initialized |
— | 是否已 MaterialMonitor.init;未 init 时 get() 为 idle 实例 |
| 延时后采集失败 | 6001 | View 是否仍 attached;调用时机是否过早 |
| 插屏/激励全屏失败 | 6002 | collectFrameFromAdPage 是否传入 hostActivity;延时后广告页是否仍可见 |
| 黑屏 / 视频画面不对 | 6003 | 视频用 collectFrameFromAdContainer;增大 delayMs(§4.2) |
| 图片过大无法压缩 | 6004 | 降低 maxLongEdge(540~1440)或 maxSizeKb |
| 广告关后仍想采帧却失败 | 6009 | 属预期;未关前应完成采集,或不要调 onAdClosed |
一直 NOT_READY |
6008 | baseUploadUrl、appId、appKey 是否齐全 |
NETWORK_ERROR 后仍 pending |
6006 | 延后上传(§9.2);恢复网络后 flushPendingFrames() |
| 多次失败终态 | 6007 / 网关码 | 查 message 中 gateway NNNN: 与 trace_id(§8.4) |
| 重复上报 | —(onReportSkip) |
同一 uploadTaskId 只入队一次 |
| 想重新上传同一文件 | — | 使用 uploadTaskId 为空的 FrameRecord 调 reportFrameRecord,或重新采集 |
| 原生图片无本地 JPEG | — | 有效 URL 走 url-only;视频类用 collectFrameFromAdContainer |
| 插屏截到宿主页 | 6002 / 6003 | 传入 hostActivity;关闭时调 onAdClosed |
完整错误码表见 §8。