跳到主要内容

TapADN SDK(聚合) Android 接入文档

SDK 支持版本​

  • TapADN SDK(聚合) 和 优量汇 (Tencent Ad SDK) 最低支持 Android 5.0(API level 21)
  • 穿山甲 (Pangle) SDK 最低支持 Android 7.0(API level 24)
  • 编译环境为 Android Studio

SDK 配置​

Maven 仓库配置​

在 project 级别的 build.gradle 文件中添加穿山甲 (Pangle) Maven 的引用:

allprojects {
repositories {
maven {
url 'https://artifact.bytedance.com/repository/pangle'
}
}
}

依赖配置​

将 DirichletAD_Mediation_5.3.1.6.aar、DirichletAD_CSJ_Adapter_5.3.1.6.aar、DirichletAD_GDT_Adapter_5.3.1.6.aar 拷贝到游戏目录下的 src/main/libs 目录中。

提示

从 SDK 5.2.1.5 版本开始,Dirichlet SDK 本身不再依赖 Android Support Library、AndroidX 或 Glide。第三方广告平台的依赖以各平台官方要求为准。

在项目目录下 build.gradle 文件中添加代码:

repositories {
flatDir{
dirs 'src/main/libs'
}
}
dependencies {
implementation (name: "DirichletAD_Mediation_5.3.1.6", ext: "aar") // TapADN SDK(聚合)(包含TapADN SDK)
implementation (name: "DirichletAD_CSJ_Adapter_5.3.1.6", ext: "aar") // 穿山甲 adapter SDK
implementation (name: "DirichletAD_GDT_Adapter_5.3.1.6", ext: "aar") // 优量汇 adapter SDK

implementation('com.pangle.cn:ads-sdk-pro:7.8.1.5') { // 穿山甲广告 SDK
exclude group: 'com.android.support'
}

implementation 'com.qq.e.union:union:4.701.1571' // 优量汇广告SDK
implementation 'com.squareup.okhttp3:okhttp:3.12.1'
}

可选广告网络​

爱奇艺和百青藤均为可选网络,仅在后台开通后追加对应依赖。

爱奇艺(可选)​

implementation (name: "DirichletAD_IQY_Adapter_5.3.1.6", ext: "aar")
implementation (name: "iadsdk-release-2.3.102.110", ext: "aar")

百青藤(可选)​

从聚合 SDK 压缩包的 dirichlet/optional/baidu-androidx/ 目录取出百青藤适配器 AAR,再复制到项目的 libs 目录。

implementation (name: "DirichletAD_BD_Adapter_5.3.1.6", ext: "aar")
implementation 'com.baidu:mobads:9.46.0'
注意

百青藤仅支持已启用 AndroidX 和 Jetifier 的工程;Support Library 工程不要添加该依赖。

权限申请 & 清单配置​

聚合 SDK 基础配置​

Android Manifest 会合并宿主、聚合 SDK、适配器及三方 SDK 的声明。请只引入实际使用的广告网络,在 Android Studio 的 Merged Manifest 中检查最终权限与组件;AAR 已声明的内容无需重复添加。

聚合 SDK 的基础网络权限如下,当前 AAR 已包含:

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

当前聚合 SDK AAR 还声明了 REQUEST_INSTALL_PACKAGES、QUERY_ALL_PACKAGES 及 OAID 相关厂商权限,并包含 TapADFileProvider。这些声明会参与自动合并;是否使用对应能力,应结合实际功能、用户授权及分发渠道要求确认。不要把 AAR 中存在某项声明理解为必须向用户申请全部权限。

各广告网络专属配置​

以下按本文当前依赖版本核对。仅接入对应网络时检查相应配置;升级三方 SDK 后,应重新核对其接入说明与最终合并清单。

广告网络当前三方 SDK 版本自动合并的关键配置宿主需要检查的内容
穿山甲7.8.1.5CSJ 适配器包含 TT_PANGOLIN 签名权限、TTFileProvider 和 @xml/dirichlet_csj_file_paths;三方 AAR 包含广告 Activity、Service 等组件。签名权限与 Provider 未被宿主覆盖或移除;无需重复声明或复制旧版组件配置。
优量汇4.701.1571GDT 适配器包含 GDTFileProvider、${applicationId}.gdt.fileprovider 和 @xml/gdt_file_path;三方 AAR 包含广告组件。Provider 与路径资源正常合并;三方 AAR 还会带入粗略位置、安装应用、查询应用列表等权限,按实际功能检查。
百青藤(可选)9.46.0三方 AAR 包含 BdFileProvider、${applicationId}.bd.provider、@xml/bd_file_paths 及广告 Activity;适配器补充 Activity 配置。使用本文指定的 AndroidX 适配器并启用 AndroidX / Jetifier;无需再手动复制 Provider。
爱奇艺(可选)2.3.102.110三方 AAR 包含 QyFileProvider、${applicationId}.qy.fileprovider、@xml/qyiad_file_paths 及 Activity、Service。同时引入适配器与三方 AAR;三方 AAR 已声明 QUERY_ALL_PACKAGES、VIBRATE、WAKE_LOCK 等权限,需检查最终合并结果。

各网络 Manifest XML 对照示例​

以下展示各网络的关键签名权限和 Provider 配置,当前版本已由对应 AAR 自动合并,仅供核对,不是要求全部复制的宿主清单。只检查已接入网络的部分;Activity、Service 等其他组件仍由三方 AAR 合并。路径资源随对应 AAR 提供,无需自行创建。

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 穿山甲:广播通信签名权限,当前 CSJ 适配器和三方 AAR 已包含 -->
<permission
android:name="${applicationId}.openadsdk.permission.TT_PANGOLIN"
android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.openadsdk.permission.TT_PANGOLIN" />

<application>
<!-- 穿山甲:由 CSJ 适配器合并 -->
<provider
android:name="com.bytedance.sdk.openadsdk.TTFileProvider"
android:authorities="${applicationId}.TTFileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/dirichlet_csj_file_paths" />
</provider>

<!-- 优量汇:由 GDT 适配器合并 -->
<provider
android:name="com.qq.e.comm.GDTFileProvider"
android:authorities="${applicationId}.gdt.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/gdt_file_path" />
</provider>

<!-- 百青藤(可选):由三方 SDK 合并 -->
<provider
android:name="com.baidu.mobads.sdk.api.BdFileProvider"
android:authorities="${applicationId}.bd.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/bd_file_paths" />
</provider>

<!-- 爱奇艺(可选):由三方 SDK 合并 -->
<provider
android:name="com.mcto.sspsdk.QyFileProvider"
android:authorities="${applicationId}.qy.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/qyiad_file_paths" />
</provider>

</application>
</manifest>

${applicationId} 由 Android 构建工具替换为宿主应用 ID。不要直接使用 Demo 包名,也不要将多个应用的 Provider authorities 配置为同一固定值。

可选权限与隐私控制​

可选权限应按实际接入网络和功能选择,不要求全部添加。权限声明不代表用户已授权;涉及运行时权限的,应在相应功能需要时申请,并处理用户拒绝的情况。

权限或能力配置原则
READ_PHONE_STATE、ACCESS_WIFI_STATE按实际网络的功能需求配置,并配合设备状态、Wi-Fi 等隐私开关。当前自研 ADN 不采集或转发 IMEI。
ACCESS_COARSE_LOCATION、ACCESS_FINE_LOCATION使用位置相关功能时按需选择;向用户说明用途并取得相应授权。手动提供位置同样受隐私控制约束。
REQUEST_INSTALL_PACKAGES用于需要发起 APK 安装的场景;声明此权限不等于用户已允许安装未知来源应用。
POST_NOTIFICATIONS用于需要发送通知的场景,按系统版本处理通知授权。
QUERY_ALL_PACKAGES涉及应用安装列表查询,按实际需求和分发渠道要求配置,并在隐私说明中披露用途。
蓝牙、存储、悬浮窗权限不作为所有接入方的通用推荐;仅在对应功能和三方 SDK 确有需要时配置,并核对系统版本限制。
RECEIVE_USER_PRESENT、EXPAND_STATUS_BAR 等 Demo 场景权限不因 Demo 使用就直接复制到宿主,按自身业务需要判断。

宿主可选权限 XML 示例​

以下列出常见可选项的写法。按上表选择需要的条目,删除不使用的项目,不要整段直接添加;已从 AAR 合入的权限不必重复声明。uses-permission 位于 manifest 内、application 外。

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 可选:设备状态与 Wi-Fi 状态,按接入网络的实际功能选用 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

<!-- 可选:位置相关功能;需要精确位置时再配置 FINE_LOCATION -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- 可选:发起 APK 安装、发送通知的场景 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<!-- 可选:确需查询全部应用,且符合分发渠道要求时选用 -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />

<!-- 可选:仅在使用穿山甲直播悬浮小窗等对应功能时选用 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
</manifest>

Manifest 声明、系统授权与 SDK 隐私开关需要分别配置。 MAC、BSSID、SSID、传感器等新增开关请在初始化前通过 DirichletAdCustomController 设置,详见下方「隐私采集开关」。开启开关不会自动取得系统权限,关闭开关也不会自动移除 Manifest 中的声明;这四个开关不对应四项需要新增的 Android 权限。

若最终清单包含不使用的权限,先确认其来自哪个 AAR,以及移除后对所接网络功能的影响,再按 Manifest 合并规则处理。仅删除宿主手写的声明,不能阻止依赖 AAR 再次合入该权限。

Android 代码混淆​

聚合 SDK 已内置 R8/ProGuard 混淆规则(consumerProguardFiles),默认情况下无需额外添加 -keep / -dontwarn 配置。 若工程使用自定义混淆策略导致类被误删/误混淆,请根据构建或运行日志按需补充规则。

资源混淆配置​

如果您的工程中接入了资源混淆插件 AndResGuard,需要在 build.gradle 中新增白名单配置:

andResGuard {
// 白名单配置,请参考demo中的 `whiteList.txt`
whiteList = [
"R.integer.min_screen_width_bucket",
"R.style.DialogAnimationUp",
"R.style.DialogAnimationRight",
"R.style.DialogFullScreen",
"R.drawable.gdt_*"
...
]
}

获取广告权限​

以下兼容接口仅检查并申请尚未授予的 READ_PHONE_STATE 和 ACCESS_COARSE_LOCATION,不会根据所接广告网络或 DirichletAdCustomController 开关自动筛选权限,也不申请通知、精确位置等其他权限。

仅在业务确需这两项权限时考虑使用;若只需其中一项,或需要其他权限,应由宿主按功能自行申请并处理用户拒绝的情况。不要把调用此接口或获得设备状态、位置权限作为所有用户初始化 SDK 的前提;当前自研 ADN 不采集或转发 IMEI。

DirichletAdManager.get().requestPermissionIfNecessary(activity);

初始化​

通过 withCustomController() 配置隐私控制器。示例中的 enableGetLocation、enableGetMacAddress 等布尔变量由宿主根据用户授权维护,请在调用 DirichletSdk.init() 前设置。新增开关的默认值和约束见下方「隐私采集开关」。

DirichletAdConfig config = new DirichletAdConfig.Builder()
.withMediaId(your_media_id) // 必选参数。为 Dirichlet ADN 注册的媒体 ID
.withMediaName(your_media_name) // 必选参数。为 Dirichlet ADN 注册的媒体名称
.withMediaKey(your_media_key) // 必选参数。媒体密钥,可以在 Dirichlet ADN 后台查看
.enableDebug(false) // 可选参数,是否打开 debug 调试信息输出:true 打开、false 关闭。默认 false 关闭
.withCustomController(new DirichletAdCustomController() {

// 是否允许 SDK 主动使用地理位置信息
@Override
public boolean isCanUseLocation() {
return enableGetLocation;
}

// 手动提供的位置也受位置授权控制,关闭位置授权时不会使用
@Override
public DirichletAdLocation getDirichletAdLocation() {
return new DirichletAdLocation(longitude, latitude, accuracy);
}

// 是否允许 SDK 主动使用手机硬件参数,如 imei
@Override
public boolean isCanUsePhoneState() {
return enableGetPhoneState;
}

// 兼容接口:关闭设备状态授权时不向三方转发;内置 ADN 不采集或转发 IMEI
@Override
public String getDevImei() {
return imei;
}

// 是否允许 SDK 主动使用 ACCESS_WIFI_STATE 权限
@Override
public boolean isCanUseWifiState() {
return enableGetWifiState;
}

// 5.3.0.0 新增:是否允许MAC 地址访问(三方需提供对应控制能力)
@Override
public boolean isCanUsePhoneMacAddress() {
return enableGetMacAddress;
}

// 5.3.0.0 新增:是否允许BSSID 采集,同时受 Wi-Fi 总开关控制
@Override
public boolean isCanUseBssid() {
return enableGetBssid;
}

// 5.3.0.0 新增:是否允许SSID 采集,与 BSSID 独立配置,同样受 Wi-Fi 总开关约束
@Override
public boolean isCanUseSsid() {
return enableGetSsid;
}

// 5.3.0.0 新增:是否允许传感器访问,与摇一摇交互开关独立
@Override
public boolean isCanUseSensor() {
return enableUseSensor;
}

// 是否允许 SDK 主动使用 WRITE_EXTERNAL_STORAGE 权限
@Override
public boolean isCanUseWriteExternal() {
return enableWriteExternal;
}

// 5.3.0.4 新增:独立 OAID 开关,默认 true,与 Android ID 开关独立
@Override
public boolean isCanUseOaid() {
return true; // 已取得必要授权时建议保持开启;用户拒绝或撤回授权时返回 false
}

// 开发者可以传入 oaid,同样受 OAID 开关控制
// 信通院 OAID 的相关采集——如何获取 OAID:
// 1. 移动安全联盟官网 http://www.msa-alliance.cn/
// 2. 信通院统一 SDK 下载 http://msa-alliance.cn/col.jsp?id=120
@Override
public String getDevOaid() {
return oaid;
}

// 是否允许 SDK 主动获取设备上应用安装列表的采集权限
@Override
public boolean alist() {
return enableGetAppList;
}

// 是否允许 SDK 主动获取 ANDROID_ID
@Override
public boolean isCanUseAndroidId() {
return enableGetAndroidId;
}
})
.build();

// context 为 application 的上下文
DirichletSdk.init(context, config, new DirichletSdk.InitListener() {
@Override
public void onInitSuccess() {
Log.d("DirichletSdk", "初始化成功");
}

@Override
public void onInitFail(int code, String msg) {
Log.d("DirichletSdk", "初始化失败: " + code + ", " + msg);
}
});

OAID 隐私开关​

Android SDK 5.3.0.4 及以上版本支持独立的 OAID 开关 isCanUseOaid(),默认返回 true。建议在取得用户必要授权后保持开启,关闭或未获取到 OAID 可能影响广告投放效果。

SDK 可自动获取 OAID,开发者无需手动传入,getDevOaid() 可保留默认 null。将 isCanUseOaid() 设为 false 后,将关闭 TapADN 自身的 OAID 获取、使用及向适配器提供 OAID,包括缓存值和手动传入值。

隐私采集开关​

以下为 Android SDK 5.3.0.4 中 DirichletAdCustomController 的全部 11 个布尔开关,默认均返回 true。请在初始化前根据用户授权覆盖对应方法,需要关闭时返回 false。返回 true 不代表已取得用户授权或 Android 系统权限,也不代表 SDK 会采集全部对应信息。

版本说明中的“既有接口”表示在 5.3.0.0 之前已提供,不表示其最早引入版本;5.3.0.0 新增四项开关,5.3.0.4 新增独立 OAID 开关。

下表描述聚合控制器的配置入口;自研 ADN 按用户授权及隐私开关配置执行,第三方 SDK 的实际控制范围取决于其公开接口和适配层映射,不能据此保证所有广告网络停止相同采集。具体限制见下方广告网络说明。

方法默认值版本说明控制范围
isCanUseLocation()true既有接口位置访问。
isCanUsePhoneState()true既有接口电话状态访问控制,包含自研 ADN 的设备序列号(Serial)读取及缓存值使用;接口存在不代表自研 ADN 主动采集 IMEI。
isCanUseWifiState()true既有接口Wi-Fi 状态访问;关闭后自研 BSSID、SSID 采集也受限。
isCanUseWriteExternal()true既有接口是否允许主动使用 WRITE_EXTERNAL_STORAGE;不等于授予系统存储权限。
alist()true既有接口应用安装列表访问。
isCanUseAndroidId()true既有接口Android ID 访问;与 OAID 开关独立。
isCanUsePhoneMacAddress()true5.3.0.0 新增MAC 地址访问兼容控制,不新增自研 ADN 主动 MAC 采集。
isCanUseBssid()true5.3.0.0 新增BSSID 采集,与 SSID 分别配置,同时受 Wi-Fi 总开关控制。
isCanUseSsid()true5.3.0.0 新增SSID 采集,与 BSSID 分别配置,同时受 Wi-Fi 总开关控制。
isCanUseSensor()true5.3.0.0 新增传感器访问;与 shakeEnabled 摇一摇交互开关独立。
isCanUseOaid()true5.3.0.4 新增自研 OAID 获取、使用及向适配器提供 OAID,包含缓存值和开发者提供值。

设备序列号(Serial)控制​

Android SDK 5.3.0.4 中,自研 ADN 在广告请求阶段通过 Android 系统接口读取设备序列号(Serial),作为广告请求中的设备标识符,并在本地缓存。该行为受既有的 isCanUsePhoneState() 开关及系统权限共同限制。

  • 请在初始化前按用户授权配置 isCanUsePhoneState();返回 false 时,自研 ADN 不读取 Serial,也不使用缓存的 Serial。此开关还控制其他电话状态访问,不能仅用于单独关闭 Serial。
  • Android 10 以下需通过 READ_PHONE_STATE 权限检查;Android 10 及以上需通过系统特权权限检查,普通应用仅获得 READ_PHONE_STATE 不能读取 Serial。SDK 不会为此主动申请额外权限,接入方无需新增特权权限声明。
  • 初始化阶段不主动读取系统 Serial;缓存值的使用同样受上述开关和系统权限限制。请在 App 隐私政策中按实际接入情况披露 Serial 的采集目的、方式和场景,参见隐私政策。

在初始化配置的 .withCustomController(...) 中覆盖该方法即可关闭;请保留控制器中已配置的其他隐私方法:

.withCustomController(new DirichletAdCustomController() {
@Override
public boolean isCanUsePhoneState() {
return false;
}
})

上述 Serial 行为说明仅针对自研 ADN;第三方广告 SDK 的实际控制范围取决于其公开接口及适配层映射,不能据此保证所有广告网络停止 Serial 采集。

手动传值接口​

以下接口默认返回 null,用于开发者主动提供数据,不是采集开关。仅在取得相应授权且确有需要时提供。

方法说明
getDirichletAdLocation()手动提供位置;自研 ADN 仅在位置授权允许时使用,不用于初始化采集。
getDevOaid()手动提供 OAID,同样受 isCanUseOaid() 控制;返回 null 不等于关闭 OAID 获取。
getDevImei()聚合兼容接口;电话状态访问关闭时不读取开发者提供的值。向第三方的传递取决于适配层,自研 ADN 不采集或转发该值。
provideCustomUser()提供 CustomUser 用户属性对象;不是隐私总开关或个性化广告开关。不提供时保留默认 null。

控制限制​

请在初始化 SDK 前完成配置。以下是当前适配版本的支持范围,不能将统一控制器的方法名理解为所有广告网络都具备相同控制能力。

广告网络开关映射与限制
自研 ADN根据宿主隐私开关控制信息采集;未新增主动 IMEI / MAC 采集。
穿山甲 7.8.1.5传感器开关映射到 motion_info,与摇一摇交互配置独立;位置及 Wi-Fi 使用下述组合条件,无法独立控制 BSSID / SSID。
优量汇 4.701.1571sensor_ts、shakable 同时受传感器开关和摇一摇开关约束,仅限制摇一摇相关能力,不是全部传感器采集的总开关;当前适配接口不提供独立 MAC / BSSID / SSID 控制。
百青藤 9.46.0位置使用下述组合条件,并保留适配层对广义设备信息等能力的关闭配置;缺少 MAC / BSSID / SSID / 传感器等独立控制接口,不能据此保证各类信息均停止采集。
爱奇艺 2.3.102.110支持 MAC 与 SSID 控制;当前公开接口没有传感器、BSSID 或主动位置采集开关,关闭 SSID 不等于关闭 BSSID。

穿山甲与百青藤的位置联动: 适配层使用「位置允许 AND Wi-Fi 允许 AND BSSID 允许 AND SSID 允许 AND MAC 允许」的组合结果控制两者的位置接口;穿山甲的 Wi-Fi 接口及手动位置回调也使用该结果。因此,即使 isCanUseLocation() 返回 true,关闭 MAC、BSSID、SSID 或 Wi-Fi 中任一项,也会关闭这两家对应的位置能力。例如,关闭 isCanUseBssid() 后,不能预期穿山甲仍使用手动传入的位置。

三方内部的采集行为以其实际支持的接口为边界,不能仅凭开关设置或编译通过推断已完全停止采集。接入方有严格限制要求时,应结合对应三方的确认与实机验证判断。隐私配置应在初始化前确定,不承诺所有三方支持初始化后的即时变更;宿主撤回授权也不能清除三方此前已缓存的信息。

  • BSSID 与 SSID 可分别控制,但均不能绕过 isCanUseWifiState() == false。
  • 传感器授权与 shakeEnabled 摇一摇交互开关独立;关闭交互不等于关闭传感器授权。
  • 自研 ADN 初始化阶段不主动获取位置、BSSID、SSID、应用安装列表或传感器信息;广告请求阶段仍需遵循用户授权及隐私开关配置。
  • 手动提供的位置也受位置授权限制,不能通过回调绕过关闭状态。上述 ADN 采集行为不代表三方 SDK 的全部采集行为。

广告个性化设置​

建议在 App 的广告设置或隐私设置中提供「个性化广告」开关,并向用户说明:

  • 开启后,SDK 会根据用户的使用情况展示更相关的广告。
  • 关闭后,广告数量不变,用户仍会看到广告,但广告相关度可能降低。

当用户修改开关时,调用以下接口同步到 SDK。App 需要自行保存用户选择,并在下次启动后重新设置一次。

import com.tapsdk.tapad.group.DirichletSdk;
import com.tapsdk.tapad.group.constants.Constants;

public void setPersonalizedAdEnabled(boolean enabled) {
DirichletSdk.putMediaGlobalSettings(
Constants.Personalization.PERSONAL_ADS_TYPE,
enabled
? Constants.Personalization.PERSONAL_ADS_TYPE_ALLOW
: Constants.Personalization.PERSONAL_ADS_TYPE_LIMIT
);
}

广告通用接口​

创建广告加载器​

// 注意,一个 Activity 中只需要创建一个 DirichletAdNative 对象
DirichletAdNative adNative = DirichletAdManager.get().createAdNative(context);

广告请求参数​

DirichletAdRequest request = new DirichletAdRequest.Builder()
.withSpaceId(spaceId) // 必填,广告位 id
.withUserId(user_id) // 选填,用户 ID(用于激励广告奖励验证)
.withExtra1(extra_info) // 选填,激励广告验证奖励时透传参数
.withRewardName(reward_name) // 选填,奖励的名称(激励广告)
.withRewardAmount(100) // 选填,奖励的数量(激励广告)
.withExpressViewAcceptedSize(width, height) // 选填,广告视图尺寸(开屏、Banner)
.withExpressImageAcceptedSize(width, height) // 选填,视频视图尺寸(信息流)
.build();

