1. 穿山甲广告SDK接入概述
穿山甲广告SDK是字节跳动旗下的一款移动广告变现解决方案,主要面向Android和iOS平台的移动应用开发者。作为国内领先的广告聚合平台,穿山甲整合了包括今日头条、抖音等在内的海量广告资源,能够为开发者提供开屏、信息流、激励视频等多种广告形式的变现能力。
在实际项目中接入穿山甲SDK,开发者需要重点关注以下几个核心环节:
- SDK版本选择与更新策略
- 基础环境配置与初始化流程
- 各广告类型的集成实现
- 合规性检查与隐私政策适配
- 性能优化与异常处理
2. 环境准备与基础配置
2.1 开发环境要求
在开始接入前,请确保开发环境满足以下要求:
- Android Studio 4.0或更高版本
- 项目编译版本compileSdkVersion ≥ 31
- 最低支持版本minSdkVersion ≥ 21(建议≥24以获得完整功能支持)
- Gradle构建工具7.0+
- Java 8或Kotlin环境
2.2 SDK获取与引入
最新版穿山甲SDK可以通过以下方式获取:
- 登录穿山甲开发者平台(https://www.csjplatform.com/)
- 进入"文档中心" → "SDK下载"页面
- 选择对应版本的SDK包下载
推荐使用Gradle依赖方式集成:
dependencies { implementation 'com.bytedance.sdk:openadsdk:7.6.1.2' // 如果需要支持armeabi-v7a架构 implementation 'com.bytedance.sdk:openadsdk-armeabi-v7a:7.6.1.2' }2.3 权限与组件配置
在AndroidManifest.xml中添加必要权限和组件声明:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 可选权限,根据广告类型需求添加 --> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application> <!-- 穿山甲SDK必要组件 --> <meta-data android:name="csj_provider_authority" android:value="${applicationId}.csj.fileprovider" /> <provider android:name="com.bytedance.sdk.openadsdk.TTFileProvider" android:authorities="${applicationId}.csj.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/csj_file_paths" /> </provider> </application>3. SDK初始化与基础配置
3.1 异步初始化最佳实践
穿山甲SDK从3.4.5.1版本开始强制要求使用异步初始化方式,建议在主线程的Application.onCreate()中执行:
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); TTAdConfig config = new TTAdConfig.Builder() .appId("你的应用ID") // 必填 .appName("应用名称") // 必填 .useTextureView(true) // 使用TextureView播放视频 .allowShowNotify(true) // 是否允许显示通知 .debug(BuildConfig.DEBUG) // 调试模式 .supportMultiProcess(false) // 是否支持多进程 .build(); TTAdSdk.init(this, config, new TTAdSdk.InitCallback() { @Override public void success() { Log.d("TTAdSdk", "初始化成功"); } @Override public void fail(int code, String msg) { Log.e("TTAdSdk", "初始化失败: " + code + ", " + msg); } }); } }3.2 关键配置参数解析
- appId:从穿山甲后台获取的唯一应用标识,必须正确填写
- appName:应用名称,会用于广告展示和用户反馈
- useTextureView:视频广告播放器类型选择
- true:使用TextureView,兼容性更好
- false:使用SurfaceView,性能更优但部分机型可能有问题
- allowShowNotify:是否允许SDK显示通知栏下载进度
- debug:调试模式开关,正式发布必须关闭
- supportMultiProcess:多进程支持,默认false
4. 广告位接入实现
4.1 开屏广告接入
开屏广告是应用启动时展示的全屏广告,实现代码如下:
public class SplashActivity extends AppCompatActivity { private TTAdNative mTTAdNative; private ViewGroup mSplashContainer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash); mSplashContainer = findViewById(R.id.splash_container); mTTAdNative = TTAdSdk.getAdManager().createAdNative(this); loadSplashAd(); } private void loadSplashAd() { AdSlot adSlot = new AdSlot.Builder() .setCodeId("开屏广告位ID") // 必填 .setSupportDeepLink(true) .setImageAcceptedSize(1080, 1920) // 宽高比必须与广告位配置一致 .build(); mTTAdNative.loadSplashAd(adSlot, new TTAdNative.SplashAdListener() { @Override public void onError(int code, String message) { // 广告加载失败,跳转主界面 startMainActivity(); } @Override public void onTimeout() { // 广告加载超时,跳转主界面 startMainActivity(); } @Override public void onSplashAdLoad(TTSplashAd ad) { if (ad == null) { startMainActivity(); return; } // 设置开屏广告交互监听 ad.setSplashInteractionListener(new TTSplashAd.AdInteractionListener() { @Override public void onAdClicked(View view, int type) { // 广告被点击 } @Override public void onAdShow(View view, int type) { // 广告展示 } @Override public void onAdSkip() { // 用户跳过广告 startMainActivity(); } @Override public void onAdTimeOver() { // 广告展示时间结束 startMainActivity(); } }); // 获取开屏View并添加到容器 View splashView = ad.getSplashView(); mSplashContainer.removeAllViews(); mSplashContainer.addView(splashView); } }, 3000); // 超时时间设置为3秒 } private void startMainActivity() { startActivity(new Intent(this, MainActivity.class)); finish(); } }4.2 信息流广告接入
信息流广告通常嵌入在内容列表中,实现示例:
public class FeedAdAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final int TYPE_AD = 1; private static final int TYPE_CONTENT = 2; private List<Object> mDataList = new ArrayList<>(); private TTAdNative mTTAdNative; public void loadFeedAd() { AdSlot adSlot = new AdSlot.Builder() .setCodeId("信息流广告位ID") .setImageAcceptedSize(640, 320) // 建议宽高比2:1 .setAdCount(3) // 一次加载3条广告 .build(); mTTAdNative.loadFeedAd(adSlot, new TTAdNative.FeedAdListener() { @Override public void onError(int code, String message) { Log.e("FeedAd", "加载失败: " + message); } @Override public void onFeedAdLoad(List<TTFeedAd> ads) { if (ads == null || ads.isEmpty()) { return; } // 随机插入广告到数据列表 Random random = new Random(); for (TTFeedAd ad : ads) { int position = random.nextInt(Math.max(1, mDataList.size())); mDataList.add(position, ad); } notifyDataSetChanged(); } }); } @Override public int getItemViewType(int position) { return mDataList.get(position) instanceof TTFeedAd ? TYPE_AD : TYPE_CONTENT; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == TYPE_AD) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_feed_ad, parent, false); return new AdViewHolder(view); } else { // 正常内容ViewHolder } } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (holder instanceof AdViewHolder) { TTFeedAd feedAd = (TTFeedAd) mDataList.get(position); AdViewHolder adHolder = (AdViewHolder) holder; // 绑定广告数据 if (feedAd.getImageList() != null && !feedAd.getImageList().isEmpty()) { Glide.with(adHolder.ivImage).load(feedAd.getImageList().get(0).getImageUrl()) .into(adHolder.ivImage); } adHolder.tvTitle.setText(feedAd.getTitle()); adHolder.tvDescription.setText(feedAd.getDescription()); // 注册广告交互视图 List<View> clickViews = new ArrayList<>(); clickViews.add(adHolder.itemView); feedAd.registerViewForInteraction((ViewGroup) adHolder.itemView, clickViews, null, new TTNativeAd.AdInteractionListener() { @Override public void onAdClicked(View view, TTNativeAd ad) { // 广告被点击 } @Override public void onAdCreativeClick(View view, TTNativeAd ad) { // 创意区域被点击 } @Override public void onAdShow(TTNativeAd ad) { // 广告展示 } }); } else { // 绑定正常内容 } } static class AdViewHolder extends RecyclerView.ViewHolder { ImageView ivImage; TextView tvTitle; TextView tvDescription; public AdViewHolder(View itemView) { super(itemView); ivImage = itemView.findViewById(R.id.iv_ad_image); tvTitle = itemView.findViewById(R.id.tv_ad_title); tvDescription = itemView.findViewById(R.id.tv_ad_desc); } } }4.3 激励视频广告接入
激励视频广告需要用户完整观看视频后才能获得奖励,实现方式:
public class RewardVideoActivity extends AppCompatActivity { private TTAdNative mTTAdNative; private TTRewardVideoAd mRewardVideoAd; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_reward_video); mTTAdNative = TTAdSdk.getAdManager().createAdNative(this); loadRewardVideoAd(); } private void loadRewardVideoAd() { AdSlot adSlot = new AdSlot.Builder() .setCodeId("激励视频广告位ID") .setSupportDeepLink(true) .setRewardName("金币") // 奖励名称 .setRewardAmount(10) // 奖励数量 .setUserID("user123") // 用户ID .setOrientation(TTAdConstant.VERTICAL) // 竖屏方向 .build(); mTTAdNative.loadRewardVideoAd(adSlot, new TTAdNative.RewardVideoAdListener() { @Override public void onError(int code, String message) { Toast.makeText(RewardVideoActivity.this, "加载失败: " + message, Toast.LENGTH_SHORT).show(); } @Override public void onRewardVideoAdLoad(TTRewardVideoAd ad) { mRewardVideoAd = ad; // 设置广告交互监听 mRewardVideoAd.setRewardAdInteractionListener(new TTRewardVideoAd.RewardAdInteractionListener() { @Override public void onAdShow() { // 广告展示 } @Override public void onAdVideoBarClick() { // 广告被点击 } @Override public void onAdClose() { // 广告关闭 finish(); } @Override public void onVideoComplete() { // 视频播放完成 } @Override public void onVideoError() { // 视频播放出错 } @Override public void onRewardVerify(boolean rewardVerify, int rewardAmount, String rewardName, int errorCode, String errorMsg) { // 奖励验证回调 if (rewardVerify) { // 发放奖励 Toast.makeText(RewardVideoActivity.this, "获得奖励: " + rewardAmount + rewardName, Toast.LENGTH_SHORT).show(); } } @Override public void onSkippedVideo() { // 用户跳过了视频 } }); // 展示广告 mRewardVideoAd.showRewardVideoAd(RewardVideoActivity.this); } @Override public void onRewardVideoCached() { // 广告视频缓存完成(已废弃,建议使用onRewardVideoAdLoad) } }); } }5. 高级功能与优化策略
5.1 广告预加载机制
为提高广告展示成功率,建议采用预加载策略:
public class AdPreloadManager { private static AdPreloadManager sInstance; private TTAdNative mTTAdNative; private Map<String, Object> mPreloadAdMap = new ConcurrentHashMap<>(); public static AdPreloadManager getInstance(Context context) { if (sInstance == null) { synchronized (AdPreloadManager.class) { if (sInstance == null) { sInstance = new AdPreloadManager(context.getApplicationContext()); } } } return sInstance; } private AdPreloadManager(Context context) { mTTAdNative = TTAdSdk.getAdManager().createAdNative(context); } public void preloadRewardVideo(String codeId) { AdSlot adSlot = new AdSlot.Builder() .setCodeId(codeId) .setSupportDeepLink(true) .setRewardName("金币") .setRewardAmount(10) .setOrientation(TTAdConstant.VERTICAL) .build(); mTTAdNative.loadRewardVideoAd(adSlot, new TTAdNative.RewardVideoAdListener() { @Override public void onError(int code, String message) { Log.e("Preload", "激励视频预加载失败: " + message); } @Override public void onRewardVideoAdLoad(TTRewardVideoAd ad) { mPreloadAdMap.put(codeId, ad); Log.d("Preload", "激励视频预加载成功"); } @Override public void onRewardVideoCached() { // 已废弃 } }); } public TTRewardVideoAd getPreloadRewardVideo(String codeId) { Object ad = mPreloadAdMap.remove(codeId); return ad instanceof TTRewardVideoAd ? (TTRewardVideoAd) ad : null; } }5.2 多广告位瀑布流策略
对于信息流广告,可以采用瀑布流策略提高填充率:
public class WaterfallAdLoader { private TTAdNative mTTAdNative; private List<String> mCodeIdList = new ArrayList<>(); private int mCurrentIndex = 0; public WaterfallAdLoader(Context context) { mTTAdNative = TTAdSdk.getAdManager().createAdNative(context); } public void setCodeIdList(List<String> codeIdList) { mCodeIdList.clear(); mCodeIdList.addAll(codeIdList); } public void loadFeedAd(OnAdLoadListener listener) { if (mCurrentIndex >= mCodeIdList.size()) { listener.onAdLoadFailed("所有广告位尝试完毕"); return; } String codeId = mCodeIdList.get(mCurrentIndex); AdSlot adSlot = new AdSlot.Builder() .setCodeId(codeId) .setImageAcceptedSize(640, 320) .setAdCount(3) .build(); mTTAdNative.loadFeedAd(adSlot, new TTAdNative.FeedAdListener() { @Override public void onError(int code, String message) { mCurrentIndex++; loadFeedAd(listener); // 尝试下一个广告位 } @Override public void onFeedAdLoad(List<TTFeedAd> ads) { if (ads == null || ads.isEmpty()) { mCurrentIndex++; loadFeedAd(listener); } else { mCurrentIndex = 0; // 重置索引 listener.onAdLoadSuccess(ads); } } }); } public interface OnAdLoadListener { void onAdLoadSuccess(List<TTFeedAd> ads); void onAdLoadFailed(String message); } }5.3 广告性能监控与优化
建议实现广告性能监控体系:
public class AdPerformanceMonitor { private static final String TAG = "AdPerformance"; // 广告类型 public static final int TYPE_SPLASH = 1; public static final int TYPE_FEED = 2; public static final int TYPE_REWARD = 3; // 记录广告请求开始时间 public static void recordAdRequestStart(int adType, String codeId) { long startTime = System.currentTimeMillis(); // 保存到SharedPreferences或内存缓存 } // 记录广告加载成功 public static void recordAdLoadSuccess(int adType, String codeId) { long endTime = System.currentTimeMillis(); // 计算耗时并上报 } // 记录广告展示 public static void recordAdShow(int adType, String codeId) { // 上报展示事件 } // 记录广告点击 public static void recordAdClick(int adType, String codeId) { // 上报点击事件 } // 记录广告错误 public static void recordAdError(int adType, String codeId, int errorCode, String errorMsg) { // 上报错误信息 } // 定期生成性能报告 public static void generatePerformanceReport() { // 分析数据并生成报告 } }6. 常见问题排查与解决方案
6.1 广告加载失败常见错误码
| 错误码 | 含义 | 解决方案 |
|---|---|---|
| 20001 | 网络错误 | 检查网络连接,确保设备可以访问穿山甲服务器 |
| 20002 | 请求超时 | 适当增加超时时间,优化网络环境 |
| 20003 | 无广告填充 | 检查广告位ID是否正确,尝试使用瀑布流策略 |
| 20004 | 服务器返回数据错误 | 检查SDK版本是否为最新,联系穿山甲技术支持 |
| 20005 | 应用未审核通过 | 确保应用已在穿山甲后台审核通过 |
| 20006 | 广告位未开启 | 检查广告位状态,确保已开启并配置正确 |
| 20007 | 请求过于频繁 | 调整广告请求频率,避免短时间内多次请求 |
| 20008 | 广告位ID无效 | 检查广告位ID是否正确配置 |
6.2 广告展示异常问题排查
问题1:开屏广告黑屏或闪退
可能原因:
- 未在主线程调用广告展示方法
- 开屏容器View未正确设置宽高
- 设备内存不足导致渲染失败
解决方案:
- 确保广告展示在主线程执行
- 检查开屏容器View的宽高是否与广告位配置一致
- 添加内存监控,在低内存时禁用广告加载
问题2:信息流广告点击无响应
可能原因:
- 未正确注册广告交互视图
- 广告View被其他视图遮挡
- 广告数据已过期
解决方案:
- 确保调用registerViewForInteraction方法注册点击视图
- 检查广告View的z-order和点击区域
- 设置广告加载超时时间,过期广告重新加载
6.3 SDK初始化失败排查
常见错误现象:
- TTAdSdk.init回调返回fail
- 广告加载时报错"SDK未初始化"
排查步骤:
检查appId是否正确
- 确认穿山甲后台配置的应用包名与实际一致
- 检查代码中的appId是否与后台一致
检查初始化线程
- 确保在主线程调用TTAdSdk.init
- 避免在异步任务或子线程中初始化
检查网络权限
- 确认AndroidManifest.xml中声明了INTERNET权限
- 确保设备网络连接正常
检查Proguard配置
- 确保proguard-rules.pro中包含穿山甲SDK的keep规则
- 混淆后检查关键类和方法是否被保留
检查SDK版本
- 使用最新稳定版SDK
- 避免使用已停止维护的旧版本
7. 合规与隐私保护
7.1 隐私政策适配
根据最新监管要求,需要在隐私政策中明确说明:
SDK收集的个人信息类型
- 设备信息(IMEI、OAID、Android ID等)
- 网络信息(IP地址、MAC地址等)
- 应用使用数据
数据使用目的
- 广告投放与效果监测
- 反作弊与安全风控
- 数据分析与优化
用户权利保障
- 提供隐私政策链接
- 提供用户选择退出个性化广告的途径
7.2 GDPR与CCPA合规
对于海外市场,需要额外注意:
GDPR合规要求:
- 获取用户明确同意后才能初始化SDK
- 提供用户数据访问和删除的途径
- 允许用户撤回同意
实现示例:
// 在用户同意隐私政策后初始化SDK if (userConsentGranted) { TTAdSdk.init(...); } else { // 显示隐私政策弹窗 showPrivacyConsentDialog(); } // 提供关闭个性化广告的选项 TTAdSdk.getAdManager().setPersonalizationState( enablePersonalizedAds ? TTAdConstant.PERSONALIZATION_ON : TTAdConstant.PERSONALIZATION_OFF);7.3 儿童隐私保护
如果应用可能被儿童使用,需要特别处理:
- 设置COPPA标识
TTAdSdk.getAdManager().setCoppa( isChildDirectedApp ? TTAdConstant.COPPA_YES : TTAdConstant.COPPA_NO);- 避免收集精确位置信息
- 禁用个性化广告
- 限制广告内容类型
8. 版本升级与维护策略
8.1 SDK版本升级流程
检查当前版本
String sdkVersion = TTAdSdk.getAdManager().getSDKVersion();查看更新日志
- 访问穿山甲开发者平台查看最新版本更新内容
- 重点关注API变更和废弃提示
测试新版本
- 在测试环境验证新版本兼容性
- 特别检查变更的API和配置项
灰度发布
- 先向小部分用户推送新版本
- 监控崩溃率和广告收益变化
全量升级
- 确认无重大问题后全量发布
- 保留回滚方案
8.2 版本维护周期
穿山甲SDK版本维护策略:
- 每个主要版本维护周期为10个月
- 维护期内会修复严重问题和安全漏洞
- 超过维护期的版本不再提供支持
建议升级节奏:
- 每3-6个月升级一次主版本
- 及时应用重要安全更新
- 避免使用即将结束维护的版本
8.3 多版本兼容方案
对于大型应用,可采用分阶段升级策略:
模块化隔离
- 将广告相关代码封装为独立模块
- 通过接口隔离SDK直接依赖
AB测试框架
public interface IAdService { void showSplashAd(Activity activity); void loadFeedAd(OnAdLoadListener listener); } // 旧版本实现 public class LegacyAdService implements IAdService { // 基于旧SDK的实现 } // 新版本实现 public class NewAdService implements IAdService { // 基于新SDK的实现 } // 根据AB测试分组选择实现 public class AdServiceFactory { public static IAdService create(Context context) { if (isInTestGroupA()) { return new NewAdService(context); } else { return new LegacyAdService(context); } } }数据对比监控
- 对比新旧版本的广告填充率
- 监控收益变化和异常情况
- 收集性能指标(加载耗时、内存占用等)
9. 收益优化实战技巧
9.1 广告场景优化
开屏广告优化
- 设置合理的超时时间(3-5秒)
- 使用动态布局适配不同设备
- 预加载下一屏广告
信息流广告优化
- 控制广告密度(建议5-8条内容1条广告)
- 采用原生样式与内容融合
- 实现智能刷新策略
激励视频优化
- 明确奖励机制和用户价值
- 设置合理的奖励门槛
- 提供多种奖励场景
9.2 分层流量管理
根据用户价值分层展示广告:
高价值用户
- 展示高eCPM广告形式
- 控制广告展示频率
- 提供无广告订阅选项
普通用户
- 平衡用户体验与收益
- 采用混合广告策略
- 优化广告展示时机
低活跃用户
- 适当增加广告曝光
- 尝试激励性广告
- 提供复活机制等强激励场景
实现示例:
public class AdStrategyManager { public static final int USER_TIER_HIGH = 1; public static final int USER_TIER_MID = 2; public static final int USER_TIER_LOW = 3; public static String getSplashAdCodeId(int userTier) { switch (userTier) { case USER_TIER_HIGH: return "高端用户开屏广告位ID"; case USER_TIER_MID: return "普通用户开屏广告位ID"; case USER_TIER_LOW: return "低端用户开屏广告位ID"; default: return "默认开屏广告位ID"; } } public static int getFeedAdInterval(int userTier) { switch (userTier) { case USER_TIER_HIGH: return 8; case USER_TIER_MID: return 6; case USER_TIER_LOW: return 4; default: return 5; } } }9.3 A/B测试框架实现
建立广告策略测试体系:
配置管理
public class AdConfig { private String splashAdId; private int feedAdInterval; private boolean enableRewardAd; // 其他配置参数 // 从服务器获取配置 public static AdConfig loadFromServer() { // 实现网络请求获取配置 } }实验分组
public class AdExperiment { public static final String GROUP_A = "A"; public static final String GROUP_B = "B"; public static String getGroup(String userId) { // 根据用户ID哈希分组 int hash = userId.hashCode(); return hash % 2 == 0 ? GROUP_A : GROUP_B; } }数据上报
public class AdMetrics { public static void trackImpression(String adType, String experimentGroup) { // 上报广告展示数据 } public static void trackClick(String adType, String experimentGroup) { // 上报广告点击数据 } public static void trackRevenue(double revenue, String adType) { // 上报广告收益数据 } }结果分析
- 对比不同实验组的收益差异
- 分析用户留存和活跃度变化
- 根据数据调整广告策略
10. 疑难问题深度解析
10.1 内存泄漏排查与修复
常见泄漏场景:
广告回调持有Activity引用
// 错误示例:匿名内部类隐式持有外部类引用 mTTAdNative.loadSplashAd(adSlot, new TTAdNative.SplashAdListener() { @Override public void onSplashAdLoad(TTSplashAd ad) { // 持有Activity引用 mActivity.showAd(ad); } }); // 正确做法:使用弱引用 private static class SafeSplashListener implements TTAdNative.SplashAdListener { private WeakReference<Activity> mActivityRef; public SafeSplashListener(Activity activity) { mActivityRef = new WeakReference<>(activity); } @Override public void onSplashAdLoad(TTSplashAd ad) { Activity activity = mActivityRef.get(); if (activity != null && !activity.isFinishing()) { activity.showAd(ad); } } }未及时释放广告资源
@Override protected void onDestroy() { if (mSplashAd != null) { mSplashAd.setSplashInteractionListener(null); mSplashAd = null; } super.onDestroy(); }
检测工具:
- Android Profiler
- LeakCanary
- MAT内存分析工具
10.2 兼容性问题处理
常见兼容性问题:
Android版本兼容
- 最低支持版本冲突(从6.8.0.7起要求minSdkVersion ≥ 24)
- 使用条件API检查:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 使用新API } else { // 回退方案 }设备厂商兼容
- 华为设备缺少GMS服务
- 小米等厂商的后台限制
- OPPO/Vivo的权限管理差异
解决方案:
- 添加厂商特定配置
- 实现优雅降级逻辑
- 测试主流厂商设备
10.3 广告样式自定义
信息流广告UI定制:
注册自定义渲染器
public class CustomAdRenderer implements TTNativeAdRenderer<TTFeedAd> { @Override public View createView(Context context, int width, int height) { // 创建自定义广告视图 return LayoutInflater.from(context).inflate(R.layout.custom_ad_layout, null); } @Override public void renderAdView(View view, TTFeedAd ad) { // 绑定广告数据到自定义视图 ImageView imageView = view.findViewById(R.id.ad_image); TextView titleView = view.findViewById(R.id.ad_title); titleView.setText(ad.getTitle()); if (ad.getImageList() != null && !ad.getImageList().isEmpty()) { Glide.with(view).load(ad.getImageList().get(0).getImageUrl()).into(imageView); } } } // 注册渲染器 TTAdSdk.getAdManager().registerNativeAdRenderer(TTFeedAd.class, new CustomAdRenderer());自定义Dislike选项
// 设置自定义Dislike弹窗 TTAdSdk.getAdManager().setDislikeCustomStyle(activity, new CustomDislikeConfig.Builder() .setTitle("选择不喜欢的理由") .setItems(new String[]{"不感兴趣", "内容重复", "广告质量差"}) .build()); // 监听Dislike选择 ad.setDislikeCallback(activity, new TTAdDislike.DislikeInteractionCallback() { @Override public void onSelected(int position, String value, boolean enforce) { // 处理用户选择 if (enforce) { // 必须关闭广告 } } @Override public void onCancel() { // 用户取消选择 } });
11. 测试与调试技巧
11.1 测试广告位配置
穿山甲提供专门的测试广告位ID:
| 广告类型 | 测试广告位ID | 备注 |
|---|---|---|
| 开屏广告 | 887341209 | 需设置期望的宽高 |
| 信息流广告 | 945196201 | 模板/自渲染通用 |
| 激励视频 | 945196202 | 竖屏样式 |
| 全屏视频 | 945196203 | 横屏样式 |
使用测试广告位的优势:
- 100%填充率
- 快速验证集成是否正确
- 避免开发阶段消耗正式广告配额
11.2 调试日志开启
穿山甲SDK提供详细的调试日志:
// 初始化时开启调试模式 TTAdConfig config = new TTAdConfig.Builder() .debug(true) // 开启调试日志 .build(); TTAdSdk.init(context, config); // 过滤日志标签查看 adb logcat -s TTAdSdk关键日志标签:
- TTAdSdk:核心流程日志
- TTAdHttp:网络请求日志
- TTAdPlayer:视频播放日志
- TTAdRender:广告渲染日志
11.3 自动化测试方案
实现广告自动化测试框架:
- Monkey测试增强
// 自定义广告Monkey策略 public class AdMonkey extends Monkey { private static final String[] AD_ACTIONS = { "CLICK_AD", "SKIP_AD", "REWARD_AD" }; @Override public void run() { // 50%概率执行广告相关操作 if (random.next