一、SDK接入
參考官方接入文檔,基于穿山甲版本號: 3.1.0.0
導(dǎo)入 aar 及 SDK 依賴的 jar 包
將本 SDK 壓縮包內(nèi)的 open_ad_sdk.aar 復(fù)制到 Application Module/libs 文件夾(沒有的話須手動創(chuàng)建), 并將以下代碼添加到您 app 的
repositories {
flatDir {
dirs 'libs'
}
}
depedencies {
compile(name: 'open_ad_sdk', ext: ‘a(chǎn)ar')
}
添加權(quán)限
穿山甲SDK建議您添加下述權(quán)限,并建議在您的隱私協(xié)議中向開發(fā)者聲明穿山甲SDK會獲取下述權(quán)限并應(yīng)用于廣告投放。
<!--必要權(quán)限-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--可選權(quán)限-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.GET_TASKS"/>
<!--可選,穿山甲提供“獲取地理位置權(quán)限”和“不給予地理位置權(quán)限,開發(fā)者傳入地理位置參數(shù)”兩種方式上報用戶位置,兩種方式均可不選,添加位置權(quán)限或參數(shù)將幫助投放定位廣告-->
<!--請注意:無論通過何種方式提供給穿山甲用戶地理位置,均需向用戶聲明地理位置權(quán)限將應(yīng)用于穿山甲廣告投放,穿山甲不強(qiáng)制獲取地理位置信息-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 如果有視頻相關(guān)的廣告且使用textureView播放,請務(wù)必添加,否則黑屏 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
注意:穿山甲SDK不強(qiáng)制獲取以上權(quán)限,即使沒有獲取可選權(quán)限SDK也能正常運(yùn)行;獲取以上權(quán)限將幫助穿山甲優(yōu)化投放廣告精準(zhǔn)度和用戶的交互體驗(yàn),提高eCPM。
為獲取更好的廣告推薦效果,以及提高激勵視頻廣告、下載類廣告等填充率,建議在廣告請求前,合適的時機(jī)調(diào)用 SDK 提供的方法,如在用戶第一次啟動您的 app 后的主界面時調(diào)用如下方法:
TTAdManager接口中的方法,context可以是Activity或Application
void requestPermissionIfNecessary(Context context);
適配 Android7.0 及以上
如果您的應(yīng)用需要在 Android7.0 及以上環(huán)境運(yùn)行,請?jiān)?AndroidManifest 中添加如下代碼:
<provider
android:name="com.bytedance.sdk.openadsdk.TTFileProvider"
android:authorities="${applicationId}.TTFileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
在 res/xml 目錄下,新建一個 xml 文件 file_paths,在該文件中添加如下代碼:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="tt_external_root" path="." />
<external-path name="tt_external_download" path="Download" />
<external-files-path name="tt_external_files_download" path="Download" />
<files-path name="tt_internal_file_download" path="Download" />
<cache-path name="tt_internal_cache_download" path="Download" />
</paths>
為了適配下載和安裝相關(guān)功能,在工程中引用的包 com.android.support:support-v4:24.2.0
使用24.2.0以及以上版本。
provider 配置
注意:單進(jìn)程或多進(jìn)程都必須配置
<provider
android:name="com.bytedance.sdk.openadsdk.multipro.TTMultiProvider"
android:authorities="${applicationId}.TTMultiProvider"
android:exported="false" />
代碼混淆配置
如果您需要使用 proguard 混淆代碼,需確保不要混淆 SDK 的代碼。 請?jiān)?proguard.cfg 文件(或其他混淆文件)尾部添加如下配置:
-keep class com.bytedance.sdk.openadsdk.** { *; }
-keep public interface com.bytedance.sdk.openadsdk.downloadnew.** {*;}
-keep class com.pgl.sys.ces.* {*;}
注意
SDK 代碼被混淆后會導(dǎo)致廣告無法展現(xiàn)或者其它異常。
本 SDK 最低支持 Android4.0 (API Level 14) 及以上版本。
targetSdkVersion API 23及以上,需要動態(tài)獲取權(quán)限,所以請確保調(diào)用本SDK的任何接口前,已經(jīng)申請到了SDK要求的所有權(quán)限,否則SDK部分特性可能受限。
SDK中使用的so文件支持五種架構(gòu):x86,x86_64,armeabi,armeabi-v7a,arm64-v8a 如果您應(yīng)用中支持的架構(gòu)超出這 五種,請?jiān)赽uild.gradle中使用abiFilters選擇支持的架構(gòu)。
ndk { // 設(shè)置支持的 SO 庫構(gòu)架,注意這里要根據(jù)你的實(shí)際情況來設(shè)置
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'armeabi'
}
SDK 初始化配置
開發(fā)者需要在 Application#onCreate()方法中調(diào)用以下代碼來初始化穿山甲 sdk。 目前 sdk 已支持多進(jìn)程,必須在所有進(jìn)程都初始化!
public class DemoApplication extends Application {
public static String PROCESS_NAME_XXXX = "process_name_xxxx";
@Override
public void onCreate() {
super.onCreate();
//強(qiáng)烈建議在應(yīng)用對應(yīng)的Application#onCreate()方法中調(diào)用,避免出現(xiàn)content為null的異常
TTAdSdk.init(context,
new TTAdConfig.Builder()
.appId("5001121")
.useTextureView(false) //使用TextureView控件播放視頻,默認(rèn)為SurfaceView,當(dāng)有SurfaceView沖突的場景,可以使用TextureView
.appName("APP測試媒體")
.titleBarTheme(TTAdConstant.TITLE_BAR_THEME_DARK)
.allowShowNotify(true) //是否允許sdk展示通知欄提示
.allowShowPageWhenScreenLock(true) //是否在鎖屏場景支持展示廣告落地頁
.debug(true) //測試階段打開,可以通過日志排查問題,上線時去除該調(diào)用
.directDownloadNetworkType(TTAdConstant.NETWORK_STATE_WIFI, TTAdConstant.NETWORK_STATE_4G) //允許直接下載的網(wǎng)絡(luò)狀態(tài)集合
.supportMultiProcess(false) //是否支持多進(jìn)程,true支持
//.httpStack(new MyOkStack3())//自定義網(wǎng)絡(luò)庫,demo中給出了okhttp3版本的樣例,其余請自行開發(fā)或者咨詢工作人員。
.build());
}
}
初始化接口說明
/**
*穿山甲sdk初始化入口
*
* @param context 必須是application context
* @param config 初始化配置信息,必要參數(shù)
* @return TTAdManager實(shí)例
*/
public static TTAdManager init(Context context, TTAdConfig config);
初始化配置參數(shù)說明:
public static class TTAdConfig.Builder {
private String mAppId;// 必選參數(shù),設(shè)置應(yīng)用的AppId
private String mAppName;// 必選參數(shù),設(shè)置應(yīng)用名稱
private boolean mIsPaid = false;// 可選參數(shù),設(shè)置是否為計(jì)費(fèi)用戶:true計(jì)費(fèi)用戶、false非計(jì)費(fèi)用戶。默認(rèn)為false非計(jì)費(fèi)用戶
private int mGender = TTAdConstant.GENDER_UNKNOWN;// 可選參數(shù),設(shè)置用戶性別。默認(rèn)為未知TTAdConstant#GENDER_UNKNOWN
private int mAge;// 可選參數(shù),設(shè)置用戶年齡 **須大于0**
private String mKeywords;// 可選參數(shù),設(shè)置用戶畫像的關(guān)鍵詞列表 **不能超過為1000個字符**
private String mData;// 可選參數(shù),設(shè)置額外的用戶信息 **不能超過為1000個字符**
private int mTitleBarTheme = TTAdConstant.TITLE_BAR_THEME_LIGHT;// 可選參數(shù),設(shè)置落地頁主題,默認(rèn)為TTAdConstant#TITLE_BAR_THEME_LIGHT
private boolean mAllowShowNotify = true;// 可選參數(shù),設(shè)置是否允許SDK彈出通知:true允許、false禁止。默認(rèn)為true允許
private boolean mIsDebug = false;// 可選參數(shù),是否打開debug調(diào)試信息輸出:true打開、false關(guān)閉。默認(rèn)false關(guān)閉
private boolean mAllowShowPageWhenScreenLock = false;// 可選參數(shù),設(shè)置是否允許落地頁出現(xiàn)在鎖屏上面:true允許、false禁止。默認(rèn)為false禁止
private int[] mDirectDownloadNetworkType;
private boolean mIsUseTextureView = false;// 可選參數(shù),設(shè)置是否使用texture播放視頻:true使用、false不使用。默認(rèn)為false不使用(使用的是surface)
private boolean mIsSupportMultiProcess = false;// 可選參數(shù),設(shè)置是否支持多進(jìn)程:true支持、false不支持。默認(rèn)為false不支持
private IHttpStack mHttpStack;//可選參數(shù),設(shè)置外部網(wǎng)絡(luò)請求,默認(rèn)為urlconnection
private boolean mIsAsyncInit = false;//是否異步初始化sdk
private TTCustomController mCustomController;//可選參數(shù),可以設(shè)置隱私信息控制開關(guān)
}
二、加載廣告
創(chuàng)建廣告插槽。
- 構(gòu)建TTAdManager對象
TTAdManager對象為整個SDK的入口接口,可用于廣告獲取、權(quán)限請求、版本號獲取
TTAdManager ttAdManager = TTAdManagerHolder.get();
- 申請部分權(quán)限 (建議每次廣告對象加載之后都需要申請一次 ps:開屏視頻不需要)
TTAdManagerHolder.get().requestPermissionIfNecessary(this);
- 創(chuàng)建TTAdNative對象(用于調(diào)用廣告請求接口)
TTAdNative mTTAdNative = ttAdManager.createAdNative(getApplicationContext());
- 構(gòu)建AdSlot對象(廣告插槽),具體構(gòu)建參數(shù)根據(jù)文檔來。
AdSlot adSlot = new AdSlot.Builder()
.setCodeId("廣告位id") //必選參數(shù) 設(shè)置您的CodeId
.setSupportDeepLink(true)
.setAdCount(1) //請求廣告數(shù)量為1到3條
.setExpressViewAcceptedSize(expressViewWidth,expressViewHeight) //期望模板廣告view的size,單位dp
.setImageAcceptedSize(350 ,500 )//這個參數(shù)設(shè)置即可,不影響模板廣告的size
.build();
在創(chuàng)建廣告插槽的時候有一個必選參數(shù)廣告位id,那么這個值是從哪里來的,又有什么作用呢?
廣告類型
想要了解廣告位id從哪里來的,我們首先需要打開穿山甲廣告的管理后臺:
創(chuàng)建新的應(yīng)用:
建議為測試集成完畢后再把應(yīng)用及對應(yīng)的代碼位改成正式狀態(tài)。
應(yīng)用ID 是對應(yīng)demo中的APPID。

創(chuàng)建新的代碼位:
廣告位ID 對應(yīng)demo中的CodeId。就是一個標(biāo)識用來加載不同種類的廣告類型。
應(yīng)用ID和代碼位ID是對應(yīng)關(guān)系 一個應(yīng)用可以對應(yīng)多個代碼位ID。
注意:新建的廣告CodeId,要等一會兒使用,否則就會出現(xiàn)ID無效。
點(diǎn)擊新創(chuàng)建的應(yīng)用的新建代碼位:

可以看到有多種樣式的廣告進(jìn)行選擇。
例如我想要創(chuàng)建一個banner廣告,則進(jìn)入bannner廣告創(chuàng)建頁面。

可以看到對應(yīng)的選項(xiàng)。
原生、自渲染和模板渲染的區(qū)別
- 自渲染和原生渲染(已經(jīng)被廢棄了):
現(xiàn)在已經(jīng)被廢棄了,簡單來說,就是穿山甲返回廣告的子元素,如標(biāo)題,描述,圖片等,你自己拼裝成自己喜歡的界面展示。 - 模板渲染廣告:
目前申請的廣告樣式只有這一類,廣告用webview(網(wǎng)頁)展示。接入者在穿山甲后臺,選擇自己需要的樣式進(jìn)行顯示就可以,如:上圖下文,左圖右文等多種形式。也就是穿山甲會直接返回一個view 不支持獲取view的信息 開發(fā)者直接展示view即可。
三、展示廣告
開屏廣告
開屏廣告建議為用戶在進(jìn)入 App 時展示的全屏廣告。開屏廣告為一個 View,寬高默認(rèn)為 match_parent,注意開屏廣告 view:width >=70%屏幕寬;height >=50%屏幕高 ,否則會影響計(jì)費(fèi)。

TTAdNative adNative = TTAdManagerHolder.INSTANCE.get().createAdNative(this);
AdSlot adSlot = new AdSlot.Builder()
.setCodeId(CodeId)
.setSupportDeepLink(true)
.setImageAcceptedSize(DisplayUtils.getScreenWidth(this), DisplayUtils.getScreenHeight(this))
.build();
//加載
adNative.loadSplashAd(adSlot, new TTAdNative.SplashAdListener() {
@Override
public void onError(int code, String message) {
Log.d(TAG, "開屏廣告請求失敗" + message);
dumpNextPage();
}
@Override
public void onTimeout() {
Log.d(TAG, "開屏廣告請求超時");
dumpNextPage();
}
@Override
public void onSplashAdLoad(TTSplashAd ad) {
Log.d(TAG, "開屏廣告請求成功");
if (ad == null) {
dumpNextPage();
return;
}
//獲取SplashView
View view = ad.getSplashView();
if (view != null && videoContainer != null && !LaunchActivity.this.isFinishing()) {
videoContainer.removeAllViews();
//把SplashView 添加到ViewGroup中,注意開屏廣告view:width >=70%屏幕寬;height >=50%屏幕高
videoContainer.addView(view);
//設(shè)置不開啟開屏廣告倒計(jì)時功能以及不顯示跳過按鈕,如果這么設(shè)置,您需要自定義倒計(jì)時邏輯
//ad.setNotAllowSdkCountdown();
} else {
dumpNextPage();
}
//設(shè)置SplashView的交互監(jiān)聽器
ad.setSplashInteractionListener(new TTSplashAd.AdInteractionListener() {
@Override
public void onAdClicked(View view, int type) {
Log.d(TAG, "onAdClicked開屏廣告點(diǎn)擊");
}
@Override
public void onAdShow(View view, int type) {
Log.d(TAG, "onAdShow開屏廣告展示");
}
@Override
public void onAdSkip() {
Log.d(TAG, "onAdSkip開屏廣告跳過");
dumpNextPage();
}
@Override
public void onAdTimeOver() {
Log.d(TAG, "onAdTimeOver開屏廣告倒計(jì)時結(jié)束");
dumpNextPage();
}
});
//廣告交互類型未下載廣告,需要設(shè)置廣告下載監(jiān)聽
if (ad.getInteractionType() == TTAdConstant.INTERACTION_TYPE_DOWNLOAD) {
ad.setDownloadListener(new TTAppDownloadListener() {
boolean hasShow = false;
@Override
public void onIdle() {
}
@Override
public void onDownloadActive(long totalBytes, long currBytes, String fileName, String appName) {
if (!hasShow) {
Log.d(TAG, "開屏廣告下載中");
hasShow = true;
}
}
@Override
public void onDownloadPaused(long totalBytes, long currBytes, String fileName, String appName) {
Log.d(TAG, "開屏廣告下載暫停");
}
@Override
public void onDownloadFailed(long totalBytes, long currBytes, String fileName, String appName) {
Log.d(TAG, "開屏廣告下載失敗");
}
@Override
public void onDownloadFinished(long totalBytes, String fileName, String appName) {
Log.d(TAG, "開屏廣告下載完成");
}
@Override
public void onInstalled(String fileName, String appName) {
Log.d(TAG, "開屏廣告安裝完成");
}
});
}
}
}, AD_TIME_OUT);
信息流廣告
信息流廣告即app推薦頁/詳情頁中的原生圖文及視頻廣告,主要特點(diǎn)是精準(zhǔn)算法推薦、原生體驗(yàn)對用戶干擾低。