广告对象通用方法​

// 所有广告对象都包含以下方法
class Dirichlet**Ad {
// 销毁广告
public void destroy();

// 广告是否有效
boolean isValid();

// 获取广告额外参数,例如广告唯一 track_id、价格等
Map<String, Object> getDirichletExtraInfo();
}

广告类型​

激励视频广告​

激励视频是一种全屏播放的视频广告,用户可以在观看完整的视频后获取奖励。

自动加载展示(推荐)​

4.2.0.1 新增

showRewardVideoAutoAd 接口将加载和展示合并为一次调用,内部自动管理广告缓存。若有可用缓存则立即展示,否则自动加载后展示。

DirichletAdRequest request = new DirichletAdRequest.Builder()
.withSpaceId(YOUR_SPACE_ID)
.withUserId("user_123")
.withRewardName("金币")
.withRewardAmount(100)
.build();

adNative.showRewardVideoAutoAd(request, new DirichletAdNative.RewardVideoAutoAdListener() {
@Override
public void onError(int code, String message) {
Log.e("DirichletAd", "激励视频加载/展示失败: " + code + ", " + message);
}

@Override
public void onAdShow() {
Log.d("DirichletAd", "激励视频展示");
}

@Override
public void onAdClose() {
Log.d("DirichletAd", "激励视频关闭");
}

@Override
public void onRewardVerify(boolean rewardVerify, int rewardAmount, String rewardName, int code, String msg) {
if (rewardVerify) {
Log.d("DirichletAd", "获得奖励: " + rewardAmount + " " + rewardName);
}
}

@Override
public void onAdClick() {
Log.d("DirichletAd", "激励视频点击");
}
});

手动加载展示​

获取广告​

// 构建广告请求
DirichletAdRequest request = new DirichletAdRequest.Builder()
.withSpaceId(YOUR_SPACE_ID) // 广告位 ID
.withUserId(user_123) // 用户 ID(用于奖励验证)
.withExtra1(extra_data) // 额外参数
.withRewardName("金币") // 奖励名称
.withRewardAmount(100) // 奖励数量
.build();

// 加载激励视频广告
adNative.loadRewardVideoAd(request, new DirichletAdNative.RewardVideoAdListener() {
@Override
public void onRewardVideoAdLoad(DirichletRewardVideoAd rewardVideoAd) {
// 广告加载成功
Log.d("DirichletAd", "激励视频广告加载成功");

// 可以展示广告
showRewardVideoAd(rewardVideoAd);
}

@Override
public void onError(int code, String message) {
// 广告加载失败
Log.e("DirichletAd", "激励视频广告加载失败: " + code + ", " + message);
}
});

播放​

private void showRewardVideoAd(DirichletRewardVideoAd rewardVideoAd) {
if (rewardVideoAd != null && rewardVideoAd.isValid()) {
// 设置交互回调 listener,曝光、点击、关闭、奖励等回调事件
rewardVideoAd.setRewardAdInteractionListener(new DirichletRewardVideoAd.RewardAdInteractionListener() {
@Override
public void onAdShow() {
Log.d("DirichletAd", "激励视频广告曝光展示");
}

@Override
public void onAdClick() {
Log.d("DirichletAd", "激励视频广告点击");
}

@Override
public void onAdClose() {
Log.d("DirichletAd", "激励视频广告关闭");
}

@Override
public void onRewardVerify(boolean rewardVerify, int rewardAmount, String rewardName, int code, String msg) {
if (rewardVerify) {
Log.d("DirichletAd", "获得奖励: " + rewardAmount + ", " + rewardName);
// 发放奖励给用户
} else {
Log.d("DirichletAd", "奖励验证失败: " + code + ", " + msg);
}
}
});

// 展示激励广告
rewardVideoAd.showRewardVideoAd(activity);
}
}

Banner 广告为固定尺寸的横幅广告,可以放置在页面的顶部或底部。

自动加载展示(推荐)​

4.2.2.0 新增

showBannerAutoAd 接口将加载和展示合并为一次调用,内部自动管理广告缓存与轮播。开发者只需提供广告容器,SDK 自动完成加载、展示和定时刷新。

ViewGroup bannerContainer = findViewById(R.id.banner_container);

DirichletAdRequest request = new DirichletAdRequest.Builder()
.withSpaceId(YOUR_BANNER_SPACE_ID)
.withSlideInternal(30) // 轮播间隔(秒),取值范围 30~120,默认 30
.build();

adNative.showBannerAutoAd(request, bannerContainer, new DirichletAdNative.BannerAutoAdListener() {
@Override
public void onError(int code, String message) {
Log.e("DirichletAd", "Banner 自动加载失败: " + code + ", " + message);
}

@Override
public void onAdShow() {
Log.d("DirichletAd", "Banner 展示");
}

@Override
public void onAdClick() {
Log.d("DirichletAd", "Banner 点击");
}

@Override
public void onAdClose() {
Log.d("DirichletAd", "Banner 关闭");
}
});

手动加载展示​

获取广告​

DirichletAdRequest request = new DirichletAdRequest.Builder()
.withSpaceId(YOUR_BANNER_SPACE_ID)
.withExpressViewAcceptedSize(width, height)
.build();

adNative.loadBannerAd(request, new DirichletAdNative.BannerAdListener() {
@Override
public void onBannerAdLoad(DirichletBannerAd bannerAd) {
// Banner 广告加载成功
Log.d("DirichletAd", "Banner 广告加载成功");
showBannerAd(bannerAd);
}

@Override
public void onError(int code, String message) {
Log.e("DirichletAd", "Banner 广告加载失败: " + code + ", " + message);
}
});

播放​

private void showBannerAd(DirichletBannerAd bannerAd) {
if (bannerAd != null) {
// 获取 Banner 视图
View bannerView = bannerAd.getBannerView();
if (bannerView != null) {
// 添加到容器中
ViewGroup container = findViewById(R.id.banner_container);
container.removeAllViews();
container.addView(bannerView);

// 设置交互监听
bannerAd.setBannerInteractionListener(new DirichletBannerAd.BannerInteractionListener() {
@Override
public void onAdShow() {
Log.d("DirichletAd", "Banner 广告展示");
}

@Override
public void onAdClick() {
Log.d("DirichletAd", "Banner 广告点击");
}

@Override
public void onAdClose() {
Log.d("DirichletAd", "Banner 广告关闭");
}
});
}
}
}

开屏广告​

开屏广告为用户在进入 App 时展示的全屏广告。

信息

开屏广告的展示和加载必须在同一个 Activity 中。

竖屏开屏广告 view 要求 width = 屏幕宽,height 需要 >= 75% 屏幕高,否则会影响计费。

横屏开屏广告 view 要求 width = 屏幕宽,height 需要 = 屏幕高,否则会影响计费。

自动加载展示(推荐)​

4.2.2.0 新增

showSplashAutoAd 接口将加载和展示合并为一次调用,内部自动管理广告缓存。若有可用缓存则立即展示,否则自动加载后展示。

ViewGroup splashContainer = findViewById(R.id.splash_container);

DirichletAdRequest request = new DirichletAdRequest.Builder()
.withSpaceId(YOUR_SPLASH_SPACE_ID)
.withExpressViewAcceptedSize(screenWidth, screenHeight)
.build();

adNative.showSplashAutoAd(request, splashContainer, new DirichletAdNative.SplashAutoAdListener() {
@Override
public void onError(int code, String message) {
Log.e("DirichletAd", "开屏自动加载失败: " + code + ", " + message);
goToMainActivity();
}

@Override
public void onAdShow() {
Log.d("DirichletAd", "开屏展示");
}

@Override
public void onAdClick() {
Log.d("DirichletAd", "开屏点击");
}

@Override
public void onAdClose() {
Log.d("DirichletAd", "开屏关闭");
goToMainActivity();
}
});

手动加载展示​

获取广告​

DirichletAdRequest request = new DirichletAdRequest.Builder()
.withSpaceId(YOUR_SPLASH_SPACE_ID)
.withExpressViewAcceptedSize(screenWidth, screenHeight) // 设置开屏尺寸
.build();

adNative.loadSplashAd(request, new DirichletAdNative.SplashAdListener() {
@Override
public void onSplashAdLoad(DirichletSplashAd splashAd) {
Log.d("DirichletAd", "开屏广告加载成功");
showSplashAd(splashAd);
}

@Override
public void onError(int code, String message) {
Log.e("DirichletAd", "开屏广告加载失败: " + code + ", " + message);
// 跳转到主页面
goToMainActivity();
}
});

播放​

private void showSplashAd(DirichletSplashAd splashAd) {
if (splashAd != null) {
splashAd.setSplashInteractionListener(new DirichletSplashAd.AdInteractionListener() {
@Override
public void onAdShow() {
Log.d("DirichletAd", "开屏广告展示");
}

@Override
public void onAdClick() {
Log.d("DirichletAd", "开屏广告点击");
}

@Override
public void onAdClose() {
Log.d("DirichletAd", "开屏广告关闭");
goToMainActivity();
}
});

ViewGroup splashContainer = findViewById(R.id.splash_container);
splashAd.show(splashContainer);
}
}
点击展开 Kotlin 完整示例代码
class SplashActivity: AppCompatActivity() {

private lateinit var splashModel: SplashViewModel
private lateinit var binding: ActivitySplashBinding
var isPortrait: Boolean = true

override fun onCreate(savedInstanceState: Bundle?) {
supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
super.onCreate(savedInstanceState)
binding = ActivitySplashBinding.inflate(layoutInflater)
setContentView(binding.root)

splashModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory())[SplashViewModel::class.java]
binding.splashModel = splashModel
binding.lifecycleOwner = this

binding.portraitSplashBtn.setOnClickListener {
loadAd(splashModel.portraitSplashId.value, true)
}

binding.landscapeSplashBtn.setOnClickListener {
loadAd(splashModel.landscapeSplashId.value, false)
}

binding.splashShowBtn.setOnClickListener {
splashModel.log.value = ""
ad?.let {
binding.splashContainer.visibility = View.VISIBLE
binding.splashLoadContainer.visibility = View.GONE
binding.splashContainer.layoutParams = FrameLayout.LayoutParams(width, height)
it.show(binding.splashContainer)
}
}

tapAdNative = DirichletAdManager.get().createAdNative(this)
}

var ad: DirichletSplashAd? = null
var width = LayoutParams.MATCH_PARENT
var height = LayoutParams.MATCH_PARENT

private lateinit var tapAdNative: DirichletAdNative

private fun loadAd(spaceId: String?, isPortrait: Boolean) {
width = UIUtils.getScreenWidthInPx(this)
height = UIUtils.getScreenHeightInPx(this)
if (isPortrait) {
height = width / 9 * 16
}
splashModel.log.value = ""
tapAdNative.loadSplashAd(
DirichletAdRequest.Builder()
.withSpaceId(spaceId?.toLong() ?: 0)
.withExpressViewAcceptedSize(width, height)
.build(), object: DirichletAdNative.SplashAdListener {
override fun onError(code: Int, message: String?) {
splashModel.log.postValue("加载失败,$code, $message")
}

override fun onSplashAdLoad(splashAd: DirichletSplashAd?) {
splashModel.log.postValue("加载成功")
this@SplashActivity.isPortrait = isPortrait
ad = splashAd
ad?.setSplashInteractionListener(object: DirichletSplashAd.AdInteractionListener {
override fun onAdClose() {
binding.splashContainer.removeAllViews()
binding.splashContainer.visibility = View.GONE
binding.splashLoadContainer.visibility = View.VISIBLE
}

override fun onAdClick() {
Toast.makeText(this@SplashActivity, "开屏广告点击", Toast.LENGTH_SHORT).show()
}

override fun onAdShow() {
}
})
}
})
}

/**
* 清理当前广告和相关引用
*/
private fun cleanupCurrentAd() {
ad?.destroy()
}

override fun onDestroy() {
super.onDestroy()
// 最终清理,确保所有引用都被释放
cleanupCurrentAd()
}
}

插屏广告​

插屏广告支持「全屏视频广告」、「全屏图文广告」和「半屏图文广告」等形式。

提示

插屏广告分横竖屏形式,强烈建议在后台配置后正确使用横屏或竖屏广告,若用错配置的 orientation,可能会出现 UI 兼容异常问题。

自动加载展示(推荐)​

4.2.2.0 新增

showInterstitialAutoAd 接口将加载和展示合并为一次调用,内部自动管理广告缓存。若有可用缓存则立即展示,否则自动加载后展示。

DirichletAdRequest request = new DirichletAdRequest.Builder()
.withSpaceId(YOUR_INTERSTITIAL_SPACE_ID)
.build();

adNative.showInterstitialAutoAd(request, activity, new DirichletAdNative.InterstitialAutoAdListener() {
@Override
public void onError(int code, String message) {
Log.e("DirichletAd", "插屏自动加载失败: " + code + ", " + message);
}

@Override
public void onAdShow() {
Log.d("DirichletAd", "插屏展示");
}

@Override
public void onAdClick() {
Log.d("DirichletAd", "插屏点击");
}

@Override
public void onAdClose() {
Log.d("DirichletAd", "插屏关闭");
}
});

手动加载展示​

获取广告​

DirichletAdRequest request = new DirichletAdRequest.Builder()
.withSpaceId(YOUR_INTERSTITIAL_SPACE_ID)
.build();

adNative.loadInterstitialAd(request, new DirichletAdNative.InterstitialAdListener() {
@Override
public void onInterstitialAdLoad(DirichletInterstitialAd interstitialAd) {
Log.d("DirichletAd", "插屏广告加载成功");
showInterstitialAd(interstitialAd);
}

@Override
public void onError(int code, String message) {
Log.e("DirichletAd", "插屏广告加载失败: " + code + ", " + message);
}
});

播放​

private void showInterstitialAd(DirichletInterstitialAd interstitialAd) {
if (interstitialAd != null && interstitialAd.isValid()) {
// 设置交互 listener
interstitialAd.setInteractionListener(new DirichletInterstitialAd.InterstitialAdInteractionListener() {
@Override
public void onAdShow() {
Log.d("DirichletAd", "插屏广告展示");
}

@Override
public void onAdClick() {
Log.d("DirichletAd", "插屏广告点击");
}

@Override
public void onAdClose() {
Log.d("DirichletAd", "插屏广告关闭");
}
});
interstitialAd.show(activity);
}
}

自渲染信息流广告​

自渲染信息流广告为可自定义布局的信息流广告,包含大图和视频两种基本样式类型。

信息

支持的广告尺寸:

  • 大图(宽高比:1.78 的图片)
  • 视频(宽高比:1.78 的视频)

获取广告​

DirichletAdRequest request = new DirichletAdRequest.Builder()
.withSpaceId(YOUR_FEED_SPACE_ID)
.withExpressViewAcceptedSize(widthPx, heightPx) // 输入期待的父 view 的宽高
.withExpressImageAcceptedSize(widthPx, heightPx) // 输入期待的视频 view 的宽高
.build();

adNative.loadNativeFeedAd(request, new DirichletAdNative.NativeFeedAdListener() {
@Override
public void onNativeFeedAdLoad(List<DirichletNativeFeedAd> ads) {
Log.d("DirichletAd", "信息流广告加载成功,数量: " + ads.size());
for (DirichletNativeFeedAd ad : ads) {
bindNativeView(ad);
}
}

@Override
public void onError(int code, String message) {
Log.e("DirichletAd", "信息流广告加载失败: " + code + ", " + message);
}
});

广告行为监听 & 展示​

信息

AdInteractionListener 涉及到广告计费,必须正确调用,container 必须使用 DirichletAdNativeContainer。

备注

示例中的 loadImage 是占位方法,请使用应用已有的图片加载方案实现;SDK 不提供也不依赖 Glide。

private void bindNativeView(final DirichletNativeFeedAd ad) {
// 使用应用已有的图片加载方案加载广告图标
loadImage(binding.imgLogo, ad.getIconUrl());

// 设置广告标题和描述
binding.textTitle.setText(ad.getTitle());
binding.textDesc.setText(ad.getDescription());

// 添加视频 view
binding.videoContainer.removeAllViews();
View videoView = ad.getAdView();
ViewParent parent = videoView != null ? videoView.getParent() : null;
if (parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(videoView);
}
binding.videoContainer.addView(ad.getAdView());

// 使用应用已有的图片加载方案加载广告图片
loadImage(binding.imgPoster, ad.getImageInfoList().get(0).getImageUrl());

// 注册可点击的 View,click/show 会在内部完成
// container: 渲染广告最外层的 DirichletAdNativeContainer
// clickViews: 可点击的视图
// creativeViews: 可点击的创意视图(广告下载的按钮)
ad.registerViewForInteraction(
activity,
binding.nativeAdContainer,
Arrays.asList(binding.customContainer),
Arrays.asList(binding.btnDownload),
new DirichletNativeFeedAd.AdInteractionListener() {
@Override
public void onAdClick(View view, DirichletNativeFeedAd ad) {
Log.d("DirichletAd", "自渲染信息流点击");
}

@Override
public void onAdShow(DirichletNativeFeedAd ad) {
Log.d("DirichletAd", "自渲染信息流展示");
}
}
);

// 设置下载监听器
ad.setDownloadListener(new DirichletAppDownloadListener() {
@Override
public void onDownloadStart() {
Log.d("DirichletAd", "开始下载");
}

@Override
public void onDownloadComplete() {
Log.d("DirichletAd", "下载完成");
}

@Override
public void onUpdateDownloadProgress(int percent) {
Log.d("DirichletAd", "下载进度: " + percent);
}

@Override
public void onDownloadError() {
Log.d("DirichletAd", "下载错误");
}

@Override
public void onInstalled() {
Log.d("DirichletAd", "安装完成");
}
});

// 设置视频广告监听器
ad.setVideoAdListener(new DirichletNativeFeedAd.VideoAdListener() {
@Override
public void onVideoPrepared(DirichletNativeFeedAd ad) {
Log.d("DirichletAd", "视频准备完成");
}

@Override
public void onVideoStart(DirichletNativeFeedAd ad) {
Log.d("DirichletAd", "视频开始播放");
}

@Override
public void onVideoPause(DirichletNativeFeedAd ad) {
Log.d("DirichletAd", "视频停止播放");
}

@Override
public void onVideoError(DirichletNativeFeedAd ad, int errorCode, String message) {
Log.d("DirichletAd", "视频播放错误: " + errorCode + ", " + message);
}
});
}

