REMAX SDK · EXTERNAL INTEGRATION

广告风控外部业务对接指南

面向宿主业务和外部检测 SDK:配置 Ad_Risk_Config、读取设备风险处置策略、限制广告平台,并接收广告收益与点击数据。

当前依赖基线 core 1.0.15
bill support_risk_1.0.9
ad-risk 1.0.8

1. core、bill、risk 最新依赖

以下版本来自当前仓库的发布配置。Risk 不向宿主传递 Bill,外部业务应显式声明三项依赖,避免依赖解析结果不一致。

模块依赖

// app/build.gradle.kts
dependencies {
    implementation("com.github.toukaremax:core:1.0.15")
    implementation("com.github.toukaremax:bill:support_risk_1.0.9")
    implementation("com.github.toukaremax:ad-risk:1.0.8")
}

2. Ad_Risk_Config 在线参数(SDK 内部获取)

远程配置逻辑 Key 固定为 Ad_Risk_Config。设备环境处置字段与行为风控字段放在同一份扁平 JSON 中。SDK 启动时先使用缓存、Assets 或硬编码默认值,再异步拉取远程配置;合法的远程配置会即时生效并写入缓存。

完整配置示例

{
  "behavior_mode": 2,
  "mistake_click_time": 20,
  "mistake_click_count": 2,
  "restrict_interval": 1800,
  "close_click_count": 3,
  "close_activity_class_names": [],
  "limits": {
    "app_open":     { "request_limit": 0, "show_limit": 0, "click_limit": 0, "fill_fail_limit": 0 },
    "interstitial": { "request_limit": 0, "show_limit": 0, "click_limit": 0, "fill_fail_limit": 0 },
    "native":       { "request_limit": 0, "show_limit": 0, "click_limit": 0, "fill_fail_limit": 0 },
    "banner":       { "request_limit": 0, "show_limit": 0, "click_limit": 0, "fill_fail_limit": 0 },
    "rewarded":     { "request_limit": 0, "show_limit": 0, "click_limit": 0, "fill_fail_limit": 0 }
  },
  "vpn_ad_config": -1,
  "dns_ad_config": -1,
  "ip_ad_config": -1,
  "simulator_ad_config": -1,
  "adb_ad_config": -1,
  "sim_ad_config": -1,
  "no_gp_ad_config": -1,
  "sys_9_low_ad_config": -1,
  "play_ad_config": -1
}

行为风控字段

字段默认值 / 范围含义
behavior_mode2;0、1、20 关闭:不计数、不限制、不强关;1 执行:计数、上报并真正应用冷却/强关;2 观察:计数和上报,但不限制、不强关。模式变化会清空行为风控状态。
mistake_click_time20 秒;整数 ≥ 0点击广告后 App 进程进入后台并返回,耗时小于等于该值时判定为误触。值为 0 不是关闭开关。
mistake_click_count2;整数 ≥ 0同一“广告类型 + position”的误触累计到第 N 次时触发;0 表示不触发限制,但仍可上报误触事件。
restrict_interval1800 秒;整数 ≥ 0执行模式下命中阈值后的冷却时长;0 表示冷却立即结束。
close_click_count3;整数 ≥ 0同一插页广告 session 第 N 次点击时触发;执行模式会强关及冷却,观察模式只计数和上报。仅插页生效,0 表示关闭强关。
close_activity_class_names[];非空字符串数组追加允许 SDK 内置关闭的 Activity 完整类名。命中后在主线程调用 finish();本地默认 AdMob Activity 始终保留。

误触风控的生效场景

前提:已收到广告点击回调,且 behavior_mode 为 1 或 2。用户未点击广告而直接关闭时,不进入误触判断。
  1. 点击后 App 进程进入后台并返回:返回前台时上报实际 leave_time;若停留时长不超过 mistake_click_time,则判定并上报误触。
  2. 点击后 App 进程未进入后台:收到广告关闭回调时按 leave_time=0 结算,并判定、上报误触。

各广告类型的 limits

<type> 支持 app_open、interstitial、native、banner、rewarded。当前行为风控只处理 AdMob;同类型 position 共用阈值,但分别计数和冷却。

字段默认值触发规则
limits.<type>.request_limit0最多允许 N 次业务请求,第 N+1 次触发;0 不限制。预加载不计入业务请求。
limits.<type>.show_limit0最多允许 N 次实际曝光,第 N+1 次触发;0 不限制。
limits.<type>.click_limit0最多允许 N 次点击,第 N+1 次触发;0 不限制。
limits.<type>.fill_fail_limit0连续加载失败到第 N 次时触发,加载成功会清零;0 不限制,预加载结果也参与。

设备环境处置字段

JSON 字段AdRisk.signalConfig 属性外部检测场景
vpn_ad_configvpnAdConfig设备正在使用 VPN
dns_ad_configdnsAdConfig设备命中 DNS 代理风险
ip_ad_configipAdConfig设备命中异常 IP 风险
simulator_ad_configsimulatorAdConfig模拟器设备
adb_ad_configadbAdConfig设备处于 ADB 调试状态
sim_ad_configsimAdConfig设备没有 SIM 卡
no_gp_ad_confignoGpAdConfig设备没有 Google Play
sys_9_low_ad_configsys9LowAdConfigAndroid 9 以下设备
play_ad_configplayAdConfig外部 XM SDK 服务端核验结果
-1 · NOT_CONFIGURED

不限制

未配置、待确认或未获得检测结果时放行。

0 · BLOCK_ADMOB

仅限制 AdMob

外部检测命中后禁用 AdMob,其他广告平台仍可承接。

1 · BLOCK_ALL_ADS

限制全部平台

外部检测命中后禁用 AdMob、GAM、Pangle 和 TopOn。

重要:这 9 个字段只描述“检测命中后如何处置”。Risk 不负责检测 VPN、模拟器等设备状态,也不会仅凭配置值自动禁用广告;外部业务必须把检测结果与配置组合后调用平台限制 API。
整份 JSON 会一起校验:行为字段必须在合法范围内,9 个设备处置字段只能为 -1 / 0 / 1。远程 JSON 非法时不会部分应用,而是继续使用当前有效配置。

3. 获取 signalConfig,并配合外部检测禁用平台(按项目可选)

Ad_Risk_Config→ AdRisk.signalConfig+ 外部设备检测结果→ updatePlatformRestriction→ Bill 平台门禁

读取当前值或持续监听

import com.android.common.risk.AdRisk
import kotlinx.coroutines.flow.collectLatest

// 同步读取当前完整快照
val current = AdRisk.signalConfig.value
val vpnPolicy = current.vpnAdConfig

// 推荐:监听缓存、Assets 和在线配置的后续变化
applicationScope.launch {
    AdRisk.signalConfig.collectLatest { config ->
        val latestVpnPolicy = config.vpnAdConfig
        // 保存最新值,或通知业务重新执行检测处置
    }
}
signalConfig 是只读 StateFlow<AdRiskSignalConfig>。初始快照全部为 -1,随后应用缓存 / Assets,并可能被远程配置更新;需要跟随在线配置时不要只读取一次。

处置示例:检测到 VPN

外部检测确认设备正在使用 VPN 后,只需读取 vpnAdConfig 并按值处理:

import com.android.common.bill.ads.config.AdPlatform
import com.android.common.risk.AdRestrictionScope
import com.android.common.risk.AdRisk
import com.android.common.risk.AdRiskSignalConfig

if (vpnDetected) {
    when (AdRisk.signalConfig.value.vpnAdConfig) {
        AdRiskSignalConfig.BLOCK_ADMOB -> {
            // 0:只禁用 AdMob
            AdRisk.updatePlatformRestriction(
                AdPlatform.ADMOB,
                AdRestrictionScope.PROCESS,
                true
            )
        }

        AdRiskSignalConfig.BLOCK_ALL_ADS -> {
            // 1:禁用全部广告平台
            AdPlatform.entries.forEach { platform ->
                AdRisk.updatePlatformRestriction(
                    platform,
                    AdRestrictionScope.PROCESS,
                    true
                )
            }
        }

        AdRiskSignalConfig.NOT_CONFIGURED -> Unit // -1:不处理
    }
}

DNS、异常 IP、模拟器等检测的处理方式完全相同,只需把 vpnAdConfig 换成对应的配置字段。检测状态恢复正常后,用相同平台和 Scope 调用 restricted=false 解除本业务设置的限制;存在多项风险时,应在所有相关风险都解除后再清除。

