# Android SDK 接入文档
# 注意:
创建应用并拿到广告SDK所需的参数。
获取并解压我们提供的压缩包,把 *.aar 放入app的libs工程中。
SDK 支持 Android API Level 19+。
Support版本支持需联系相关人员获取。
# 一、导入SDK依赖
# 1.1 本地aar依赖
1、在 app 目录下新建 libs 文件夹
2、将压缩包中的 aar 文件放入 libs 文件夹
3、在 app/build.gradle 添加如下代码
repositories {
flatDir {
dirs 'libs'
}
}
dependencies {
// SDK AAR文件 放入项目libs中
implementation fileTree(include: ['*.aar'], dir: 'libs')
// 或直接引用
// implementation files('./libs/mp_xxx.aar')
}
# 1.2 AndroidManifest 配置
<manifest>
<application>
<!-- 联网权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 检测当前网络状态是移动、WiFi -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 读写存储权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Android 9(API 级别 28)及更高版本中 需要后台下载高版本需要配置此项 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
</application>
# 1.3 混淆配置
# 注意事项:
如果 tools.build:gradle 版本小于 4.1.0,请检查自定义混淆规则或 proguard-android-optimize,确认混淆规则中没有使用 -allowaccessmodification 属性
# 1.4 媒体需要接入信通院 OAID-SDK
Android10 及以上设备需要 OAID 的支持才能正常进行收益计费
移动安全联盟 MSA 官网 (opens new window) https://msa-alliance.cn/col.jsp?id=120 (opens new window)
注意:如果使用自定义OAID, 则需要确保 OAID 的合法性,否则将影响广告获取和收益
# 二、接入配置
# 2.1 初始化SDK
提示:开发者需要在符合网信办合规要求的前提下进行SDK的初始化,开发者需要在用户同意APP的隐私政策之后调用以下代码来初始化SDK 注意:默认仅支持初始化SDK一次,多次初始化SDK以第一次初始化为准。
// 初始化广告SDK
PtgAdSdk.init(上下文, new PtgSDKConfig.Builder()
.setAppName(媒体名称)
.setMediaId(媒体ID)
.setMediaSecret(媒体秘钥)
.setDebug(DEBUG模式)
.setPtgCustomController(隐私控制器)
.build());
# PtgSDKConfig.Builder 说明:初始化需要传入以下参数
| 方法 | 说明 |
|---|---|
| setMediaId(String meidaId) | 必选参数,媒体ID |
| setAppName(String appName) | 可选参数,媒体App名称 |
| setMediaSecret(String mediaSecret) | 必选参数,媒体秘钥 |
| setDebug(boolean debug) | Debug模式:true 是, fasle 否 |
| setPtgCustomController(PtgCustomController customController) | 隐私信息控制开关 |
# PtgAdSdk.Callback说明
| 方法 | 说明 |
|---|---|
| void success(); | SDK初始化成功回调 |
| void fail(int code, String msg); | SDK初始化失败回调 |
# 2.2 隐私信息控制开关
注意:接入时请实现以下控制开关,否则影响广告收益
private static PtgCustomController buildCustomController(Application application) {
String TAG = "CustomControllerTag";
return new PtgCustomController() {
String tempOaid = "";
AdLocation tempLocation;
List<String> tempPackageList;
/// 当isAllowSDKObtainPhoneInfo = false时,可传入imei信息
@Override
public String getMediaDeviceImei() {
Log.d(TAG, "getMediaDeviceImei");
// FIXME: 这里建议回传IMEI
return super.getMediaDeviceImei();
}
/// 是否允许SDK主动获取ANDROID_ID
@Override
public boolean isAllowSDKObtainAndroidId() {
Log.d(TAG, "isAllowSDKObtainAndroidId");
// return super.isAllowSDKObtainAndroidId();
// 需要禁止,这里设置为true
return false;
}
/// 当isAllowSDKObtainAndroidId = false时,可传入androidId信息
@Override
public String getMediaAndroidId() {
Log.d(TAG, "getMediaAndroidId");
// return super.getMediaAndroidId();
// FIXME:这里回传AndroidId
return XXX.getAndroidId(application);
}
/// 是否允许SDK 获取 oaid
@Override
public boolean isAllowSDKObtainOaId() {
Log.d(TAG, "isAllowSDKObtainOaId");
// return super.isAllowSDKObtainOaId();
// FIXME:这里建议媒体自行获取OAID回传给SDK
return false;
}
/// 开发者可以传入oaid
/// 信通院OAID的相关采集——如何获取OAID:
/// 1. 移动安全联盟官网http://www.msa-alliance.cn/
/// 2. 信通院统一SDK下载http://msa-alliance.cn/col.jsp?id=120
@Override
public String getMediaDeviceOaId() {
Log.d(TAG, "getMediaDeviceOaId");
// return super.getMediaDeviceOaId();
// FIXME:这里建议媒体自行获取OAID回传给SDK
return XXX.getOaid();
}
///是否允许获取硬盘存储空间大小
public boolean isAllowHardDiskSizeKBytes(){
return true;
}
/// 当isAllowSDKObtainMacAddress = false 时,可以传入mac 地址信息, sdk 使用传入的Mac地址信息
@Override
public String getMediaMacAddress() {
Log.d(TAG, "getMediaMacAddress");
// return super.getMediaMacAddress();
return XXX.getMacAddress();
}
/// 是否允许SDK主动使用地理位置信息
@Override
public boolean isAllowSDKObtainLocation() {
Log.d(TAG, "isAllowSDKObtainLocation");
// return super.isAllowSDKObtainLocation();
// FIXME:这里建议媒体自行获取Location回传给SDK
return false;
}
/// 当isAllowSDKObtainLocation =false时,可传入地理位置信息,sdk使用您传入的地理位置信息
@Override
public AdLocation getMediaLocation() {
Log.d(TAG, "getMediaLocation");
// return super.getMediaLocation();
// FIXME:这里建议媒体自行获取Location回传给SDK
return XXX.getLocation;
}
/// 是否允许SDK主动获取设备上应用安装列表的采集权限
@Override
public boolean isAllowSDKInstallList() {
Log.d(TAG, "isAllowSDKInstallList");
// return super.isAllowSDKInstallList();
return false;
}
/// 当 isAllowSDKInstallList =false 可传入packages 信息,SDK使用传入的 packages list
@Override
public List<String> getMediaInstalledPackages() {
Log.d(TAG, "getMediaInstalledPackages");
return tempPackageList;
}
/// ali boot 没有可以不传递
@Override
public String getAliBoot() {
Log.d(TAG, "getAliBoot");
return super.getAliBoot();
}
/// ali update 没有可以不传递
@Override
public String getAliUpdate() {
Log.d(TAG, "getAliUpdate");
return super.getAliUpdate();
}
};
}
PtgCustomController说明 注意:以下方法为同步方法,媒体实现时需考虑代码执行时间及持久化处理
| 方法 | 说明 |
|---|---|
| String getMediaDeviceImei() | 可传入imei信息,SDK仅接收媒体传入,不主动获取 |
| boolean isAllowSDKObtainAndroidId() | 设置是否允许获取AndroidId,默认为true |
| boolean isAllowHardDiskSizeKBytes() | 设置是否允许获取硬盘存储空间,默认为true |
| String getMediaAndroidId() | isAllowSDKObtainAndroidId传false时 ,可以传入 android_id 信息 |
| boolean isAllowSDKObtainOaId() | 设置是否允许获取OAID,默认为true |
| String getMediaDeviceOaId() | isAllowSDKObtainOaId传false时,可传入oaid信息 |
| String getMediaMacAddress() | 可传入mac地址信息,SDK仅接收媒体传入,不主动获取 |
| AdLocation getMediaLocation() | 可传入地理位置信息,SDK仅接收媒体传入,不主动获取 |
| boolean isAllowSDKInstallList() | 设置是否允许获取应用安装列表,默认为true |
| List | isAllowSDKInstallList传false时,可传入应用安装列表信息 |
| boolean isCanUseCarrier() | 是否可以使用运营商信息,默认true |
| String getAliBoot() | 可传入ali boot信息(非必须),广告预算使用 |
| String getAliUpdate() | 可传入ali update信息(非必须),广告预算使用 |
其他隐私设置说明
- 加速度传感器、陀螺仪传感器;在进行摇一摇、扭一扭等广告投放和广告反作弊分析时使用
// SDK初始化后配置,是否允许使用传感器 true 开启, false 关闭, 默认值为true
PtgAdSdk.setSensorStatus(bool);
- 设置是否允许开启个性化开关,默认为true
// 是否开启个性化广告开关,默认为true(开启),false为关闭
PtgAdSdk.setPersonalizedState(bool);
# 三、广告位接入
# 3.1 开屏广告
参考 Demo 中 SplashFragment 类 说明:开屏广告建议为用户在进入App时展示的全屏广告。开屏广告为一个View,宽高默认为match_parent。 注意:开屏广告View:width>=70%屏幕宽;height>=50%屏幕高,否则会影响计费。
# 3.1.1 开屏广告位
AdSlot.Builder
| 方法名 | 说明 | 必填 |
|---|---|---|
| setPtgSlotId(String slotId) | 广告位ID | 是 |
| setExpressViewAcceptedDpSize(float w, float h) | 设置宽高,单位dp | 否 |
| setAdLoadType(PtgAdLoadType.UNKNOWN) | 加载类型: PtgAdLoadType.UNKNOWN 默认值 PtgAdLoadType.PRELOAD 预加载 | 否 |
# 3.1.2 请求开屏广告
调用 PtgAdSdk.get().loadSplashAd(Context context, AdSlot adSlot, SplashAdListener splashAdListener) 加载广告
| 参数 | 说明 | 必填 |
|---|---|---|
| Context context | 上下文,可以是Application级 | 是 |
| AdSlot adSlot | 广告位配置 | 是 |
| PtgAdNative.SplashAdListener splashAdListener | 广告加载回调 | 是 |
# 3.1.3 开屏广告加载回调说明
PtgAdNative.SplashAdListener
| 回调方法 | 说明 |
|---|---|
| onSplashAdLoad(PtgSplashAd ptgSplashAd) | 广告加载成功,PtgSplashAd 为广告对象 |
| onError(AdError adError) | 广告加载失败,AdError 错误信息 |
| onTimeout() | 广告加载超时 |
# 3.1.4 开屏广告对象
PtgSplashAd
| 方法 | 说明 | 是否必须实现 |
|---|---|---|
| setSplashInteractionListener (AdInteractionListener var1) | 设置广告监听器 | 是 |
| isReady() | 用于判断广告是否已经是准备完成的状态 true:准备完成 false:未完成或过期 | 是 |
| isResourceLoaded() | 资源是否已加载 | 否 |
| showAd(ViewGroup var1) | 展示广告,传入容器 | 是 |
| destroy() | 销毁广告 | 是 |
# 3.1.5 开屏广告监听器
PtgSplashAd.AdInteractionListener
| 回调方法 | 说明 |
|---|---|
| onAdClicked() | 点击 |
| onAdShow() | 展示 |
| onAdSkip() | 点击跳过 |
| onAdTimeOver() | 倒计时结束 |
| onRenderSuccess() | 资源渲染成功 |
| onRenderError(AdError adError) | 渲染失败 |
| onDismiss(int closeType) | 广告关闭,会多次回调onDismiss(),需开发者特殊处理 closeType 取值: AdConstant.SPLASH_AD_CLOSE_TYPE.CLICK_SKIP = 1 (跳过) AdConstant.SPLASH_AD_CLOSE_TYPE.COUNT_DOWN_OVER = 2 (倒计时结束) AdConstant.SPLASH_AD_CLOSE_TYPE.CLICK_JUMP = 3 (点击跳转) |
# 3.1.6 示例代码
// 请求广告
// 注意** 请确保splashContainer容器高度大于400,使用权重layout_weight时,高度不可使用0
// TODO: 2022/5/9 1.6.2 版本以上必须设置 setExpressViewAcceptedDpSize 高度必传,宽度可不传,单位为dp
// 创建广告AdSlot,用于请求广告前,传递部分广告配置参数。
AdSlot adSlot = new AdSlot.Builder()
.setPtgSlotId(AdvertContract.AD_ID_SPLASH)
.setExpressViewAcceptedDpSize(screenDpW, screenDpH)
.build();
PtgAdSdk.get().loadSplashAd(getActivity().getApplicationContext(), adSlot, new PtgAdNative.SplashAdListener() {
@Override
public void onError(AdError adError) {
Log.d(TAG, "广告无填充 Code: " + adError.getErrorCode() + " Message: " + adError.getMessage());
// 开发者处理跳转到APP主页面逻辑
jumpMainActivity();
}
@Override
public void onTimeout() {
Log.d(TAG, "广告加载超时");
// 开发者处理跳转到APP主页面逻辑
jumpMainActivity();
}
@Override
public void onSplashAdLoad(PtgSplashAd advert) {
Log.d(TAG, "广告加载成功");
// 保存广告对象,用于销毁广告使用
tempPtgSplashAd = advert;
if (advert == null) {
jumpMainActivity();
} else {
advert.setSplashInteractionListener(new PtgSplashAd.AdInteractionListener() {
@Override
public void onAdClicked() {
Log.d(TAG, "广告点击");
}
@Override
public void onAdShow() {
Log.d(TAG, "广告展现");
// 媒体时长保护机制使用(该逻辑仅供参考)
mHasShowing = true;
}
@Override
public void onAdSkip() {
Log.d(TAG, "广告点击跳过");
jumpMainActivity();
}
@Override
public void onAdTimeOver() {
Log.d(TAG, "广告倒计时结束");
jumpMainActivity();
}
@Override
public void onRenderError(AdError adError) {
// 只有传入的viewGroup不是Activity创建的,才会回调onRenderError
Log.d(TAG, "广告渲染失败:" + adError.getErrorCode() + " " + adError.getMessage());
jumpMainActivity();
}
});
// 展示广告,传入容器
// 注意:viewGroup必须基于Activity创建,否则会导致广告渲染失败
advert.showAd(binding.splashContainer);
// 关闭保护机制(该逻辑仅供参考)
closeProtection();
}
}
});
// 销毁广告
@Override
public void onDestroy() {
super.onDestroy();
// PtgSplashAd splashAd;
closeProtection();
if (tempPtgSplashAd != null) {
tempPtgSplashAd.destroy();
}
}
# 3.2 插屏广告
参考 Demo InteractionActivity 类
# 3.2.1 插屏广告位
AdSlot.Builder
| 方法 | 说明 | 必填 |
|---|---|---|
| setPtgSlotId(String slotId) | 广告位ID | 是 |
# 3.2.2 请求插屏广告
调用 PtgAdSdk.get().loadInteractionExpressAd(Activity activity, AdSlot asSlot, InteractionExpressAdListener interactionExpressAdListener) 加载广告
| 参数 | 说明 | 必填 |
|---|---|---|
| Context context | 上下文,可以是Application级 | 是 |
| AdSlot adSlot | 广告位配置 | 是 |
| PtgAdNative.InteractionExpressAdListener interactionExpressAdListener | 广告加载回调 | 是 |
# 3.2.3 插屏广告加载回调说明
PtgAdNative.InteractionExpressAdListener
| 回调方法 | 说明 |
|---|---|
| onInteractionAdLoad(PtgInteractionAd ptgInteractionAd) | 广告加载成功,PtgInteractionAd 为广告对象 |
| onError(AdError adError) | 广告加载失败,AdError 错误信息 |
# 3.2.4 插屏广告对象
PtgInteractionAd
| 方法 | 说明 | 是否必须实现 |
|---|---|---|
| setAdInteractionListener (AdInteractionListener adInteractionListener) | 设置广告监听器 | 是 |
| isReady() | 用于判断广告是否已经是准备完成的状态 true:准备完成 false:未完成或过期 | 是 |
| showInteractionAd(Activity activity) | 展示广告,传入当前Activity | 是 |
| closureInterstitialAd() | 关闭当前插屏广告 | 否 |
| destroy() | 销毁广告 | 是 |
# 3.2.5 插屏广告监听器
PtgSplashAd.AdInteractionListener
| 回调方法 | 说明 |
|---|---|
| onAdClicked() | 点击 |
| onAdShow() | 展示 |
| onAdDismiss() | 倒计时结束 |
| onRenderError(AdError adError) | 渲染失败 |
# 3.2.6 示例代码
// 请求广告
// 创建广告AdSlot,用于请求广告前,传递部分广告配置参数。
AdSlot adSlot = new AdSlot.Builder()
.setPtgSlotId(slotId)
.build();
// 请求插屏广告
PtgAdSdk.get().loadInteractionExpressAd(this, adSlot, new PtgAdNative.InteractionExpressAdListener() {
@Override
public void onInteractionAdLoad(PtgInteractionAd interactionAd) {
Log.d(TAG, "广告加载成功");
// 保存广告对象,用于销毁广告使用
tempInteractionAd = interactionAd;
// 设置广告监听器
tempInteractionAd.setAdInteractionListener(new PtgInteractionAd.AdInteractionListener() {
@Override
public void onAdClicked() {
Log.d(TAG, "广告点击");
// 这里可以根据自己的业务需求,自行处理关闭广告逻辑,tempClickAdClose true: 点击后关闭 false: 不关闭
if (tempInteractionAd != null && tempClickAdClose) {
tempInteractionAd.closureInterstitialAd();
}
}
@Override
public void onAdShow() {
Log.d(TAG, "广告显示");
}
@Override
public void onAdDismiss() {
Log.d(TAG, "广告关闭");
}
@Override
public void onRenderError(AdError adError) {
Log.d(TAG, "渲染失败:" + adError.getMessage());
}
});
// 展示广告
tempInteractionAd.showInteractionAd(InteractionActivity.this);
}
@Override
public void onError(AdError adError) {
Log.d(TAG, "广告加载失败 " + adError.getMessage());
}
});```
```java
// 销毁广告
@Override
protected void onDestroy() {
super.onDestroy();
// 销毁广告
if (tempInteractionAd != null) {
tempInteractionAd.destroy();
}
}
# 3.3 原生广告(模版\自渲染)
注意: 原生广告具体实现可参考 Demo 中 FeedActivity、FeedListActivity类。 自渲染广告: SDK返回物料,建议由开发者根据自己的广告设计自行UI适配和展示。
# 3.3.1 原生广告位
AdSlot.Builder
| 方法名 | 说明 | 必填 |
|---|---|---|
| setPtgSlotId(String slotId) | 广告位ID | 是 |
| setExpressViewAcceptedDpSize(float w, float h) | 设置宽高,单位dp | 是 |
| setSelfRender(boolean selfRender) | 是否自渲染 | 否,默认:false 模板,true 自渲染 |
注意:后期版本将不再支持本地setSelfRender方法,将以服务端配置为准
# 3.3.2 请求原生广告
调用 PtgAdSdk.get().loadNativeExpressAd(Context context, AdSlot adSlot, NativeExpressAdListener splashAdListener) 加载广告
| 参数 | 说明 | 必填 |
|---|---|---|
| Context context | 上下文,可以是Application级 | 是 |
| AdSlot adSlot | 广告位配置 | 是 |
| PtgAdNative.NativeExpressAdListener nativeExpressAdListener | 广告加载回调 | 是 |
# 3.3.3 原生广告加载回调说明
PtgAdNative.NativeExpressAdListener
| 回调方法 | 说明 |
|---|---|
| onNativeExpressAdLoad(PtgNativeExpressAd nativeExpressAd) | 广告加载成功,PtgNativeExpressAd 为广告对象 |
| onError(AdError adError) | 广告加载失败,AdError 错误信息 |
# 3.3.4 原生广告对象
PtgNativeExpressAd
| 方法 | 说明 | 是否必须实现 |
|---|---|---|
| setExpressInteractionListener (AdInteractionListener adInteractionListener) | 设置广告监听器,建议render之前设置 | 是 |
| setVideoAdListener(PtgVideoAdListener videoAdListener) | 视频播放状态监听器 | 否 |
| isReady() | 用于判断广告是否已经是准备完成的状态 true:准备完成 false:未完成或过期 | 是 |
| render() | 模板&自渲染展示广告 | 是 |
| getExpressAdView() | 获取最终广告视图,模板&自渲染均从此方法获取,建议render之后添加到容器 | 是 |
| isSelfRender() | 是否是自渲染模式 | 是 |
| getEcpm() | 获取ecpm价格,单位:分 | 否 |
| registerAdvertViews(ViewGroup nativeView, List | 自渲染绑定布局 nativeView 广告容器 clickViewList 点击布局 creativeViewList 创意布局 nativeAdClose 关闭布局 | 否 |
| isVideo() | 是否是视频广告 | 是 |
| getMediaView() | 视频视图,自渲染使用 | 否 |
| getCustomizeVideo() | 获取视频素材 | 否 |
| startVideo() | 视频播放 | 否 |
| pauseVideo() | 视频暂停 | 否 |
| destroy() | 销毁广告 | 是 |
# 3.3.5 原生广告监听器 & 视频播放监听器
PtgNativeExpressAd.AdInteractionListener
| 回调方法 | 说明 |
|---|---|
| onAdClicked() | 点击 |
| onRenderFail(AdError adError) | 渲染失败,AdError: 错误对象 |
| onRenderSuccess(View view) | 渲染成功,View: 最终广告视图 |
| onAdShow() | 曝光 |
| onAdDismiss() | 关闭 |
PtgVideoAdListener
| 回调方法 | 说明 |
|---|---|
| onVideoStart() | 开始播放 |
| onVideoPause() | 播放暂停 |
| onVideoResume() | 恢复播放 |
| onVideoComplete() | 播放完毕 |
| onVideoError(int code, String msg) | 播放错误,code 错误码,msg 错误信息 |
| onVideoProgressUpdate(long duration, long position) | 进度更新,duration 总时长, position 当前进度 |
# 3.3.6 NativeAdvertData 广告数据说明:
| 方法 | 说明 |
|---|---|
| getTitle() | 文字标题 |
| getDesc() | 文字描述 |
| getAid() | 创意id |
| getImageList() | 物料URL,图片地址 |
| getAction() | 动作 0 下载app 1 跳转落地页 |
| getUrl() | action = 0: 下载地址,action = 1:落地页 |
| getApp() | app信息 |
| getApp().getAppInfoList() | 下载类合规信息列表(AppInfoItem) |
| getBrand() | 品牌信息 |
| getStyle() | 物料展示样式 |
| getLogo() | logo图片 |
| 物料的附加信息 | |
| getButtonText() | 按钮文案 |
说明:新版将图片封装到了getImageList,旧方法不影响使用
下载类 AppInfoItem
自渲染下载类广告通过 getApp().getAppInfoList() 获取合规信息。按 key 匹配;
| 方法 | 说明 |
|---|---|
| getKey() | 六要素标识 |
| getSubtitle() | 无链接时取值(展示文案) |
| getUrl() | 有链接时取值(跳转地址),空则不可点 |
| key | 含义 | 取值 |
|---|---|---|
| name | 应用名称 | getSubtitle() |
| developer | 开发者 | getSubtitle() |
| version | 版本号 | getSubtitle() |
| icp | 备案号 | getSubtitle() |
| permission | 权限列表 | getUrl() |
| privacy | 隐私政策 | getUrl() |
| function | 功能介绍 | getUrl() |
AppInfo app = nativeAdvertData.getApp();
if (app != null && app.getAppInfoList() != null) {
for (AppInfoItem item : app.getAppInfoList()) {
if (item == null) {
continue;
}
String key = item.getKey();
String value = TextUtils.isEmpty(item.getUrl()) ? item.getSubtitle() : item.getUrl();
// name / developer / version / icp → value 为文案
// permission / privacy / function → value 为链接
}
}
# 3.3.7 PtgNativeExpressAd.CustomizeVideo 视频数据说明:
| 方法 | 说明 | 是否必须实现 |
|---|---|---|
| getVideoUrl() | 视频地址 | 是 |
| getCoverUrl() | 封面地址 | 否 |
| reportVideoStart() | 上报开始播放 | 否 |
| reportVideoPause() | 上报暂停播放 | 否 |
| reportVideoResume() | 上报继续播放 | 否 |
| reportVideoComplete() | 上报播放完毕 | 是 |
| reportVideoError() | 上报播放错误 | 是 |
| reportVideoProgress(long duration, long currentPosition) | 上报播放进度 | 是 |
style: 物料展示样式
| 常量 | 说明 |
|---|---|
| 301 | 上文下图 - 支持图片&视频 |
| 302 | 上图下文 - 支持图片&视频 |
| 303 | 文字浮层 - 支持图片&视频 |
| 304 | 左图右文 - 仅支持图片 |
| 305 | 左文右图 - 仅支持图片 |
| 306 | 三图模板 - 仅支持图片 |
注意: 已经展示过且不再使用的广告需要在合适的时机调用destroy方法及时对广告进行销毁,避免内存异常场景。
# 3.3.8 示例代码
// 加载广告
// 创建广告AdSlot,用于请求广告前,传递部分广告配置参数。
AdSlot adSlot = new AdSlot.Builder()
.setPtgSlotId(AdvertContract.AD_ID_FEED)
// TODO: 2026/9/23 3.0.20 版本以上必须设置 setExpressViewAcceptedDpSize 宽度必传,宽度可传0自适应,单位为dp
.setSelfRender(selfRender)// true: 自渲染 false: 模板
.build();
// 请求信息流模板广告
PtgAdSdk.get().loadNativeExpressAd(getApplicationContext(), adSlot, new PtgAdNative.NativeExpressAdListener() {
@Override
public void onError(AdError adError) {
Log.d(TAG, "加载失败,code:" + adError.getErrorCode() + " msg:" + adError.getMessage());
}
@Override
public void onNativeExpressAdLoad(PtgNativeExpressAd nativeExpressAd) {
// 销毁不使用的广告
destroyAd();
tempPtgNativeExpressAd = nativeExpressAd;
if (tempPtgNativeExpressAd == null) {
Log.e(TAG, "加载失败 广告为空");
return;
}
if (!tempPtgNativeExpressAd.isReady()) {
Log.e(TAG, "广告已过期");
return;
}
Log.d(TAG, "加载成功");
// 设置广告监听器
tempPtgNativeExpressAd.setExpressInteractionListener(new PtgNativeExpressAd.AdInteractionListener() {
@Override
public void onAdDismiss() {
Log.d(TAG, "关闭了");
// 媒体自行移除广告视图,此处为媒体的广告容器
advertContainer.removeAllViews();
// 销毁广告
destroyAd();
}
@Override
public void onAdClicked() {
Log.d(TAG, "点击了");
}
@Override
public void onAdShow() {
Log.d(TAG, "展现了");
}
@Override
public void onRenderFail(AdError adError) {
Log.d(TAG, "渲染失败 " + String.format("渲染错误 %s", adError.getMessage()));
}
@Override
public void onRenderSuccess(View view) {
Log.d(TAG, "渲染成功");
// 注意:不可在此方法中添加布局
}
});
// 设置视频监听器
tempPtgNativeExpressAd.setVideoAdListener(new PtgVideoAdListener() {
@Override
public void onVideoStart() {
Log.d(TAG, "视频 开始播放");
}
@Override
public void onVideoPause() {
Log.d(TAG, "视频 暂停");
}
@Override
public void onVideoResume() {
Log.d(TAG, "视频 恢复播放");
}
@Override
public void onVideoComplete() {
Log.d(TAG, "视频 播放完成");
}
@Override
public void onVideoError(int code, String msg) {
Log.d(TAG, "视频 播放错误 " + code + " / " + msg);
}
@Override
public void onVideoProgressUpdate(long duration, long position) {
Log.d(TAG, "视频 进度更新 " + duration + " / " + position);
}
});
// 广告视图
View advertView;
// 渲染广告, 区分自渲染和模板
if (tempPtgNativeExpressAd.isSelfRender()) {
// 创建自渲染广告视图
advertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.app_native_self_item, null);
advertView.setBackgroundColor(Color.WHITE);
// 媒体绘制自渲染视图
AppNativeSelfUtils.renderCommonAd(getApplicationContext(), nativeExpressAd, (ViewGroup) advertView);
} else {
// 从广告对象中获取最终的广告视图
advertView = tempPtgNativeExpressAd.getExpressAdView();
}
// 兼容该场景下广告视图可能被添加到其他容器中(或重用),需要移除
ViewParent viewParent = advertView.getParent();
if (viewParent instanceof ViewGroup) {
((ViewGroup) viewParent).removeAllViews();
}
// 将广告视图添加到媒体的容器中
advertContainer.removeAllViews();
advertContainer.addView(advertView);
// 调用渲染方法
tempPtgNativeExpressAd.render();
// 销毁广告
@Override
protected void onDestroy() {
super.onDestroy();
// 销毁广告
destroyAd();
}
// 销毁广告(注意在不使用时或页面销毁时销毁广告,否则或出现内存问题)
private void destroyAd() {
if (tempPtgNativeExpressAd != null) {
tempPtgNativeExpressAd.destroy();
tempPtgNativeExpressAd = null;
}
}
# 3.3.9 自渲染帮助类方法(AppNativeSelfUtils.renderCommonAd)(仅供参考)
/**
* 渲染普通自渲染广告
*
* @param context
* @param nativeExpressAd
* @param nativeView
* @return
*/
public static boolean renderCommonAd(Context context, PtgNativeExpressAd nativeExpressAd, ViewGroup nativeView) {
if (context == null || nativeExpressAd == null || nativeView == null) {
return false;
}
NativeAdvertData advertData = nativeExpressAd.getAdvertData();
if (advertData == null) {
return false;
}
advertData.getStyle()
FrameLayout native_ad_container = nativeView.findViewById(R.id.native_ad_container);
ImageView native_ad_close = nativeView.findViewById(R.id.native_ad_close);
Button native_detail_btn = nativeView.findViewById(R.id.native_detail_btn);
TextView native_detail_tv = nativeView.findViewById(R.id.native_detail_tv);
GeneralRoundFrameLayout native_ad_logo_layout = nativeView.findViewById(R.id.native_ad_logo_layout);
TextView native_ad_title = nativeView.findViewById(R.id.native_ad_title);
TextView native_ad_desc = nativeView.findViewById(R.id.native_ad_desc);
ArrayList<View> clickViewList = new ArrayList<>();
clickViewList.add(native_ad_container);
clickViewList.add(native_detail_btn);
clickViewList.add(native_detail_tv);
ArrayList<View> creativeViewList = new ArrayList<>();
// creativeViewList.add(native_detail_btn);
// creativeViewList.add(native_detail_tv);
nativeExpressAd.registerAdvertViews(nativeView, clickViewList, creativeViewList, native_ad_close);
String advertTitle = advertData.getTitle();
if (!TextUtils.isEmpty(advertTitle)) {
native_ad_title.setText(advertTitle);
native_ad_title.setVisibility(View.VISIBLE);
} else {
native_ad_title.setVisibility(View.GONE);
}
String advertDesc = advertData.getDesc();
if (!TextUtils.isEmpty(advertDesc)) {
native_ad_desc.setText(advertDesc);
native_ad_desc.setVisibility(View.VISIBLE);
} else {
native_ad_desc.setVisibility(View.GONE);
}
String logoUrl = advertData.getLogo();
if (!TextUtils.isEmpty(logoUrl)) {
ViewGroup.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
ImageView imageView = new ImageView(context);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setLayoutParams(params);
Glide.with(context).load(logoUrl).into(imageView);
native_ad_logo_layout.addView(imageView, params);
native_ad_logo_layout.setVisibility(View.VISIBLE);
} else {
native_ad_logo_layout.setVisibility(View.GONE);
}
int advertWidth = CommonViewUtils.getScreenWidth();
int advertHeight = advertWidth * 3 / 5;
FrameLayout.LayoutParams advertViewParams = new FrameLayout.LayoutParams(advertWidth, advertHeight);
advertViewParams.gravity = Gravity.CENTER;
View mediaView = nativeExpressAd.getMediaView();
if (mediaView != null) {
if (mediaView.getParent() != null && mediaView.getParent() instanceof ViewGroup) {
((ViewGroup) mediaView.getParent()).removeView(mediaView);
}
native_ad_container.addView(mediaView, advertViewParams);
} else {
List<PtgImageInfo> imageInfoList = advertData.getImageList();
String imageUrl = imageInfoList != null && !imageInfoList.isEmpty() ? imageInfoList.get(0).getImageUrl() : "";
ImageView imageView = new ImageView(context);
imageView.setLayoutParams(advertViewParams);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
Glide.with(context).load(imageUrl).into(imageView);
native_ad_container.addView(imageView, advertViewParams);
}
return true;
}
# 3.4 横幅广告
注意: 横幅广告具体实现可参考 Demo 中 BannerActivity 类
# 3.4.1 横幅广告位
AdSlot.Builder
| 方法名 | 说明 | 必填 |
|---|---|---|
| setPtgSlotId(String slotId) | 广告位ID | 是 |
| setExpressViewAcceptedDpSize(float w, float h) | 设置宽高,单位dp | 是 |
# 3.4.2 请求横幅广告
调用 PtgAdSdk.get().loadBannerExpressAd(Context context, AdSlot adSlot, NativeExpressAdListener nativeExpressAdListener) 加载广告
| 参数 | 说明 | 必填 |
|---|---|---|
| Context context | 上下文,可以是Application级 | 是 |
| AdSlot adSlot | 广告位配置 | 是 |
| NativeExpressAdListener nativeExpressAdListener | 广告加载回调 | 是 |
# 3.4.3 横幅广告加载回调说明
PtgAdNative.NativeExpressAdListener
| 回调方法 | 说明 |
|---|---|
| onNativeExpressAdLoad(PtgNativeExpressAd nativeExpressAd) | 广告加载成功,PtgNativeExpressAd 为广告对象 |
| onError(AdError adError) | 广告加载失败,AdError 错误信息 |
# 3.4.4 横幅广告对象
PtgNativeExpressAd
| 方法 | 说明 | 是否必须实现 |
|---|---|---|
| setExpressInteractionListener (AdInteractionListener adInteractionListener) | 设置广告监听器,建议render之前设置 | 是 |
| isReady() | 用于判断广告是否已经是准备完成的状态 true:准备完成 false:未完成或过期 | 是 |
| render() | 模板展示广告 | 是 |
| getExpressAdView() | 获取最终广告视图,模板&自渲染均从此方法获取,建议render之后添加到容器 | 是 |
| destroy() | 销毁广告 | 是 |
# 3.4.5 横幅广告监听器
PtgNativeExpressAd.AdInteractionListener
| 回调方法 | 说明 |
|---|---|
| onAdClicked() | 点击 |
| onRenderFail(AdError adError) | 渲染失败,AdError: 错误对象 |
| onRenderSuccess(View view) | 渲染成功,View: 最终广告视图 |
| onAdShow() | 曝光 |
| onAdDismiss() | 关闭 |
# 3.4.6 示例代码
// 请求横幅广告
// 创建横幅广告AdSlot,用于请求广告前,传递部分广告配置参数。
AdSlot adSlot = new AdSlot.Builder()
.setPtgSlotId(AdvertContract.AD_ID_BANNER) // 广告位id,此处替换为媒体分配的广告位id
// TODO: 2026/9/23 3.0.20 版本以上必须设置 setExpressViewAcceptedDpSize 宽度必传,宽度可传0自适应,单位为dp
.build();
// 请求横幅广告
PtgAdSdk.get().loadBannerExpressAd(this, adSlot, new PtgAdNative.NativeExpressAdListener() {
@Override
public void onError(AdError adError) {
Log.d(TAG, "加载错误");
}
@Override
public void onNativeExpressAdLoad(PtgNativeExpressAd nativeExpressAd) {
// 保存广告对象,用于销毁广告使用
tempNativeExpressAd = nativeExpressAd;
if (nativeExpressAd == null) {
return;
}
Log.d(TAG, "加载成功");
// 设置广告监听器
nativeExpressAd.setExpressInteractionListener(new PtgNativeExpressAd.AdInteractionListener() {
@Override
public void onAdDismiss() {
Log.d(TAG, "广告关闭");
// 媒体自行移除广告视图,此处为媒体的广告容器
advertContainer.removeAllViews();
}
@Override
public void onAdClicked() {
Log.d(TAG, "广告被点击");
}
@Override
public void onAdShow() {
Log.d(TAG, "广告展示");
}
@Override
public void onRenderFail(AdError adError) {
Log.d(TAG, "展示失败");
}
@Override
public void onRenderSuccess(View view) {
Log.d(TAG, "展示成功");
}
});
// 从广告对象中获取最终的广告视图
View advertView = nativeExpressAd.getExpressAdView();
// 兼容该场景下广告视图可能被添加到其他容器中,需要移除
ViewParent viewParent = advertView.getParent();
if (viewParent instanceof ViewGroup) {
((ViewGroup) viewParent).removeAllViews();
}
// 将广告视图添加到媒体的容器中
advertContainer.removeAllViews();
advertContainer.addView(advertView);
// 调用渲染广告方法
nativeExpressAd.render();
}
});
// 销毁横幅广告
@Override
protected void onDestroy() {
super.onDestroy();
// 媒体根据时机,销毁广告
if (tempNativeExpressAd != null) {
tempNativeExpressAd.destroy();
}
}
# 3.5 激励视频广告
参考 Demo 中的 RewardVideoActivity 类
# 3.5.1 激励视频广告位
AdSlot.Builder
| 方法 | 说明 | 必填 |
|---|---|---|
| setPtgSlotId(String slotId) | 广告位ID | 是 |
| setRewardAmount(int amount) | 设置奖励数量 | 否 |
| setRewardName(String name) | 设置奖励名称 | 否 |
| setUserID(String userId) | 仅奖励发放服务端回调时需要使用 建议保持唯一性即可 | 否 |
| setMediaExtra(String jsonExtra) | 用户透传信息,仅支持单个json对象格式 不可以嵌套json对象 | 否 |
注意:是否使用本地奖励及服务端校验可在后台进行配置
# 3.5.2 请求激励视频广告
调用 PtgAdSdk.get().loadRewardVideoAd(Context context, AdSlot adSlot, RewardVideoAdListener rewardVideoAdListener) 加载广告
| 参数 | 说明 | 必填 |
|---|---|---|
| Context context | 上下文,可以是Application级 | 是 |
| AdSlot adSlot | 广告位配置 | 是 |
| PtgAdNative.RewardVideoAdListener rewardVideoAdListener | 广告加载回调 | 是 |
# 3.5.3 激励视频广告加载回调说明
PtgAdNative.RewardVideoAdListener
| 回调方法 | 说明 |
|---|---|
| onRewardVideoAdLoad(PtgRewardVideoAd ptgRewardVideoAd) | 广告加载成功,PtgRewardVideoAd 为广告对象 |
| onRewardVideoCached(PtgRewardVideoAd ptgRewardVideoAd) | 视频素材预加载完成回调,媒体在收到该回调后调用 showRewardVideoAd 起播体验最佳。最长 5s 必触发一次 |
| onError(AdError adError) | 广告加载失败,AdError 错误信息 |
# 3.5.4 激励视频广告对象
PtgInteractionAd
| 方法 | 说明 | 是否必须实现 |
|---|---|---|
| setRewardAdInteractionListener (PtgRewardVideoAd.RewardAdInteractionListener rewardAdInteractionListener) | 设置广告监听器 | 是 |
| setDownloadListener(PtgAppDownloadListener ptgAppDownloadListener) | 设置下载监听器 | 否 |
| isReady() | 用于判断广告是否已经是准备完成的状态 true:准备完成 false:未完成或过期 | 是 |
| isVideoCached() | 实时检查视频素材本地缓存是否可用 true:缓存就绪 false:未缓存或缓存已被清理 非视频广告或 URL 缺失时返回 true | 否 |
| showRewardVideoAd(Activity activity) | 展示广告,传入当前Activity | 是 |
| destroy() | 销毁广告 | 是 |
# 3.5.5 激励视频广告监听器
PtgRewardVideoAd.RewardAdInteractionListener
| 回调方法 | 说明 |
|---|---|
| onAdShow() | 展示 |
| onAdVideoBarClick() | 点击 |
| onAdClose() | 关闭 |
| onVideoComplete() | 视频播放完毕 |
| onVideoError(int code, String message) | 渲染失败 |
| onRenderError(AdError adError) | 渲染失败,AdError: 错误对象 |
| onRewardVerify(boolean verify, Bundle extra) | 视频奖励,verify true 可发送奖励 false 不可发送奖励、extra 激励信息 |
| onSkippedVideo() | 跳过 |
| onVideoStart() | 开始播放 |
| onVideoPause() | 播放暂停 |
| onVideoResume() | 恢复播放 |
| onVideoProgressUpdate(long duration, long position) | 进度更新,duration 总时长, position 当前进度 |
监听奖励发放 在用户浏览广告达到一定时长等相关条件满足后,SDK会给开发者回调RewardAdInteractionListener.onRewardVerify 方法,表示奖励触发,建议开发者在收到此回调且参考各参数后,决定是否给用户发送奖励。
- onRewardVerify参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
| verify | boolean | 此次奖励是否有效 |
| extra | Bundle | 使用key-value存储的其他信息 参考PtgRewardConstant.REWARD_PARAMS : server_code:错误码 server_msg:错误信息 reward_name:平台配置的奖励名称 reward_amount:平台配置的奖励数量 |
- 奖励发放机制分为客户端回调与服务端回调,具体可参考文档:其他问题 -> 激励视频交互方式简介&奖励方法说明
# 3.5.6 激励视频下载监听器
PtgAppDownloadListener
| 回调方法 | 说明 |
|---|---|
| onIdle() | 当系统处于空闲状态时调用。无参数。 |
| onDownloadActive(long totalBytes, long currBytes, String appName) | 当下载进程活跃时调用,参数包括:totalBytes(下载总字节数)、currBytes(当前下载字节数)、appName(应用名称)。 |
| onDownloadPaused(long totalBytes, long currBytes, String appName) | 当下载暂停时调用,参数包括:totalBytes(下载总字节数)、currBytes(当前下载字节数)、appName(应用名称)。 |
| onDownloadCancel() | 当下载被取消时调用。无参数。 |
| onDownloadFailed(String message) | 当下载失败时调用,参数包括:message(失败的错误信息)。 |
| onDownloadFinished(String appName) | 当下载完成时调用,参数包括:appName(已完成下载的应用名称)。 |
| onInstalled(String appName) | 当应用安装完成时调用,参数包括:appName(已安装的应用名称)。 |
# 3.5.7 示例代码
// 请求激励视频
// 创建广告AdSlot,用于请求广告前,传递部分广告配置参数。
AdSlot adSlot = new AdSlot.Builder()
.setPtgSlotId(AdvertContract.AD_ID_REWARD_VIDEO)
.setRewardAmount(30000) // 设置挽留弹框显示的奖励数量
.setRewardName("金币") // 设置挽留弹框显示的奖励名称
.setUserID("test_0001") // 非必传仅奖励发放服务端回调时需要使用 ,建议保持唯一性即可
.setMediaExtra("{\"test\":\"testData\"}")//用户透传信息,仅支持单个json对象格式,不可以嵌套json对象
.build();
// 请求激励视频广告
PtgAdSdk.get().loadRewardVideoAd(this, adSlot, new PtgAdNative.RewardVideoAdListener() {
@Override
public void onError(AdError adError) {
Log.d(TAG, "激励视频 加载失败 " + adError.getErrorCode() + " " + adError.getMessage());
}
@Override
public void onRewardVideoAdLoad(PtgRewardVideoAd ad) {
Log.d(TAG, "激励视频 加载成功");
tempRewardVideoAd = ad;
}
@Override
public void onRewardVideoCached(PtgRewardVideoAd ad) {
// 视频素材预加载完成,建议在该回调后再调用 showRewardVideoAd 起播体验最佳。
// 等待已超过预期时长仍未触发,可调用 ad.isVideoCached() 实时校验缓存是否可用。
Log.d(TAG, "激励视频 缓存完成");
}
});
}
private void showRewardVideoAdvert() {
if (tempRewardVideoAd == null) {
Log.d(TAG, "no RewardVideoCached");
return;
}
// 可选:展示前再次校验视频缓存是否仍然可用(缓存可能因 LRU 被清理)
if (!tempRewardVideoAd.isVideoCached()) {
Log.d(TAG, "激励视频 缓存已失效,建议重新加载");
return;
}
tempRewardVideoAd.setRewardAdInteractionListener(new PtgRewardVideoAd.RewardAdInteractionListener() {
@Override
public void onAdShow() {
Log.d(TAG, "激励视频 展示");
}
@Override
public void onAdVideoBarClick() {
Log.d(TAG, "激励视频 点击");
}
@Override
public void onAdClose() {
Log.d(TAG, "激励视频 关闭");
}
@Override
public void onRenderError(AdError adError) {
Log.d(TAG, "激励视频 渲染失败");
}
@Override
public void onVideoComplete() {
Log.d(TAG, "激励视频 播放完成");
}
@Override
public void onVideoError(int i, String s) {
Log.d(TAG, "激励视频 视频错误");
}
@Override
public void onRewardVerify(boolean verify, Bundle extra) {
StringBuilder sb = new StringBuilder();
sb.append("奖励是否有效:").append(verify).append("\n");
if (extra != null) {
sb.append("---> 状态码:").append(extra.getInt(PtgRewardConstant.REWARD_PARAMS.SERVER_CODE)).append("\n");
sb.append("---> 状态信息:").append(extra.getString(PtgRewardConstant.REWARD_PARAMS.SERVER_MSG)).append("\n");
}
if (!verify) {
Log.d(TAG, "onRewardVerify: " + sb.toString());
} else {
if (extra != null) {
sb.append("---> 奖励名称:").append(extra.getString(PtgRewardConstant.REWARD_PARAMS.REWARD_NAME)).append("\n");
sb.append("---> 奖励数量:").append(extra.getInt(PtgRewardConstant.REWARD_PARAMS.REWARD_AMOUNT)).append("\n");
sb.append("---> 用户ID:").append(extra.getString(PtgRewardConstant.REWARD_PARAMS.USER_ID)).append("\n");
sb.append("---> 扩展参数:").append(extra.getString(PtgRewardConstant.REWARD_PARAMS.EXTRA)).append("\n");
}
Log.d(TAG, "视频奖励校验: " + sb.toString());
}
}
@Override
public void onSkippedVideo() {
Log.d(TAG, "onSkippedVideo");
}
@Override
public void onVideoStart() {
Log.d(TAG, "激励视频 onVideoStart");
}
@Override
public void onVideoPause() {
Log.d(TAG, "激励视频 onVideoPause");
}
@Override
public void onVideoResume() {
Log.d(TAG, "激励视频 onVideoResume");
}
@Override
public void onVideoProgressUpdate(long l, long l1) {
Log.d(TAG, "激励视频 onVideoProgressUpdate");
}
});
// 展示激励视频
tempRewardVideoAd.showRewardVideoAd(RewardVideoActivity.this);
// 销毁激励视频
@Override
protected void onDestroy() {
super.onDestroy();
// 销毁广告
if (tempRewardVideoAd != null) {
tempRewardVideoAd.destroy();
}
}
# *** ⚠️ 重要说明:媒体自行竞价必须通过广告对象回传竞价状态
【必须调用】务必在相应业务节点调用广告对象的以下方法:span>
| 方法 | 说明 | 参数 |
|---|---|---|
| notifyBidWin(double costPrice, double secondPrice) | 广告竞胜之后,在广告展示前调用回传竞价成功信息 | costPrice 单位:分 竞胜的价格 ,secondPrice 单位:分 第一位竞败的价格 |
| notifyBidLoss(AdBidLossReason bidReason) | 广告竞败之后,调用回传竞价失败信息 | bidReason 失败原因 |
# AdBidLossReason 失败信息说明:
| 方法 | 说明 | 参数 |
|---|---|---|
| setLossCode(String lossCode) | 竞败码 | BiddingConst.BIDDING_LOSS :1001 比普通常规广告的价格低(媒体自己底价),1002 比竞价广告的价格低,1003 竞价超时,如因长时间无竞价结果通知 MP SDK,1004 广告缓存过期,1005 其他 |
| setLossOtherReason(String lossOtherReason) | 竞败其他自定义原因 | 竞败其他自定义原因 当lossCode == BiddingConst.BIDDING_LOSS.WITH_OTHER 时传该参数 |
| setWinAdnId(String winAdnId) | 竞胜方渠道ID | BiddingConst.ADN_ID LOSE_TO_NORMAL_IN_SAME_ADN 输给了 同个广告平台的普通常规广告 LOSE_TO_HB_IN_SAME_ADN 输给了 同个广告平台的竞价广告 LOSE_TO_OWN_ADN 输给了 自有广告(直投广告、交叉推广) LOSE_TO_OTHER_ADN 输给了 其他广告平台 OTHER 其他 或具体消耗源 |
| setWinPrice(double winPrice) | 竞胜方价格, 单位:分/千次 | |
| setExtraParams(Map<String, String> extraParams) | 组装额外参数 | 可自定义 |
# 四、聚合广告平台
# 4.1 TopOn聚合平台自定义广告接入文档
Topon自定义广告接入地址:https://docs.toponad.com/#/zh-cn/android/NetworkAccess/customnetwork/customnetwork (opens new window)
接入方式:下载aar包集成 topon_mp_adapter-xxx.aar
# 自定义广告源adapter参数
| 广告类型 | 广告类名称 | 服务端配置参数 |
|---|---|---|
| 插屏 | com.ptg.ad.adapter.PtgInterstitialAdapter | "app_id" //媒体ID "app_key" //媒体key "app_name" //媒体名称 "slot_id" //广告位ID |
| 横幅 | com.ptg.ad.adapter.PtgBannerAdapter | "app_id" //媒体ID "app_key" //媒体key "app_name" //媒体名称 "slot_id" //广告位ID |
| 原生 | com.ptg.ad.adapter.PtgNativeAdapter | "app_id" //媒体ID "app_key" //媒体key "app_name" //媒体名称 "slot_id" //广告位ID "self_render": "0" //模板渲染 "self_render": "1"//自渲染 |
| 开屏 | com.ptg.ad.adapter.PtgSplashAdapter | "app_id" //媒体ID "app_key" //媒体key "app_name" //媒体名称 "slot_id" //广告位ID |
| 激励视频 | com.ptg.ad.adapter.PtgRewardVideoAdapter | "app_id" //媒体ID "app_key" //媒体key "app_name" //媒体名称 "slot_id" //广告位ID |
# 添加广告平台
进入TopOn后台,选择顶部‘广告平台’ -> '+广告平台', 添加[广告平台]&[Adapter类名]
# 创建广告源
进入TopOn后台,选择顶部‘聚合管理’ -> 选择'广告位' -> '+广告源',选择新增的广告源,输入参数即可
# 4.2 GroMore聚合平台自定义广告接入文档
接入方式:下载aar包集成 gromore_mp_adapter-xxx.aar
注:聚合版本可通过gromore的接口设置合规参数(TTAdConfig->setMediationConfig->getLocalExtra)
Map<String, Object> stringObjectMap = new HashMap<>();
stringObjectMap.put(PTGCustomerConfig.MEDIA_IS_CAN_APP_LIST, true);
stringObjectMap.put(PTGCustomerConfig.MEDIA_IS_CAN_ANDROID_ID, true);
stringObjectMap.put(PTGCustomerConfig.MEDIA_IS_CAN_OAID, true);
stringObjectMap.put(PTGCustomerConfig.MEDIA_IS_CAN_LOCATION, true);
// 添加 安装包列表
ArrayList<String> packageInfoList = new ArrayList<>();
packageInfoList.add("包名");
stringObjectMap.put(PTGCustomerConfig.MEDIA_APP_LIST, packageInfoList);
// 添加 AndroidId
stringObjectMap.put(PTGCustomerConfig.MEDIA_KEY_ANDROID_ID, "");
// 添加 Oaid
stringObjectMap.put(PTGCustomerConfig.MEDIA_OAID, "");
// 添加 当前Location
PTGLocation location = new PTGLocation();
location.setLatitudeDegrees(0.0d);
location.setLongitudeDegrees(0.0d);
stringObjectMap.put(PTGCustomerConfig.MEDIA_LOCATION, location);
# 自定义广告源adapter参数
| 广告类型 | 广告类名称 | 服务端配置参数 |
|---|---|---|
| 初始化 | com.ptg.gm.PTGCustomerConfig | |
| 插屏 | com.ptg.gm.PTGInterstitialAdapter | |
| 横幅 | com.ptg.gm.PTGBannerAdapter | |
| 原生 | com.ptg.gm.PTGNativeAdapter | "self_render": "0" //模板渲染 "self_render": "1"//自渲染 |
| 开屏 | com.ptg.gm.PTGSplashAdapter | |
| 激励 | com.ptg.gm.PTGRewardAdapter |
# 4.3 ToBid聚合平台自定义广告接入文档
接入方式:下载aar包集成 windmill-mp-adapter-xxx.aar
# 自定义广告源adapter参数
| 广告类型 | 广告类名称 | 服务端配置参数 |
|---|---|---|
| 初始化类 | com.windmill.ptg.PtgAdapterProxy | ToBid聚合平台,输入自定义广告网络应用维度参数 "app_id" //媒体ID "app_key" //媒体key "app_name" //媒体名称 |
| 开屏 | com.windmill.ptg.PtgSplashAdapter | ToBid聚合平台配置 “代码位ID” |
| 插屏 | com.windmill.ptg.PtgInterstitialAdapter | ToBid聚合平台配置 “代码位ID” |
| 原生 | com.windmill.ptg.PtgNativeAdapter | ToBid聚合平台配置 “代码位ID” "self_render": "0" //模板渲染 "self_render": "1"//自渲染 |
| 激励 | com.windmill.ptg.PtgRewardVideoAdapter | ToBid聚合平台配置 “代码位ID” |
| 横幅 | com.windmill.ptg.PtgBannerAdapter | ToBid聚合平台配置 “代码位ID” |
# 4.4 JiGuang聚合平台自定义广告接入文档
接入方式:下载aar包集成 jiguang_mp_adapter-xxx.aar
媒体仅需在极光后台添加我们的广告源和配置广告位即可
# 4.5 横幅与信息流宽高
横幅、信息流模板的期望宽高由各聚合平台传入 Adapter,Adapter 统一换成 dp 后调用 AdSlot.Builder.setExpressViewAcceptedDpSize(width, height)。
| 聚合平台 | 广告类型 | 参数 | 媒体传入单位 |
|---|---|---|---|
| TopOn | 横幅 | ATAdConst.KEY.AD_WIDTH、ATAdConst.KEY.AD_HEIGHT | px |
| TopOn | 信息流 | ATAdConst.KEY.AD_WIDTH、ATAdConst.KEY.AD_HEIGHT | px |
| ToBid | 横幅、信息流 | WMConstants.AD_WIDTH、WMConstants.AD_HEIGHT | dp |
| GroMore | 横幅、信息流 | AdSlot.setExpressViewAcceptedSize(width, height) | dp |
| 极光 | 信息流 | ADJgAdSize(getAdWidth / getAdHeight) | px |
TopOn 示例(单位 px):
int width = getResources().getDisplayMetrics().widthPixels;
int height = 0;
Map<String, Object> localMap = new HashMap<>();
localMap.put(ATAdConst.KEY.AD_WIDTH, width);
localMap.put(ATAdConst.KEY.AD_HEIGHT, height);
ToBid 示例(单位 dp):
Map<String, Object> options = new HashMap<>();
options.put(WMConstants.AD_WIDTH, ScreenUtils.getScreenWidthDp(context));
options.put(WMConstants.AD_HEIGHT, 0);
GroMore 示例(模板宽高单位 dp):
new AdSlot.Builder()
.setExpressViewAcceptedSize(ScreenUtils.getScreenWidthDp(context), 0)
.build();
极光信息流示例(单位 px):
int widthPixels = getResources().getDisplayMetrics().widthPixels;
ADJgExtraParams extraParams = new ADJgExtraParams.Builder()
.adSize(new ADJgAdSize(widthPixels, 0))
.build();
# 五、Android SDK 支持微信小程序/小游戏跳转接入方案
# 简介
微信小程序/小游戏广告,是指用户点击广告后,将跳转至微信打开微信小程序/小游戏,在微信内部发生后续的行为转化。
# 适用场景:
适用于SDK当前的多种广告样式,接入后可有效提升流量的填充和CPM值。
# 整体接入流程
- 进入微信开放平台创建移动应用
- 应用创建完成后,在微信开放平台获取到相应的AppID
- 在移动端嵌入最新版微信openSDK(>=5.3.1)(仅嵌入即可openSDK 接入指南 (opens new window),无需额外开发工作 )
- 在开发者平台,将微信开放平台填写的AppID 与当前应用进行关联
步骤:媒体管理 - 选择媒体 - 编辑 - 填写微信AppID
# 六、异常排查
# 1、Debug 日志排查
new PtgSDKConfig.Builder()
.setDebug(true)
.build();
在 SDK 进行初始化的时候,可以通过 setDebug 打开日志开关。
在 Logcat 中可以通过搜索 ptg 关键字过滤 SDK 中打印的日志信息
# 2、错误码
SDK中定义的错误码及其描述如下:
| 错误码 | 描述 |
|---|---|
| 10001 | 无广告填充/无广告返回 |
| 10002 | 广告数据异常 |
| 10003 | 参数错误 |
| 10004 | 请求超时 |
| 10005 | 网络异常 |
| 10006 | SDK初始化异常 |
| 10011 | 未知错误 |
| 10012 | 广告已过期 |
| 10013 | 消耗源为空 |
| 10014 | 资源错误 |
| 10015 | 展示失败 |
| 10016 | 渲染失败 |
| 10017 | 资源异常 |
| 10018 | 视频播放错误 |
| 10019 | 内部错误 |
| 10020 | 无匹配策略 |
| 10021 | 广告过滤 |
| 10023 | 请求频繁 |
| 10024 | 资源下载失败 |
| 10026 | 低于底价 |
| 10027 | 广告形式不合法 |
| 10036 | 数据异常 |
| 10040 | 消耗源异常 |
| 10044 | 广告形式不支持 |
错误码分类说明:
- 100xx:SDK相关错误(初始化、参数、广告加载等)
- 400xx:网络相关错误(请求、响应、连接等)
常见错误处理建议:
- 10001(无广告填充):检查广告位配置、底价设置、流量填充情况
- 10003(参数错误):确认MediaId和MediaSecret配置正确
- 10004(请求超时):检查网络连接状态,适当调整超时时间
- 10005(网络异常):检查网络连接状态和网络权限
- 10007(SDK未初始化):确保在请求广告前完成SDK初始化
- 10016(渲染失败):检查广告容器设置和Activity上下文
- 10017(资源异常):检查素材加载情况,可能需要重新请求广告
- 10026(低于底价):调整广告位底价设置或优化流量质量
- 10027(样式不合法):联系平台处理,确认广告位类型配置
# 七、验收
为了保证对接,建议接入完成后提供测试包给我方人员进行验收。
所提供的验收包,需要满足以下条件:
- 需要打开 debug 日志开关
- 需要对 Log 不进行混淆配置优化
# 八、其他问题
本文档提供产品和运营人员在使用过程中遇到的常见问题和解决方案。
# 1、常见问题
1.1.为什么要经常更新广告SDK?
会在每次更新迭代中进行功能和稳定性优化。同时也会及时对三方广告平台的最新版SDK进行适配。保持更新除了稳定性和性能提升外。有些广告平台SDK版本维护时间超出后将不再做任何优化、修复等维护操作; 有些广告平台对最新版广告SDK会填充和收益方面的倾斜。
1.2.Android对接时设备请求权限该怎么设置? 获取设备识别码和状态(READ_PHONE_STATE)权限和 定位权限(ACCESS_COARSE_LOCATION),不再强制要求,媒体可根据自身业务需求自行处理。其余权限要求请参考对接文档。
1.3.App中的广告样式比较特殊,MediaPrime 提供的样式不支持该如何处理? 目前主流广告平台都是提供标准样式和比例的广告, 非主流广告位可能会出现裁剪,遮挡的等情况影响广告美观度和收益。 若需要对接建议选用样式比例相近或使用原生信息流封装渲染。请查看广告平台广告类型样式
1.4.为什么在SDK数据中某些广告位的展示数会比请求数更多呢?
1> 若是原生信息流,则有可能一次请求多条广告,属于正常情况,可以向对接的技术人询问。
2> 若是其它情况,可能是重复展示曝光等异常情况,请及时排查原因。
1.5.是否支持上架Google Play?
不支持,建议单独做一个渠道包给Google Play用。
1.6.为什么广告位的填充率很高但是展示率很低?
1> 服务端拉取数据成功后,本地未播放( 播放了其它来源广告、广告管理异常)
2> 广告播放未满足曝光标准
1.7.填充率下降是什么原因?
答:填充率下降,建议按以下原因中自查优化
1> 广告位设置:设置底价、过滤字段、屏蔽包名、先审后播等条件有关;
2> 广告展示率低:预加载时机不合适/预加载了过多广告,请求成功但是不展示,展示率小于10%时,填充率容易下降;
3> 广告效果:广告位转化效果差,广告主不愿意在该位置出价;
1.8.SDK曝光数据与广告平台数据差异如何排查?
答: 广告平台后台判罚策略:20%以内的数据差异属于合理范围。超过该范围请联系商务人员或直接联系广告平台人工客服
1.9.SDK点击数据与广告平台数据差异如何排查?
答: 广告平台后台判罚策略:20%以内的数据差异属于合理范围。超过该范围请联系商务人员或直接联系广告平台人工客服
1.10.模板广告和自渲染广告有啥区别?
模板广告 App会“收到”一个完整的view对象,开发者无法自定义广告view内部的组件布局(文字TextView、图片ImageView),而原生广告自渲染方式支持开发者自由拼合这些素材,最大程度的满足开发需求;与原生广告(模板方式)相比,自渲染方式更加自由灵活。
1.11.OAID是否必须接入,不接入会有什么影响
OAID在广告投放作为匿名广告设备标识符《移动智能终端补充设备标识体系》,是广告平台用移动设备的“门牌号”。如不提供将严重影响广告效果,降低广告收益。
# 2:激励视频交互方式简介&奖励方法说明
背景 为满足开发者对激励视频奖励发放逻辑处理,SDK广告变现为开发者提供了客户端和服务端回调校验功能,开发者可遵循此文档进行相关配置和开发,基于此功能开发者可对满足奖励发放条件的用户进行发放奖励。
2.1.奖励发放条件 一般视频时长为5~60s,可通过观看足够视频时长获取奖励,奖励回调下发时即成功领奖。
2.2.奖励回调方法说明
客户端回调: SDK根据“奖励发放条件”,直接给到开发者客户端是否发放奖励的回调,故对接相对简单。
服务端回调:
- 开启服务端校验需要在后台配置媒体提供的校验地址。
- 服务器回调模式不是必须的,只是增加了一次第三方服务器的验证判断。具体的奖励发放由客户端完成。
- SDK服务端只是透传验证请求,不会在中间过程添加校验逻辑。为了保障开发者利益和用户体验,开发者可以在验证环节增加自己的校验逻辑。
- 当用户观看时长满足“ 奖励发放条件 ”,先通过“SDK服务端”访问“开发者服务端”向开发者确认是否进行奖励发放,再依据“开发者服务端”返回的 true/false,在客户端给出是/否发放奖励。
← 帮助中心 Android 更新日志 →