自渲染信息流广告接口说明​

public interface DirichletNativeFeedAd {
/**
* 获取广告应用图标 url
*/
String getIconUrl();

/**
* 设置 logo 在广告容器中的位置
*/
void setLogoLayoutParams(FrameLayout.LayoutParams params);

/**
* 获取图片 ImageInfo list(宽、高、以及 url 地址)
*/
List<ImageInfo> getImageInfoList();

/**
* 广告标题
*/
String getTitle();

/**
* 广告描述
*/
String getDescription();

/**
* 获取下载类广告的下载应用大小
*/
String getApkSize();

/**
* @return 0: unknown
* 1: 下载类广告
* 2, 3: 跳转类广告
* 4: 小程序跳转类广告
*/
int getInteractionType();

/**
* @return 1: 大图
* 2: 视频
*/
int getImageMode();

/**
* 获取广告合规信息(六要素)
*/
ComplianceInfo getComplianceInfo();

/**
* 注册可点击的 view,click/show 会在内部完成
*
* @param activity
* @param container 广告容器(DirichletAdNativeContainer)
* @param clickViews 点击区域
* @param creativeViews 创意点击区域(下载按钮等)
* @param listener 广告交互监听回调
*/
void registerViewForInteraction(Activity activity, DirichletAdNativeContainer container,
List<View> clickViews, List<View> creativeViews, AdInteractionListener listener);

/**
* 获取广告视频的视频 view
* 注意:必须在 registerViewForInteraction 前调用
*/
View getAdView();

/**
* 设置下载监听器
*/
void setDownloadListener(DirichletAppDownloadListener listener);

/**
* 设置视频广告监听器
*/
void setVideoAdListener(VideoAdListener listener);
}

资源管理与最佳实践​

广告加载时机​

  • 激励视频:在用户可能需要奖励的场景前预加载
  • 插屏广告:在关卡结束、页面切换等自然停顿点展示
  • Banner 广告:在页面稳定后加载,避免影响用户体验
  • 开屏广告:在应用启动时立即加载

内存管理​

在 onDestroy 方法中释放广告资源:

@Override
protected void onDestroy() {
super.onDestroy();

// 释放广告对象
if (rewardVideoAd != null) {
rewardVideoAd.destroy();
rewardVideoAd = null;
}
}

常见问题​

错误码说明​

以下为 Android 聚合 SDK 5.3.0.4 中接入方可收到的常见错误。请同时记录回调场景、code 和完整 message,不要与 AD SDK 的错误码表混用。

回调场景错误码说明及处理建议
初始化失败-1初始化参数、执行状态或任务异常;根据消息检查 Application Context、配置及初始化调用
初始化失败-9501广告网络注册配置无效;检查 SDK 和适配器的配套版本
初始化失败-9502初始化运行环境不可用;保留消息并联系技术支持
初始化失败-9504广告网络初始化状态异常;检查依赖版本,保留消息并联系技术支持
初始化失败-9606初始化配置或组件不匹配;检查依赖版本及完整消息
加载失败-1加载失败;具体原因见错误消息
加载失败3006加载请求暂时无法处理;避免短时间重复请求,稍后重试
加载失败200001广告加载失败,包括请求、配置或加载流程异常;以详细消息为准
加载失败200002没有可用广告位或广告无填充;检查配置后稍后重试
自动加载 Banner 失败200003当前无可用广告;稍后重试
自动加载开屏失败104003展示容器为空;传入有效容器
模板信息流渲染失败200010广告渲染失败;检查视图及详细消息
自渲染信息流视频失败200011视频播放失败;检查网络及详细消息

初始化或广告网络返回的其他错误,以公开回调实际收到的值为准,并按对应接口的失败流程处理。聚合主错误码与第三方平台错误码不是同一套编号;错误消息中若包含第三方错误详情,请与聚合回调码一起记录。

联系技术支持时,请提供 SDK 及适配器版本、广告类型、广告位 ID、回调场景和完整错误消息。

服务端错误的返回方式​

Android 聚合 SDK 5.3.0.4 在各广告源均未成功加载时,公开加载失败回调返回 200002,消息包含“无填充广告”。这条汇总回调不附带单个广告源的服务端错误码,因此不能将它直接解释为某一种服务端原因。

排查时请提供聚合回调的完整 code、message、广告位 ID;若消息中包含请求标识,请一并提供。获得具体的 TapADN 服务端错误码后,可参考 AD 接入文档的服务端错误码说明;其中的数字不是聚合主回调码,第三方广告网络的同号错误也不适用。

广告不展示​

  • 检查广告位 ID 是否正确
  • 确认网络连接正常
  • 验证媒体配置信息(Media ID、Media Name、Media Key)
  • 查看日志中的错误信息
  • 确认是否正确初始化 SDK

第三方广告平台配置问题​

  • 检查 AndroidManifest.xml 中是否包含穿山甲和优量汇的必要配置
  • 确认已接入网络的 Provider 路径资源正常合并,例如 @xml/gdt_file_path 和 @xml/dirichlet_csj_file_paths
  • 检查资源混淆配置是否正确

广告加载失败​

  • 查看错误码和错误信息
  • 确认广告位是否配置正确
  • 检查是否有可用的广告填充
  • 仅在使用相关功能时,检查对应的系统授权和隐私开关;不要将未授予 IMEI 或位置权限直接判断为无广告的原因