Android Integeration with Ad Manager Mediation
詳細範例
Google ADS 22.2.0 Mediation Project (包含橫幅、插頁、原生廣告)。
新增廣告單元 / 收益群組
登入Google Ad Manager
https://admanager.google.com/home/
橫幅(Banner)
進入 Ad Manager 網頁選擇 應用單元 -> 新增廣告單元
名稱: GAM_Mediation_Android_Banner_320x50
大小: 320x50
新增收益群組
名稱: GAM_Mediation_Android_Banner_320x50
狀態: 有效
廣告格式: 橫幅
新增收益夥伴
其他收益夥伴詳細資訊
Label: 用於AdManagerAdView.adUnitId
Class Name: com.taiwanmobile.pt.gam.mediation.TAmediaGAMCustomEvent
Parameter: TAmedia廣告版位
插頁(Interstitial)
進入 Ad Manager 網頁選擇 應用單元 -> 新增廣告單元
名稱: GAM_Mediation_Android_Interstitial
大小: 1024x768, 768x1024, 480x320, 320x480
新增收益群組
名稱: GAM_Mediation_Android_Interstitial
狀態: 有效
廣告格式: 插頁式
新增收益夥伴
其他收益夥伴詳細資訊
Label: 用於AdManagerInterstitialAd.load()
Class Name: com.taiwanmobile.pt.gam.mediation.TAmediaGAMCustomEvent
Parameter: TAmedia廣告版位
原生(Native)
進入 Ad Manager 網頁選擇 應用單元 -> 新增廣告單元
名稱: GAM_Mediation_Android_Native
大小: 自訂顯示
新增收益群組
名稱: GAM_Mediation_Android_Native
狀態: 有效
廣告格式: 原生格式
新增收益夥伴
其他收益夥伴詳細資訊
Label: 用於AdLoader.Builder().forNativeAd()
Class Name: com.taiwanmobile.pt.gam.mediation.TAmediaGAMCustomEvent
Parameter: TAmedia原生廣告參數(下方說明)
MediaView顯示影音
adUnitId: TAmedia廣告版位
mediaType: VIDEO
videoStartUnmuted: 影片聲音預設開關, true: 有聲音, false: 無聲音
videoCustomControlRequest: 影片上是否要顯示秒數, 靜音, 前往瀏覽等UI按鈕, true: 不顯示, false: 顯示
{ "parameters": { "adUnitId": "ADUNIT_ID", "mediaType": "VIDEO", "videoStartUnmuted":true, "videoCustomControlRequest":true } }
MediaView顯示大圖(1200x628)
adUnitId: TAmedia廣告版位
mediaType: BIG_IMAGE
{ "parameters": { "adUnitId": "ADUNIT_ID", "mediaType": "BIG_IMAGE" } }
MediaView顯示小圖(960x640)
adUnitId: TAmedia廣告版位
mediaType: SMALL_IMAGE
{ "parameters": { "adUnitId": "ADUNIT_ID", "mediaType": "SMALL_IMAGE" } }
Gradle設定
// =======================================================================// // Google Play Service - ADS // =======================================================================// implementation 'com.google.android.gms:play-services-ads:22.2.0' // =======================================================================// // MADP SDK // =======================================================================// implementation fileTree(include: ['*.aar'], dir: 'libs') // =======================================================================// // MADP SDK dependencies // =======================================================================// // kotlin implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.21' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' // retrofit implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.retrofit2:converter-scalars:2.9.0' // advertising ID implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1' // appset implementation 'com.google.android.gms:play-services-appset:16.0.2' // gson implementation 'com.google.code.gson:gson:2.10.1' // lifecycle def lifecycle_version = "2.6.1" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycle_version" // viewModels implementation 'androidx.activity:activity-ktx:1.7.2' // EncryptedSharedPreferences implementation 'androidx.security:security-crypto:1.1.0-alpha06'
Proguard設定
-keep class com.taiwanmobile.pt.adp.mediation.** { public protected <fields>; public protected <methods>; } -keep class com.taiwanmobile.pt.gam.mediation.** { public protected <fields>; public protected <methods>; } -dontwarn com.google.errorprone.annotations.**
開發者程式
橫幅(Banner)
請參考GAMBannerActivity.kt, 使用AdManagerAdView載入AdManagerAdRequest, 進行廣告請求
adUnitId?.let { val request = AdManagerAdRequest.Builder().build() adManagerAdView = AdManagerAdView(this@GAMBannerActivity) adManagerAdView?.adUnitId = it adManagerAdView?.setAdSizes(adSize) adManagerAdView?.adListener = object: AdListener() { override fun onAdLoaded() {} override fun onAdFailedToLoad(adError : LoadAdError) {} override fun onAdClicked() {} override fun onAdImpression() {} } adManagerAdView?.loadAd(request) // add view into layout binding.adContainer.addView(adManagerAdView) }
插頁(Interstitial)
請參考GAMInterstitialActivity.kt, 使用AdManagerInterstitialAd載入AdManagerAdRequest物件, 進行廣告請求
adUnitId?.let { val adRequest = AdManagerAdRequest.Builder().build() AdManagerInterstitialAd.load(this, it, adRequest, object : AdManagerInterstitialAdLoadCallback() { override fun onAdFailedToLoad(adError: LoadAdError) {} override fun onAdLoaded(ad: AdManagerInterstitialAd) { interstitialAd = ad interstitialAd?.show(this@GAMInterstitialActivity) } }) }
原生(Native)
請參考GAMNativeActivity.kt, 使用AdManagerInterstitialAd載入AdManagerAdRequest物件, 進行廣告請求
val adUnitId = args.getString(GAMMainActivity.KEY_ADUNITID) ?: "" val adLoader = AdLoader.Builder(this, adUnitId) .forNativeAd { ad: NativeAd -> if (isDestroyed) { ad.destroy() return@forNativeAd } nativeAd = ad val vb = NativeAdBinding.inflate(layoutInflater) populateNativeAdView(ad, vb) binding.nativeContainer.removeAllViews() binding.nativeContainer.addView(vb.root) } .withAdListener(object : AdListener() { override fun onAdFailedToLoad(adError: LoadAdError) {} override fun onAdClicked() {} override fun onAdImpression() {} }) .withNativeAdOptions(NativeAdOptions.Builder().build()) .build() adLoader.loadAd(AdManagerAdRequest.Builder().build())
請參考GAMNativeActivity.kt, 在populateNativeAdView()有填充NativeAd物件實作細節
private fun populateNativeAdView(nativeAd: NativeAd, viewBinding: NativeAdBinding) { // fetch NativeAdView from NativeAdBinding val nativeAdView: NativeAdView = viewBinding.root // headline if (nativeAd.headline == null) { viewBinding.adHeadline.visibility = View.INVISIBLE } else { viewBinding.adHeadline.visibility = View.VISIBLE viewBinding.adHeadline.text = nativeAd.headline nativeAdView.headlineView = viewBinding.adHeadline } // body if (nativeAd.body == null) { viewBinding.adBody.visibility = View.INVISIBLE } else { viewBinding.adBody.visibility = View.VISIBLE viewBinding.adBody.text = nativeAd.body nativeAdView.bodyView = viewBinding.adBody } // call to action if (nativeAd.callToAction == null) { viewBinding.adCallToAction.visibility = View.INVISIBLE } else { viewBinding.adCallToAction.visibility = View.VISIBLE viewBinding.adCallToAction.text = nativeAd.callToAction nativeAdView.callToActionView = viewBinding.adCallToAction } // icon if (nativeAd.icon == null) { viewBinding.adAppIcon.visibility = View.GONE } else { nativeAd.icon?.drawable?.let { viewBinding.adAppIcon.setImageDrawable(it) viewBinding.adAppIcon.visibility = View.VISIBLE nativeAdView.iconView = viewBinding.adAppIcon } } // mediaView nativeAdView.mediaView = viewBinding.adMedia nativeAdView.mediaView?.mediaContent = nativeAd.mediaContent nativeAdView.setNativeAd(nativeAd) }
請參考NativeAdBinding佈局細節, 使用NativeAdView設計原生廣告佈局, 使用MediaView顯示 影片 或 大圖 或 小圖 /p>
<com.google.android.gms.ads.nativead.NativeAdView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:id="@+id/ad_app_icon" android:layout_width="40dp" android:layout_height="40dp" android:adjustViewBounds="true" android:paddingEnd="5dp" android:paddingRight="5dp" android:paddingBottom="5dp" /> <TextView android:id="@+id/ad_headline" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#0000FF" android:textSize="16sp" android:textStyle="bold" /> <TextView android:id="@+id/ad_body" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="20dp" android:layout_marginRight="20dp" android:textSize="12sp" /> <com.google.android.gms.ads.nativead.MediaView android:id="@+id/ad_media" android:layout_width="250dp" android:layout_height="175dp" android:layout_gravity="center_horizontal" android:layout_marginTop="5dp" /> <Button android:id="@+id/ad_call_to_action" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:textSize="12sp" /> </com.google.android.gms.ads.nativead.NativeAdView>
TAmedia中介程式
橫幅(Banner)
請參考TAmediaGAMCustomEvent.loadBannerAd(), 此為橫幅中介程式進入點
override fun loadBannerAd( configuration: MediationBannerAdConfiguration, callback: MediationAdLoadCallback<MediationBannerAd, MediationBannerAdCallback> ) { bannerLoader = TAmediaGAMBannerLoader(configuration, callback) bannerLoader?.loadAd() }
插頁(Interstitial)
請參考TAmediaGAMCustomEvent.loadInterstitialAd(), 此為插頁中介程式進入點
override fun loadInterstitialAd( configuration: MediationInterstitialAdConfiguration, callback: MediationAdLoadCallback<MediationInterstitialAd, MediationInterstitialAdCallback> ) { interstitialLoader = TAmediaGAMInterstitialLoader(configuration, callback) interstitialLoader?.loadAd() }
原生(Native)
請參考TAmediaGAMCustomEvent.loadNativeAd(), 此為原生中介程式進入點
override fun loadNativeAd( configuration: MediationNativeAdConfiguration, callback: MediationAdLoadCallback<UnifiedNativeAdMapper, MediationNativeAdCallback> ) { nativeLoader = TAmediaGAMNativeLoader(configuration, callback) nativeLoader?.loadAd() }