ATFOpenAdSDK Android 接入指南(非聚合)
目录
- 集成说明
- 一、依赖配置
- 二、SDK 初始化
- 三、广告加载和展示
- 四、竞价回传
- 五、清理广告缓存
- 六、生命周期管理
- 七、错误处理
- 八、隐私数据获取说明
- 九、联调排查
- 十、注意事项
- 十一、技术支持
- 十二、SDK 涉及的三方库版本
集成说明
快速指南
⬇️ Demo 下载:https://github.com/atfoenginex/ATFOpenAndroidSdkDemo
简介
ATFOpenAdSDK 是一款广告 SDK,为开发者提供广告变现能力。
非聚合模式仅依赖 com.atfo.ad:core,ATFO 自有广告源开箱即用,无需任何三方 adapter 依赖;如需三方广告源或 GroMore 聚合,参见聚合接入指南。
接入提示
- 请确保您的应用已在各广告平台完成注册和审核
- 建议在测试环境中完成 SDK 集成和调试后再发布到生产环境
- 注意遵守各广告平台的规范和政策
注册开户
- 联系商务人员获取媒体账户
- 在管理后台创建应用,获取应用 ID
- 创建广告位,获取广告位 ID
- 获取安全密钥用于 SDK 初始化
联系对应的商务对接人员!
一、依赖配置
1、远程依赖方式引入
1、在项目的 settings.gradle.kts 中添加 Maven 仓库:
dependencyResolutionManagement {
repositories {
maven {
url = uri("https://maven.cxwlad.com/repository/maven-releases/")
}
google()
mavenCentral()
}
}
2、在项目的 app 下的 build.gradle.kts 中添加依赖:
最新版本号:2.9.5
dependencies {
// ATFO 广告核心组件
implementation("com.atfo.ad:core:2.9.5")
}
本地 aar 依赖方式引入(本地文件和接入方式,请联系 ATFO 侧)。
2、权限配置
在 AndroidManifest.xml 中添加必要的权限:
<!-- Network permissions -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- 可选权限(防作弊+优化广告投放) -->
<uses-permission android:name="android.permission.READ_BASIC_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
二、SDK 初始化
在 Application 中初始化
import android.app.Application
import android.util.Log
import com.atfo.ad.core.ATFOADSDKManager
import com.atfo.ad.core.ATFOSDKConfig
import com.atfo.ad.listener.ATFOAdInitListener
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// 初始化 SDK
initializeATFOSDK()
}
private fun initializeATFOSDK() {
val sdkConfig = ATFOSDKConfig.Builder()
.appId("your_app_id") // 应用 ID
.mediaId("your_mediaId") // 媒体 ID
.secret("your_secret") // 安全密钥
.appName("应用名称")
.environment(ATFOSDKConfig.Environment.PRODUCTION) // 环境配置
.debug(true) // 调试模式,开发阶段建议开启
.build()
ATFOADSDKManager.getInstance().init(this, sdkConfig, object : ATFOAdInitListener {
override fun onSuccess() {
Log.d("MyApplication", "✅ SDK初始化成功")
}
override fun onFail() {
Log.e("MyApplication", "SDK初始化失败")
}
})
}
}
ATFOSDKConfig.Builder 参数速查:
| 参数 | 说明 | 默认值 |
|---|---|---|
appId / mediaId / secret | 开户获取的三个必填凭证(build() 要求 appId 非空) | — |
appName | 应用名称 | — |
environment | PRODUCTION / STAGING | PRODUCTION |
debug | 调试日志开关 | false |
extra(key, value) | 透传参数 | 空 |
环境配置
SDK 支持多种环境配置:
// 生产环境
.environment(ATFOSDKConfig.Environment.PRODUCTION)
// 测试环境
.environment(ATFOSDKConfig.Environment.STAGING)
透传参数配置
// 设置透传参数(key 在前,value 在后)
.extra("cur_id", "android_mock_oaid_123")
三、广告加载和展示
加载统一走 ATFOAdLoader(context, isDebug),第二个参数是调试日志开关,与初始化的 debug 保持一致即可;回调接口为 ATFOAdLoader.AdLoadListener(onAdLoadSuccess / onAdLoadFailed)。
开屏广告
class SplashActivity : AppCompatActivity() {
private lateinit var adContainer: FrameLayout
private var splashAdObject: ATFOAd? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash)
adContainer = findViewById(R.id.ad_container)
// 加载开屏广告
loadSplashAd()
}
private fun loadSplashAd() {
val adSlot = ATFOAdSlot.Builder()
.adType(AdType.SPLASH)
.timeout(5000) // 可选;超时以管理后台下发为准,建议不配置(见「注意事项」)
.activity(this) // 当前 Activity
.slotId("") // 代码位 Id(向对接运营获取)
.size(1080, 1920)
.build()
ATFOAdLoader(this, true).loadAd(adSlot, object : ATFOAdLoader.AdLoadListener {
override fun onAdLoadSuccess(adObject: ATFOAd) {
// 广告加载成功
Log.d("SplashAd", "广告加载成功")
Log.d("SplashAd", "广告ID: ${adObject.adId}")
// 如获取价格方法不存在,请联系运营
Log.d("SplashAd", "价格: ${adObject.price}")
splashAdObject = adObject
// 显示广告
showSplashAd()
}
override fun onAdLoadFailed(error: ATFOAdError) {
// 广告加载失败
Log.e("SplashAd", "广告加载失败: ${error.message}")
goToMainActivity()
}
})
}
private fun showSplashAd() {
val adObject = splashAdObject ?: return
if (!adObject.isValid()) {
// 判断广告是否可用
return
}
adObject.showAd(adContainer, object : AdInteractionListener {
override fun onAdShowSuccess() {
Log.d("SplashAd", "开屏广告展示成功")
}
override fun onAdShowFailed(errorCode: Int, errorMessage: String) {
Log.e("SplashAd", "开屏广告展示失败: $errorMessage")
goToMainActivity()
}
override fun onAdClicked() {
Log.d("SplashAd", "开屏广告被点击")
goToMainActivity()
}
override fun onAdClosed() {
Log.d("SplashAd", "开屏广告被关闭")
goToMainActivity()
}
override fun onAdRenderFail(errorCode: Int, errorMessage: String) {
Log.d("SplashAd", "❌ 开屏广告renderFail")
}
override fun onAdExposed() {
Log.d("SplashAd", "开屏广告exposed")
}
})
}
override fun onDestroy() {
super.onDestroy()
splashAdObject?.destroy()
}
}
goToMainActivity()为示例中的业务跳转方法,需要按自己的页面结构实现。
信息流广告
class FeedAdActivity : AppCompatActivity() {
private lateinit var adContainer: FrameLayout
private var feedAdObject: ATFOAd? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_feed)
adContainer = findViewById(R.id.ad_container)
// 加载信息流广告
loadFeedAd()
}
private fun loadFeedAd() {
val adSlot = ATFOAdSlot.Builder()
.adType(AdType.FEED)
.timeout(8000) // 可选;超时以管理后台下发为准,建议不配置(见「注意事项」)
.slotId("") // 代码位 Id(向对接运营获取)
.activity(this)
.size(1080, 1080)
.extras(mapOf("scene" to "feed_list"))
.build()
ATFOAdLoader(this, true).loadAd(adSlot, object : ATFOAdLoader.AdLoadListener {
override fun onAdLoadSuccess(adObject: ATFOAd) {
Log.d("FeedAd", "信息流广告加载成功")
Log.d("FeedAd", "广告ID: ${adObject.adId}")
// 如获取价格方法不存在,请联系运营
Log.d("FeedAd", "价格: ${adObject.price}")
feedAdObject = adObject
// 显示广告
showFeedAd()
}
override fun onAdLoadFailed(error: ATFOAdError) {
Log.e("FeedAd", "信息流广告加载失败: ${error.message}")
}
})
}
private fun showFeedAd() {
val adObject = feedAdObject ?: return
if (!adObject.isValid()) {
// 判断广告是否可用
return
}
adObject.showAd(adContainer, object : AdInteractionListener {
override fun onAdShowSuccess() {
Log.d("FeedAd", "信息流广告展示成功")
}
override fun onAdShowFailed(errorCode: Int, errorMessage: String) {
Log.e("FeedAd", "信息流广告展示失败: $errorMessage")
}
override fun onAdClicked() {
Log.d("FeedAd", "信息流广告被点击")
}
override fun onAdClosed() {
Log.d("FeedAd", "信息流广告被关闭")
}
override fun onAdRenderFail(errorCode: Int, errorMessage: String) {
Log.d("FeedAd", "❌ 信息流广告renderFail")
}
override fun onAdExposed() {
Log.d("FeedAd", "信息流广告exposed")
}
})
}
override fun onDestroy() {
super.onDestroy()
feedAdObject?.destroy()
}
}
宿主自渲染(render_type=1)
SDK 通过 ATFOAd.nativeInfo 提供物料(标题、描述、图片、视频等),UI 由接入方自行构建,最后调用 ATFOAd.registerInteraction 绑定交互。曝光、点击、关闭的上报由 SDK 统一处理,接入方无需重复上报。
判断方式:ATFOAd.isNativeExpress() 为 false 即为自渲染广告,物料见 ATFOAd.nativeInfo;为 true 为三方模板广告,使用 ATFOAd.showAd 展示。
物料字段(ATFONativeFeedAdData):
| 字段 | 说明 |
|---|---|
title / description / source | 标题、描述、来源 |
icon / images | 图标与主图(ATFONativeImage,含 url / width / height) |
videoUrl / videoCover / videoDurationSec | 视频地址、封面、时长(秒);videoUrl 由宿主 VideoView 播放 |
landingUrl / deeplinkUrl / downloadUrl | 落地页、深链、下载地址 |
buttonText | CTA 按钮文案 |
appInfo | 应用下载类广告信息(名称、包名、隐私政策等) |
sdkVideoRender | true 表示视频由三方 SDK 的 MediaView 播放,必须使用 getAdView() |
adLogo | 广告合规角标(如百度/穿山甲),宿主 UI 需展示 |
辅助方法:primaryImageUrl() 返回第一张可用主图/封面/图标;hasRenderableMaterial() 判断物料是否足以渲染。
展示示例
private fun showSelfRenderFeed(ad: ATFOAd, adContainer: FrameLayout) {
// 模板广告(render_type=2/3)走 showAd;自渲染走本方法
if (ad.isNativeExpress()) {
ad.showAd(adContainer, interactionListener())
return
}
val info = ad.nativeInfo
if (info == null || !info.hasRenderableMaterial()) {
Log.e("FeedAd", "自渲染广告物料为空或不足")
return
}
// 1. 优先使用三方 SDK 提供的 View(如 GDT 自渲染视频 MediaView)
val sdkView = ad.getAdView()
if (sdkView != null) {
adContainer.addView(
sdkView,
FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
)
ad.registerInteraction(
container = adContainer,
clickableViews = listOf(sdkView),
closeViews = emptyList(),
interactionListener = interactionListener(),
adLogoParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
).apply {
gravity = Gravity.END or Gravity.TOP
leftMargin = dp(8f)
topMargin = dp(8f)
}
)
return
}
// 2. sdkVideoRender=true 必须依赖 getAdView()
if (info.sdkVideoRender) {
Log.e("FeedAd", "sdkVideoRender=true 但 getAdView() 为空,无法展示")
return
}
// 3. 宿主自行构建 UI(示例:左图右文;图片加载由接入方实现,如 Glide)
val root = LinearLayout(this).apply {
orientation = LinearLayout.HORIZONTAL
setPadding(dp(12f), dp(8f), dp(12f), dp(8f))
layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
val cover = ImageView(this).apply {
layoutParams = LinearLayout.LayoutParams(dp(110f), dp(110f))
info.primaryImageUrl()?.let { loadImage(it, this) } // 自行实现图片加载(如 Glide)
}
val textColumn = TextView(this).apply {
text = listOf(info.title, info.description)
.filter { !it.isNullOrBlank() }
.joinToString("\n")
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)
}
root.addView(cover)
root.addView(textColumn)
// 合规角标需展示:info.adLogo(registerInteraction 的 adLogoParams 控制其位置)
adContainer.addView(root)
// 4. 绑定交互:点击区域、关闭按钮;曝光/点击/关闭由 SDK 自动上报
ad.registerInteraction(
container = root,
clickableViews = listOf(root, cover, textColumn),
closeViews = emptyList(), // 如需关闭按钮:listOf(closeButton)
interactionListener = interactionListener(),
adLogoParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
).apply {
gravity = Gravity.END or Gravity.TOP
leftMargin = dp(8f)
topMargin = dp(8f)
}
)
}
// dp 换算由接入方实现,示例:
private fun dp(value: Float): Int =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, resources.displayMetrics).toInt()
// 交互监听器:与模板广告一致
private fun interactionListener(): AdInteractionListener = object : AdInteractionListener {
override fun onAdShowSuccess() {}
override fun onAdShowFailed(errorCode: Int, errorMessage: String) {}
override fun onAdClicked() {}
override fun onAdClosed() {}
override fun onAdRenderFail(errorCode: Int, errorMessage: String) {}
override fun onAdExposed() {}
}
注意事项
- 自渲染广告不要调用
showAd,否则物料无法展示; nativeInfo为空或hasRenderableMaterial()为false时按渲染失败处理;sdkVideoRender = true时视频由三方 SDK 渲染,必须使用getAdView(),不能自行用videoUrl播放;adLogo为广告合规角标,需要展示在广告视图上;- 容器需先加入视图树再调用
registerInteraction; - 广告对象使用完毕后调用
destroy()释放。
插屏广告
class InterstitialAdActivity : AppCompatActivity() {
private var interstitialAdObject: ATFOAd? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_interstitial)
// 加载插屏广告
loadInterstitialAd()
}
private fun loadInterstitialAd() {
val adSlot = ATFOAdSlot.Builder()
.adType(AdType.INTERSTITIAL)
.timeout(10000) // 可选;超时以管理后台下发为准,建议不配置(见「注意事项」)
.activity(this)
.slotId("") // 代码位 Id(向对接运营获取)
.size(1080, 1920)
.extras(mapOf("scene" to "interstitial"))
.build()
ATFOAdLoader(this, true).loadAd(adSlot, object : ATFOAdLoader.AdLoadListener {
override fun onAdLoadSuccess(adObject: ATFOAd) {
Log.d("InterstitialAd", "插屏广告加载成功")
Log.d("InterstitialAd", "广告ID: ${adObject.adId}")
// 如获取价格方法不存在,请联系运营
Log.d("InterstitialAd", "价格: ${adObject.price}")
interstitialAdObject = adObject
// 显示广告
showInterstitialAd()
}
override fun onAdLoadFailed(error: ATFOAdError) {
Log.e("InterstitialAd", "插屏广告加载失败: ${error.message}")
}
})
}
private fun showInterstitialAd() {
val adObject = interstitialAdObject ?: return
if (!adObject.isValid()) {
// 判断广告是否可用
return
}
adObject.showAd(this, object : AdInteractionListener {
override fun onAdShowSuccess() {
Log.d("InterstitialAd", "插屏广告展示成功")
}
override fun onAdShowFailed(errorCode: Int, errorMessage: String) {
Log.e("InterstitialAd", "插屏广告展示失败: $errorMessage")
}
override fun onAdClicked() {
Log.d("InterstitialAd", "插屏广告被点击")
}
override fun onAdClosed() {
Log.d("InterstitialAd", "插屏广告被关闭")
}
override fun onAdRenderFail(errorCode: Int, errorMessage: String) {
Log.d("InterstitialAd", "❌ 插屏广告renderFail")
}
override fun onAdExposed() {
Log.d("InterstitialAd", "插屏广告exposed")
}
})
}
override fun onDestroy() {
super.onDestroy()
interstitialAdObject?.destroy()
}
}
激励视频广告
class RewardAdActivity : AppCompatActivity() {
private var rewardAdObject: ATFOAd? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_reward)
// 加载激励视频广告
loadRewardAd()
}
private fun loadRewardAd() {
val adSlot = ATFOAdSlot.Builder()
.adType(AdType.REWARD)
.timeout(10000) // 可选;超时以管理后台下发为准,建议不配置(见「注意事项」)
.activity(this)
.slotId("") // 代码位 Id(向对接运营获取)
.size(720, 1280)
.extras(mapOf("scene" to "reward"))
.build()
ATFOAdLoader(this, true).loadAd(adSlot, object : ATFOAdLoader.AdLoadListener {
override fun onAdLoadSuccess(adObject: ATFOAd) {
Log.d("RewardAd", "激励视频广告加载成功")
Log.d("RewardAd", "广告ID: ${adObject.adId}")
Log.d("RewardAd", "广告类型: ${adObject.adType}")
// 如获取价格方法不存在,请联系运营
Log.d("RewardAd", "价格: ${adObject.price}")
rewardAdObject = adObject
// 根据业务时机展示广告
showRewardAd()
}
override fun onAdLoadFailed(error: ATFOAdError) {
Log.e("RewardAd", "激励视频广告加载失败: ${error.message}")
}
})
}
private fun showRewardAd() {
val adObject = rewardAdObject ?: return
if (!adObject.isValid()) {
// 判断广告是否可用
return
}
// 激励视频广告为全屏展示,无需传入广告容器
adObject.showAd(this, object : AdInteractionListener {
override fun onAdShowSuccess() {
Log.d("RewardAd", "激励视频广告展示成功")
}
override fun onAdShowFailed(errorCode: Int, errorMessage: String) {
Log.e("RewardAd", "激励视频广告展示失败: $errorMessage")
}
override fun onAdClicked() {
Log.d("RewardAd", "激励视频广告被点击")
}
override fun onAdClosed() {
Log.d("RewardAd", "激励视频广告被关闭")
}
override fun onAdRenderFail(errorCode: Int, errorMessage: String) {
Log.d("RewardAd", "❌ 激励视频广告renderFail")
}
override fun onAdExposed() {
Log.d("RewardAd", "激励视频广告exposed")
}
override fun onReward() {
Log.d("RewardAd", "激励视频广告获得奖励")
// 在这里处理业务发奖逻辑
}
})
}
override fun onDestroy() {
super.onDestroy()
rewardAdObject?.destroy()
}
}
通知广告
通知广告是 ATFOpenAdSDK 提供的一种特殊信息流广告:客户端按信息流广告(AdType.FEED)拉取,由 SDK 模板渲染并负责展示。
后台配置(两项缺一不可):
- 代码位的渲染方式选「ATFO 模板渲染」
- 模板选「信息流-通知广告」
如果需要使用非 ATFO 广告平台的代码位,需要在对应的平台后台创建代码位,选择 FEED 流广告类型,自渲染即可。
class NotifyAdActivity : AppCompatActivity() {
private lateinit var adContainer: FrameLayout
private var notifyAdObject: ATFOAd? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_notify_ad)
adContainer = findViewById(R.id.ad_container)
// 加载信息流广告
loadNotifyAd()
}
private fun loadNotifyAd() {
val adSlot = ATFOAdSlot.Builder()
.adType(AdType.FEED)
.timeout(8000) // 可选;超时以管理后台下发为准,建议不配置(见「注意事项」)
.slotId("") // 代码位 Id(向对接运营获取)
.activity(this)
.size(1000, 1000)
.extras(mapOf("scene" to "notify_ad"))
.build()
ATFOAdLoader(this, true).loadAd(adSlot, object : ATFOAdLoader.AdLoadListener {
override fun onAdLoadSuccess(adObject: ATFOAd) {
Log.d("NotifyAd", "通知广告加载成功")
Log.d("NotifyAd", "广告ID: ${adObject.adId}")
// 如获取价格方法不存在,请联系运营
Log.d("NotifyAd", "价格: ${adObject.price}")
notifyAdObject = adObject
// 显示广告
showNotifyAd()
}
override fun onAdLoadFailed(error: ATFOAdError) {
Log.e("NotifyAd", "通知广告加载失败: ${error.message}")
}
})
}
private fun showNotifyAd() {
val adObject = notifyAdObject ?: return
if (!adObject.isValid()) {
// 判断广告是否可用
return
}
adObject.showAd(adContainer, object : AdInteractionListener {
override fun onAdShowSuccess() {
Log.d("NotifyAd", "通知广告展示成功")
}
override fun onAdShowFailed(errorCode: Int, errorMessage: String) {
Log.e("NotifyAd", "通知广告展示失败: $errorMessage")
}
override fun onAdClicked() {
Log.d("NotifyAd", "通知广告被点击")
}
override fun onAdClosed() {
Log.d("NotifyAd", "通知广告被关闭")
}
override fun onAdRenderFail(errorCode: Int, errorMessage: String) {
Log.d("NotifyAd", "❌ 通知广告renderFail")
}
override fun onAdExposed() {
Log.d("NotifyAd", "通知广告exposed")
}
})
}
override fun onDestroy() {
super.onDestroy()
notifyAdObject?.destroy()
}
}
四、竞价回传
当媒体使用 GroMore、TopOn 等第三方聚合平台,且本 SDK 作为竞价渠道参与竞价时,需通过竞价回传功能向本 SDK 通知竞价结果。
找不到竞价回传方法请联系运营。
参与竞价(Bidding)的广告位,在竞价结束后需要把竞胜/竞败结果回传给 SDK,用于计费和效果归因。
1、竞胜回传
val winPrice = 100
val secondPrice = 20
val secondPriceWinner = AdSource.QM // 见广告渠道映射
val currency = "CNY" // 币种,默认 CNY
val mBidFeedback = BidFeedback.buildWinFeedback(
winPrice = winPrice,
secondPrice = secondPrice,
secondPriceWinner = secondPriceWinner,
currency = currency,
)
mATFOAd.sendWinNotification(mBidFeedback)
2、竞败回传
val winPrice = 100
val lossReason = BidFeedback.LOSS_REASON_LOW_PRICE
val winner = AdSource.QM // 见广告渠道映射
val currency = "CNY" // 币种,默认 CNY
val mBidFeedback = BidFeedback.buildLossFeedback(
winPrice = winPrice,
lossReason = lossReason,
winner = winner,
currency = currency,
)
adObject.sendLossNotification(mBidFeedback)
3、广告渠道映射
AdSource | 广告平台 |
|---|---|
GRM | gromore |
GDT | 广点通 |
BAIDU | 百度 |
KS | 快手 |
MS | 美数 |
YK | 优酷 |
QM | 趣盟 |
BZ | 倍孜 |
NOAH | 汇川 |
SIGMOB | sigmob |
WM | 旺脉 |
其他平台,使用字符串 OTHER
五、清理广告缓存
广告加载成功后会缓存在本地,需要主动清理时使用以下方法(com.atfo.ad.core.ATFOADSDKManager)。清理方法自 2.10.0 起提供。
1、清空全部广告缓存
val clearedCount = ATFOADSDKManager.getInstance().clearAllCachedAds()
2、按广告位清理缓存
val clearedCount = ATFOADSDKManager.getInstance().clearCachedAds(slotId)
返回值均为本次清理的广告数量。
六、生命周期管理
广告对象销毁
在 Activity 或 Fragment 销毁时,必须调用广告对象的销毁方法:
override fun onDestroy() {
super.onDestroy()
// 销毁广告对象
adObject?.destroy()
}
取消正在加载的广告
如果需要取消正在加载的广告:
// 取消加载
loader.cancelLoad()
七、错误处理
错误码说明
| 错误码 | 常量 | 说明 |
|---|---|---|
| 1001 | ATFOAdError.ERROR_NETWORK | 网络错误,可以重试 |
| 1002 | ATFOAdError.ERROR_NO_AD | 无广告返回,显示默认内容 |
| 1003 | ATFOAdError.ERROR_TIMEOUT | 超时 |
| 1004 | ATFOAdError.ERROR_INVALID_SLOT | 无效广告位,检查配置 |
| 1005 | ATFOAdError.ERROR_SDK_NOT_INIT | SDK 未初始化,检查初始化流程 |
| 1006 | ATFOAdError.ERROR_LOAD_FAILED | 加载失败(同一 slotId 在途重复加载也会以此拒绝) |
| 1007 | ATFOAdError.ERROR_RENDER_FAILED | 渲染失败 |
| 1008 | ATFOAdError.ERROR_THIRD_PARTY | 三方广告源错误 |
| 1009 | ATFOAdError.ERROR_BIDDING_FAILED | 竞价失败 |
| 1010 | ATFOAdError.ERROR_CONFIG_FAILED | 配置获取失败 |
| 1011 | ATFOAdError.ERROR_INVALID_RENDER_TYPE | 渲染类型无效 |
处理示例:
override fun onAdLoadFailed(error: ATFOAdError) {
when (error.code) {
ATFOAdError.ERROR_NETWORK -> {
// 网络错误,可以重试
retryLoadAd()
}
ATFOAdError.ERROR_NO_AD -> {
// 无广告返回,显示默认内容
showDefaultContent()
}
ATFOAdError.ERROR_TIMEOUT -> {
// 超时,可以增加超时时间重试
loadAdWithLongerTimeout()
}
ATFOAdError.ERROR_INVALID_SLOT -> {
// 无效广告位,检查配置
Log.e("AdError", "广告位配置错误")
}
ATFOAdError.ERROR_SDK_NOT_INIT -> {
// SDK未初始化,检查初始化流程
Log.e("AdError", "SDK未初始化")
}
else -> {
Log.e("AdError", "未知错误: ${error.message}")
}
}
}
八、隐私数据获取说明
1、SDK 收集的信息
| 参数 | 说明 |
|---|---|
| 操作系统 | 设备的操作系统(如:Android/iOS) |
| 系统版本名 | 设备的系统版本名 |
| 系统版本号 | 设备的系统版本号 |
| 应用包名 | 当前应用的包名 |
| 应用版本名 | 当前应用版本名 |
| 设备品牌 | 移动设备的品牌名称 |
| 设备型号 | 移动设备的型号名称 |
| 分辨率 | 设备的屏幕分辨率 |
| 屏幕方向 | 设备的屏幕方向(如:1:竖屏,2:横屏) |
| 网络类型 | 网络类型(如:WiFi、3G、4G、5G),可在初始化的时候关闭 |
| 移动网络代码 | 移动设备网络代码 |
| 移动国家代码 | 移动设备国家代码 |
| 系统语言 | 系统语言(如:zh-CN) |
| 时区 | 时区(如:GMT+08:00) |
| User Agent | User Agent 信息 |
| AndroidId | 设备广告标识符 |
九、联调排查
为方便联调,SDK 内置了日志导出页面。遇到广告加载、展示异常等问题时,可先导出日志,发给我们研发同学分析定位。
打开日志导出页面的命令:
adb shell am start -n 你的App包名/com.atfo.ad.debug.ATFODebugActivity在页面中导出日志后,把日志文件发给对接的研发即可。
十、注意事项
- 生命周期管理:在 Activity 销毁时调用
adObject.destroy() - 网络权限:确保添加了网络权限
- 调试模式:开发阶段建议开启
debug(true) - 三方 SDK:非聚合模式仅含 ATFO 自有广告源;如需三方广告源或 GroMore 聚合,参见聚合接入指南
- 广告显示:广告加载成功后需要调用
showAd()方法显示广告 - 错误处理:实现完善的错误处理和重试机制
- 内存管理:及时销毁广告对象,避免内存泄漏
- 超时管理:超时时间以管理后台下发为准(未下发时默认 5000ms),建议不在本地通过
timeout()配置
十一、技术支持
- 技术支持:support@atfo.com
- 版本:2.9.5
- 最低支持:Android API 23(Android 6.0)
十二、SDK 涉及的三方库版本(若存在不兼容等问题,请联系开发进行定制化修改)
[versions]
# 核心版本
agp = "7.0.4"
kotlin = "1.5.21"
# AndroidX
coreKtx = "1.3.2"
appcompat = "1.2.0"
constraintlayout = "2.1.4"
# 网络和媒体
glide = "4.11.0"
okhttp = "4.9.0"
gson = "2.8.6"
# 协程和工具
coroutines = "1.5.2"
mmkv = "1.2.14"
# Architecture components
room = "2.3.0"
[libraries]
# AndroidX核心
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
androidx-cardview = { group = "androidx.cardview", name = "cardview", version = "1.0.0" }
# 协程
coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
# 媒体
glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" }
# 网络(SDK 仅直接使用 OkHttp;接入方需引入 okhttp)
okhttp-core = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
# 存储
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
mmkv = { group = "com.tencent", name = "mmkv-static", version.ref = "mmkv" }
# 数据库和后台
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }