# mp_demo_android **Repository Path**: jun8n/mp_demo_android ## Basic Information - **Project Name**: mp_demo_android - **Description**: MP-SDK-Android - **Primary Language**: Android - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-04-11 - **Last Updated**: 2026-01-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Android SDK 接入文档 #### 注意: * 创建应用并拿到广告SDK所需的参数。 * 获取并解压我们提供的压缩包,把 *.aar 放入app的libs工程中。 * SDK 支持 Android API Level 16+。 * Support版本支持需联系相关人员获取。 ## 一、导入SDK依赖 [点击查看Android示例Demo](https://gitee.com/jun8n/mp_demo_android.git) ### 1.1 本地aar依赖 1、在 app 目录下新建 libs 文件夹 2、将压缩包中的 aar 文件放入 libs 文件夹 3、在 app/build.gradle 添加如下代码 ```auto repositories { flatDir { dirs 'libs' } } dependencies { // SDK AAR文件 放入项目libs中 implementation fileTree(include: ['*.aar'], dir: 'libs') // 或直接引用 // implementation files('./libs/mp_xxx.aar') // 添加其他消耗源 & 适配器 // implementation files('./libs/xxx.aar') // implementation files('./libs/xxx-mp-adapter.aar') } ``` 4、其他消耗源 & 适配器 说明 (注意版本号xxx修改成实际版本号) ```auto // 优酷 SDK implementation files('./libs/OneAdSDK_vxxx.aar') // 优酷 适配器 implementation files('./libs/OneAdSDK_vxxx_mp_adapter.aar') // UBIX SDK implementation files('./libs/UBiX_Merak.SDK_xxx.aar') // UBIX 适配器 implementation files('./libs/UBiX_Merak.SDK_xxx_mp_adapter.aar') ``` ### 1.2 AndroidManifest 配置 ``` java ``` ### 1.3 混淆配置 #### 注意事项: 如果 tools.build:gradle 版本小于 4.1.0,请检查自定义混淆规则或 proguard-android-optimize,确认混淆规则中没有使用 -allowaccessmodification 属性 ### 1.4 媒体需要接入信通院 OAID-SDK Android10 及以上设备需要 `OAID` 的支持才能正常进行收益计费 [移动安全联盟 MSA 官网](https://msa-alliance.cn/col.jsp?id=120) [https://msa-alliance.cn/col.jsp?id=120](https://msa-alliance.cn/col.jsp?id=120) 注意:如果使用自定义OAID, 则需要确保 OAID 的合法性,否则将影响广告获取和收益 ## 二、接入配置 ### 2.1 初始化SDK 提示:开发者需要在符合网信办合规要求的前提下进行SDK的初始化; 注意:默认仅支持初始化SDK一次,多次初始化SDK以第一次初始化为准。 ```auto // 初始化广告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 隐私信息控制开关 注意:接入时请实现以下控制开关,否则影响广告收益 ```java private static PtgCustomController buildCustomController(Application application) { String TAG = "CustomControllerTag"; return new PtgCustomController() { String tempOaid = ""; AdLocation tempLocation; List tempPackageList; /// 是否允许SDK主动使用手机参数,imei等 @Override public boolean isAllowSDKObtainPhoneInfo() { Log.d(TAG, "isAllowSDKObtainPhoneInfo"); // return super.isAllowSDKObtainPhoneInfo(); return false; } /// 当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 DeviceInfoHelper.getInstance().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(); if (TextUtils.isEmpty(tempOaid)) { tempOaid = OaidManager.getOaid(application, new OaidManager.OaidListener() { @Override public void callBack(String oaid) { tempOaid = oaid; } }); } // FIXME:这里建议媒体自行获取OAID回传给SDK return tempOaid; } /// 是否 SDK 主动获取 mac_address @Override public boolean isAllowSDKObtainMacAddress() { Log.d(TAG, "isAllowSDKObtainMacAddress"); // return super.isAllowSDKObtainMacAddress(); return false; } /// 当isAllowSDKObtainMacAddress = false 时,可以传入mac 地址信息,sdk 使用传入的Mac地址信息 @Override public String getMediaMacAddress() { Log.d(TAG, "getMediaMacAddress"); // return super.getMediaMacAddress(); return DeviceInfoHelper.getInstance().getMacAddress(application); } /// 是否允许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(); LocationUtils.getLocation(application, new LocationUtils.ICallback() { @Override public void onResult(LocationUtils.LocationBean result) { AdLocation location = new AdLocation(); location.setLatitude(result.getLatitude()); location.setLongitude(result.getLongitude()); tempLocation = location; } @Override public void onError(Throwable error) { } }); // FIXME:这里建议媒体自行获取Location回传给SDK return tempLocation; } /// 是否允许SDK主动获取设备上应用安装列表的采集权限 @Override public boolean isAllowSDKInstallList() { Log.d(TAG, "isAllowSDKInstallList"); // return super.isAllowSDKInstallList(); return false; } /// 当 isAllowSDKInstallList =false 可传入packages 信息,SDK使用传入的 packages list @Override public List getMediaInstalledPackages() { Log.d(TAG, "getMediaInstalledPackages"); // return super.getMediaInstalledPackages(); // if (tempPackageList == null || tempPackageList.isEmpty()) { // tempPackageList = PackageInstallHelper.getPackageList(application); // } 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说明** 注意:以下方法为同步方法,媒体实现时需考虑代码执行时间及持久化处理 | 方法 | 说明 | | --- | --- | | boolean isAllowSDKObtainPhoneInfo() | 是否允许SDK主动使用手机参数、imei等,默认:true | | String getMediaDeviceImei() | 当isAllowSDKObtainPhoneInfo = false时,开发者可传入imei信息| | boolean isAllowSDKObtainAndroidId() | 是否允许SDK主动获取ANDROID_ID,默认:true | | String getMediaAndroidId() | 当 isAllowSDKObtainPhoneInfo =false 时 ,开发者可以传入 android_id 信息 | | boolean isAllowSDKObtainOaId() | 是否允许SDK 获取 oaid,默认:true | | String getMediaDeviceOaId() | 开发者可以传入oaid,信通院OAID的相关采集——如何获取OAID:
1. 移动安全联盟官网http://www.msa-alliance.cn/
2. 信通院统一SDK下载http://msa-alliance.cn/col.jsp?id=120 | | boolean isAllowSDKObtainWriteExternal() | 是否允许SDK主动使用WRITE_EXTERNAL_STORAGE权限, 默认:true | | boolean isAllowSDKObtainLocation() | 是否允许SDK主动使用地理位置信息,默认:false | | AdLocation getMediaLocation() | 当isAllowSDKObtainLocation =false时,可传入地理位置信息 | ## 三、广告位接入 ### 3.1 开屏广告 > 参考[Demo](https://gitee.com/jun8n/mp_demo_android/blob/master/app/src/main/java/com/fancy/demo/ui/splash/SplashFragment.java)中 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 |否| #### 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:未完成或过期 |是| | showAd(ViewGroup var1) | 展示广告,传入容器 |是| | destroy() | 销毁广告 |是| #### 3.1.5 开屏广告监听器 **PtgSplashAd.AdInteractionListener** | 回调方法 | 说明 | | --- | --- | | onAdClicked() | 点击 | | onAdShow() | 展示 | | onAdSkip() | 点击跳过 | | onAdTimeOver() | 倒计时结束 | | onRenderError(AdError adError)| 渲染失败 | #### 3.1.6 示例代码 ```java // 请求广告 // 注意** 请确保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(); } } }); ``` ```java // 销毁广告 @Override public void onDestroy() { super.onDestroy(); // PtgSplashAd splashAd; closeProtection(); if (tempPtgSplashAd != null) { tempPtgSplashAd.destroy(); } } ``` ### 3.2 插屏广告 > 参考[Demo](https://gitee.com/jun8n/mp_demo_android/blob/master/app/src/main/java/com/fancy/demo/ui/InteractionActivity.java)中的 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 示例代码 ```java // 请求广告 // 创建广告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](https://gitee.com/jun8n/mp_demo_android/blob/master/app/src/main/java/com/fancy/demo/ui/FeedActivity.java)中 FeedActivity、FeedListActivity类。 > 自渲染广告: SDK返回物料,建议由开发者根据自己的广告设计自行UI适配和展示。 #### 3.3.1 原生广告位 **AdSlot.Builder** | 方法名 | 说明 | 必填 | | --- | --- | --- | | setPtgSlotId(String slotId) | 广告位ID |是| | 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() | 模板&自渲染展示广告 |是| | ~~render(View view)~~| ~~自渲染展示广告,传入自定义广告容器~~ |~~是~~| | getExpressAdView() | 获取最终广告视图,模板&自渲染均从此方法获取,建议render之后添加到容器 |是| |isSelfRender()| 是否是自渲染模式| 是 | | getEcpm()|获取ecpm价格,单位:分|否| | registerAdvertViews(ViewGroup nativeView, List clickViewList, List creativeViewList, View nativeAdClose)|自渲染绑定布局
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信息 | | getBrand() | 品牌信息 | | getLogo()| logo图片 | | ~~getExt()~~ | 物料的附加信息 | | ~~getPrice()~~ | 出价,单位:分 | 说明:新版将图片封装到了getImageList,旧方法不影响使用 #### 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 示例代码 ```java // 加载广告 // 创建广告AdSlot,用于请求广告前,传递部分广告配置参数。 AdSlot adSlot = new AdSlot.Builder() .setPtgSlotId(AdvertContract.AD_ID_FEED) .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(); ``` ```java // 销毁广告 @Override protected void onDestroy() { super.onDestroy(); // 销毁广告 destroyAd(); } // 销毁广告(注意在不使用时或页面销毁时销毁广告,否则或出现内存问题) private void destroyAd() { if (tempPtgNativeExpressAd != null) { tempPtgNativeExpressAd.destroy(); tempPtgNativeExpressAd = null; } } ``` #### 3.3.9 自渲染帮助类方法(AppNativeSelfUtils.renderCommonAd)(仅供参考) ```java /** * 渲染普通自渲染广告 * * @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 clickViewList = new ArrayList<>(); clickViewList.add(native_ad_container); clickViewList.add(native_detail_btn); clickViewList.add(native_detail_tv); ArrayList 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 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](https://gitee.com/jun8n/mp_demo_android/blob/master/app/src/main/java/com/fancy/demo/ui/BannerActivity.java)中 BannerActivity 类 #### 3.4.1 横幅广告位 **AdSlot.Builder** | 方法名 | 说明 | 必填 | | --- | --- | --- | | setPtgSlotId(String slotId) | 广告位ID |是| #### 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 示例代码 ```java // 请求横幅广告 // 创建横幅广告AdSlot,用于请求广告前,传递部分广告配置参数。 AdSlot adSlot = new AdSlot.Builder() .setPtgSlotId(AdvertContract.AD_ID_BANNER) // 广告位id,此处替换为媒体分配的广告位id .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(); } }); ``` ```java // 销毁横幅广告 @Override protected void onDestroy() { super.onDestroy(); // 媒体根据时机,销毁广告 if (tempNativeExpressAd != null) { tempNativeExpressAd.destroy(); } } ``` ### 3.5 激励视频广告 > 参考[Demo](https://gitee.com/jun8n/mp_demo_android/blob/master/app/src/main/java/com/fancy/demo/ui/RewardVideoActivity.java)中的 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 为广告对象 | | onError(AdError adError) | 广告加载失败,AdError 错误信息 | #### 3.5.4 激励视频广告对象 **PtgInteractionAd** | 方法 | 说明 | 是否必须实现 | | --- | --- | --- | | setRewardAdInteractionListener
(PtgRewardVideoAd.RewardAdInteractionListener rewardAdInteractionListener)| 设置广告监听器 |是| | setDownloadListener(PtgAppDownloadListener ptgAppDownloadListener)| 设置下载监听器 |否| | isReady() | 用于判断广告是否已经是准备完成的状态
true:准备完成 false:未完成或过期 |是| | 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 示例代码 ```java // 请求激励视频 // 创建广告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; } }); } private void showRewardVideoAdvert() { if (tempRewardVideoAd == null) { Log.d(TAG, "no RewardVideoCached"); 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); ``` ```java // 销毁激励视频 @Override protected void onDestroy() { super.onDestroy(); // 销毁广告 if (tempRewardVideoAd != null) { tempRewardVideoAd.destroy(); } } ``` ## ***注意: 媒体自行竞价需通过广告对象回传媒体竞价状态: #### 对应时机调用广告对象的如下方法 | 方法 | 说明 |参数| | --- | --- |--- | | 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 extraParams) | 组装额外参数 | 可自定义 | ## 四、聚合广告平台 ### 4.1 TopOn聚合平台自定义广告接入文档 Topon自定义广告接入地址:[https://docs.toponad.com/#/zh-cn/android/NetworkAccess/customnetwork/customnetwork](https://docs.toponad.com/#/zh-cn/android/NetworkAccess/customnetwork/customnetwork) **接入方式:下载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) ```java Map 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 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** **媒体仅需在极光后台添加我们的广告源和配置广告位即可** ## 五、Android SDK 支持微信小程序/小游戏跳转接入方案 ### 简介 微信小程序/小游戏广告,是指用户点击广告后,将跳转至微信打开微信小程序/小游戏,在微信内部发生后续的行为转化。 #### 适用场景: 适用于SDK当前的多种广告样式,接入后可有效提升流量的填充和CPM值。 #### 整体接入流程 1. 进入微信开放平台创建移动应用 2. 应用创建完成后,在微信开放平台获取到相应的AppID 3. 在移动端嵌入最新版微信openSDK(>=5.3.1)(仅嵌入即可[openSDK 接入指南](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Launching_a_Mini_Program/Android_Development_example.html),无需额外开发工作 ) 4. 在开发者平台,将微信开放平台填写的AppID 与当前应用进行关联 步骤:媒体管理 - 选择媒体 - 编辑 - 填写微信AppID ## 六、异常排查 ### debug 日志排查 ```java new PtgSDKConfig.Builder() .setDebug(true) .build(); ``` 在 SDK 进行初始化的时候,可以通过 **`setDebug`** 打开日志开关。 在 **`Logcat`** 中可以通过搜索 **`ptg`** 关键字过滤 `SDK` 中打印的日志信息 ## 七、验收 **为了保证对接,建议接入完成后提供测试包给我方人员进行验收。** 所提供的验收包,需要满足以下条件: + **需要打开 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,在客户端给出是/否发放奖励。