/**
* 加載feed廣告
*/
private fun loadListAd() {
val mTTAdNative = TTAdManagerHolder.get().createAdNative(this)
TTAdManagerHolder.get().requestPermissionIfNecessary(this)
//創(chuàng)建feed廣告請求類型參數(shù)AdSlot,具體參數(shù)含義參考文檔
val adSlot = AdSlot.Builder()
.setCodeId("945230381")
.setSupportDeepLink(true)
.setExpressViewAcceptedSize(250f, 300f) //期望模板廣告view的size,單位dp
.setAdCount(3) //請求廣告數(shù)量為1到3條
.build()
//step5:請求廣告,調(diào)用feed廣告異步請求接口,加載到廣告后,拿到廣告素材自定義渲染
mTTAdNative.loadNativeExpressAd(adSlot, object : TTAdNative.NativeExpressAdListener {
override fun onError(code: Int, message: String) {
Log.e("loadFeedAd", "code: : $code message :$message")
}
override fun onNativeExpressAdLoad(ads: List<TTNativeExpressAd>?) {
if (ads == null || ads.isEmpty()) {
Log.i("loadFeedAd", "on FeedAdLoaded: ad is null!")
return
}
Log.i("loadFeedAd", "FeedAdLoaded: ad is success===============" + ads.size)
bindAdListener(ads)
}
})
}
private fun bindAdListener(ads: List<TTNativeExpressAd>) {
for (ad in ads) {
ad.setExpressInteractionListener(object :
TTNativeExpressAd.ExpressAdInteractionListener {
override fun onAdClicked(view: View, type: Int) {
Log.i("loadFeedAd", "廣告被點(diǎn)擊")
}
override fun onAdShow(view: View, type: Int) {
Log.i("loadFeedAd", "廣告展示")
}
override fun onRenderFail(view: View, msg: String, code: Int) {
Log.i("loadFeedAd", "$msg code:$code")
}
override fun onRenderSuccess(view: View, width: Float, height: Float) {
//返回view的寬高 單位 dp
Log.i("loadFeedAd", "渲染成功")
}
})
ad.render()
}
}
注:模板類型的代碼位在設(shè)置AdSlot廣告對象的時候一定要設(shè)置該方法setExpressViewAcceptedSize
注:在廣告加載展示完畢后 在Activity的onDestroy方法中把廣告對象置為null
banner
Banner廣告可以在您設(shè)定刷新時間后自動刷新。也就是說,即使用戶停留在同一個屏幕上,他們也會 每隔一段時間就看到新的Banner廣告。

val mTTAdNative = TTAdManagerHolder.get().createAdNative(this)
TTAdManagerHolder.get().requestPermissionIfNecessary(this)
val adSlot = AdSlot.Builder()
.setCodeId("945230387")
.setSupportDeepLink(true)
.setExpressViewAcceptedSize(
150f,
300f
) //期望模板廣告view的size,單位dp
.setAdCount(3) //請求廣告數(shù)量為1到3條
.setNativeAdType(AdSlot.TYPE_BANNER)
.build()
mTTAdNative.loadBannerExpressAd(adSlot, object : TTAdNative.NativeExpressAdListener {
override fun onNativeExpressAdLoad(ads: MutableList<TTNativeExpressAd>?) {
if (ads == null || ads.isEmpty()) {
Log.i("loadFeedAd", "on FeedAdLoaded: ad is null!")
return
}
Log.i("loadFeedAd", "FeedAdLoaded: ad is success===============" + ads.size)
bindAdListener(ads)
}
override fun onError(code: Int, message: String) {
Log.i("loadFeedAd", "code: :$code message :$message")
}
})
四、注意事項(xiàng)
為何廣告轉(zhuǎn)化率低?
發(fā)布前,一定要測試整個廣告的展示,下載,安裝,激活流程。如果廣告能展示就發(fā)布了,完全沒測試廣告后續(xù)流程,就會導(dǎo)致廣告的CPM非常低。廣告cpm依賴廣告展示后的后續(xù)轉(zhuǎn)化,如:點(diǎn)擊,下載,安裝,激活,注冊,充值等。存儲權(quán)限,imei權(quán)限,定位權(quán)限是必須的嗎?
3個權(quán)限都不是必須的,但是如果有的話,大大提高廣告的Ecpm。
有了存儲權(quán)限,就可以推下載類的廣告;
有了imei權(quán)限,就能跟蹤后期轉(zhuǎn)化;
有了定位權(quán)限,就可以推更加精準(zhǔn)的廣告。如何屏蔽競品廣告?
在穿山甲后臺可設(shè)置。