限制生命周期

Scope保存方式自动失效主动清除
AdRestrictionScope.PROCESS仅内存当前应用进程结束同一平台和 Scope 再传 restricted=false
AdRestrictionScope.TODAY本地持久化设备本地自然日变化同一平台和 Scope 再传 restricted=false

4. 广告收益与点击回调

广告库通过 BillConfig 暴露两个可选回调,参数类型均为 RevenueAdData。建议在 Application.onCreate() 中完成赋值。

注册回调

import com.android.common.bill.BillConfig

BillConfig.onAdRevenue = { data ->
    // 每次广告展示产生收益时回调(impression 级)
    analytics.logAdRevenue(
        value = data.revenue.value,
        currency = data.revenue.currencyCode,
        platform = data.platform,
        network = data.adRevenueNetwork,
        adUnitId = data.adRevenueUnit,
        adSourceId = data.adSourceId,
        placement = data.adRevenuePlacement,
        format = data.adFormat
    )
}

BillConfig.onAdClick = { data ->
    // 用户点击广告时回调
    analytics.logAdClick(
        platform = data.platform,
        network = data.adRevenueNetwork,
        adUnitId = data.adRevenueUnit,
        adSourceId = data.adSourceId,
        placement = data.adRevenuePlacement,
        format = data.adFormat,
        value = data.revenue.value,
        currency = data.revenue.currencyCode
    )
}

// 不再接收时可清除
// BillConfig.onAdRevenue = null
// BillConfig.onAdClick = null

RevenueAdData 可获取的参数

字段类型含义 / 可能值
revenue.valueDouble单次展示收益金额;点击回调携带当前广告已知的收益值,平台未提供时可能为 0。
revenue.currencyCodeString货币代码,通常为 USD;SDK 不做汇率换算,使用金额前应同时检查该字段。
platformString聚合平台:Admob、GAM、Pangle、TopOn。
adRevenueNetworkString实际广告网络 / 底层广告源名称;拿不到时回退为聚合平台名称。
adRevenueUnitString本次请求使用的聚合广告位 ID(Ad Unit / Placement ID)。
adSourceIdString底层广告源或实例 ID;拿不到时回退为 adRevenueUnit。
adRevenuePlacementString平台提供的 placement、scenario 或实例名称;平台未提供时可能为空字符串。
adFormatStringSplash、Interstitial、Native、Banner、FullNative、Rewarded。
两个回调都会在广告平台触发事件的原线程同步执行。不要在回调中直接执行网络请求、文件 I/O 等耗时操作;请切换到业务自己的协程或线程。宿主回调抛出的异常会被广告库隔离,不会中断广告主链路。
每个属性保存一个回调;再次给 BillConfig.onAdRevenue 或 BillConfig.onAdClick 赋值会替换之前的回调。

5. Bill 设置固定广告源 (用于调试工具)

通过 AdSourceController.setCurrentSource(...) 设置 Bill 当前使用的聚合平台。建议在 Application.onCreate() 中、首次广告请求之前完成设置。

固定为指定平台

import com.android.common.bill.ads.bidding.AdSourceController
import com.android.common.bill.ads.bidding.AdSourceController.AdSource

// 固定使用 AdMob;也可改为 GAM、PANGLE 或 TOPON
AdSourceController.setCurrentSource(AdSource.ADMOB)

// 读取当前设置
val currentSource = AdSourceController.getCurrentSource()

恢复为竞价模式

// BIDDING 为默认模式:由 Bill 在所有可用平台中竞价选择
AdSourceController.setCurrentSource(AdSource.BIDDING)
AdSource行为
ADMOB固定使用 AdMob
GAM固定使用 Google Ad Manager
PANGLE固定使用 Pangle
TOPON固定使用 TopOn
BIDDING恢复默认竞价模式,由可用平台参与选择
注意:该设置会持久化并作用于全部广告类型。固定平台未启用、尚未初始化、命中频控,或固定 AdMob 被 Risk 拦截时,本次广告直接不可用,不会自动切换到其他平台;需要其他平台承接时请设置为 AdSource.BIDDING。