Android Admob(二)开屏广告和原生广告 您所在的位置:网站首页 单页广告和原生广告一样吗英语翻译 Android Admob(二)开屏广告和原生广告

Android Admob(二)开屏广告和原生广告

2024-02-24 06:25| 来源: 网络整理| 查看: 265

之前的文章Android Admob接入中介绍了如何接入和测试Admob的插屏广告、激励广告和横幅广告。评论区有掘友问了为啥没有开屏广告,当然是因为目前我司项目中没有用到开屏广告咯。不过既然有人问了,那么本篇文章就介绍下缺少的开屏广告和原生广告。

前置条件以及如何测试请移步Android Admob接入,本文不多赘述。

开屏广告

开屏广告一般在App初次打开(冷启动)或者App从后台进入前台(热启动)时显示。

官方文档

开屏广告控制类

自定义广告控制类,用于加载、显示开屏广告,代码如下:

class AppOpenAdManager { private val appOpenAdUnit = "ca-app-pub-3940256099942544/3419835294" private val appOpenAdValidTime = 4 * 60 * 60 * 1000 private var currentAppOpenAd: AppOpenAd? = null private var currentAppOpenAdLoadedTime: Long = 0 private var loadingAd: Boolean = false var showingAd: Boolean = false private set private var showAdWhenReady: Boolean = false private var tempActivity: Activity? = null private var tempAppOpenAdShowCallback: AppOpenAdShowCallback? = null private val appOpenAdLoadCallback = object : AppOpenAdLoadCallback() { override fun onAdLoaded(appOpenAd: AppOpenAd) { super.onAdLoaded(appOpenAd) // 开屏广告加载成功 currentAppOpenAdLoadedTime = System.currentTimeMillis() currentAppOpenAd = appOpenAd loadingAd = false // 设置了当加载完成时打开广告 if (showAdWhenReady) { showAdWhenReady = false tempActivity?.let { showAppOpenAd(it, tempAppOpenAdShowCallback) } } } override fun onAdFailedToLoad(loadAdError: LoadAdError) { super.onAdFailedToLoad(loadAdError) // 开屏广告加载失败 // 官方不建议在此回调中重新加载广告 // 如果确实需要,则必须限制最大重试次数,避免在网络受限的情况下连续多次请求 loadingAd = false } } fun loadAd(context: Context) { if (!loadingAd) { loadingAd = true AppOpenAd.load(context, appOpenAdUnit, AdRequest.Builder().build(), appOpenAdLoadCallback) } } fun showAppOpenAd(activity: Activity, appOpenAdShowCallback: AppOpenAdShowCallback? = null, showAdWhenReady: Boolean = false) { if (showingAd) { // 开屏广告正在展示 return } if (!appOpenAdAvailable()) { // 开屏广告不可用,重新加载 loadAd(activity) if (showAdWhenReady) { // 设置当加载完成时打开广告,缓存当前页面和回调方法 this.showAdWhenReady = true tempActivity = activity tempAppOpenAdShowCallback = appOpenAdShowCallback } else { // 广告不可用,回调播放完成继续执行后续步骤 appOpenAdShowCallback?.onAppOpenAdShowComplete() } return } currentAppOpenAd?.run { fullScreenContentCallback = object : FullScreenContentCallback() { override fun onAdShowedFullScreenContent() { super.onAdShowedFullScreenContent() // 广告展示 } override fun onAdFailedToShowFullScreenContent(adError: AdError) { super.onAdFailedToShowFullScreenContent(adError) // 广告展示失败,清空缓存数据,重新加载 showingAd = false currentAppOpenAd = null appOpenAdShowCallback?.onAppOpenAdShowComplete() tempActivity = null tempAppOpenAdShowCallback = null loadAd(activity) } override fun onAdDismissedFullScreenContent() { super.onAdDismissedFullScreenContent() // 广告关闭,清空缓存数据,重新加载 showingAd = false currentAppOpenAd = null appOpenAdShowCallback?.onAppOpenAdShowComplete() tempActivity = null tempAppOpenAdShowCallback = null loadAd(activity) } } showingAd = true show(activity) appOpenAdShowCallback?.onAppOpenAdShow() } } fun stopAutoShow() { showAdWhenReady = false tempActivity = null tempAppOpenAdShowCallback = null } private fun appOpenAdAvailable(): Boolean { return currentAppOpenAd != null && (currentAppOpenAdLoadedTime != 0L && System.currentTimeMillis() - currentAppOpenAdLoadedTime val readyAdapter = initializationStatus.adapterStatusMap.entries.find { it.value.initializationState == AdapterStatus.State.READY } if (readyAdapter != null) { appOpenAdManager?.loadAd(applicationContext) } } } } 在AndroidManifest中配置Application ... 自定义启动页

自定义启动页作为过渡页面。

启动页(播放广告) class AppOpenAdActivity : AppCompatActivity() { private val handler = Handler(Looper.myLooper() ?: Looper.getMainLooper()) private val enterRunnable = Runnable { // 停止自动显示,避免进入主页后自动展示广告打断用户行为 (application as ExampleApplication).appOpenAdManager?.stopAutoShow() enterHomePage() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.layout_app_open_activity) (application as ExampleApplication).appOpenAdManager?.showAppOpenAd(this, object : AppOpenAdManager.AppOpenAdShowCallback { override fun onAppOpenAdShow() { // 开屏广告已显示,停止计时线程 handler.removeCallbacks(enterRunnable) } override fun onAppOpenAdShowComplete() { // 开屏广告播放完毕(成功或失败),停止计时线程并进入主页 handler.removeCallbacks(enterRunnable) enterHomePage() } }, true) // 三秒内没有显示出广告,自动进入主页 handler.postDelayed(enterRunnable, 3000) } private fun enterHomePage() { startActivity(Intent(this, HomeActivity::class.java)) finish() } } 在AndroidManifest中配置启动页

效果如图:

appopenad.gif SplashScreen(官方建议)

从Android12开始,官方建议使用SplashScreen库实现启动页。

添加SplashScreen库 dependencies { implementation("androidx.core:core-splashscreen:1.0.0") } 在主页面中配置SplashScreen,并通过viewTreeObserver.addOnPreDrawListener来暂停主页面的绘制,实现过渡效果 class HomeActivity : AppCompatActivity() { private var noWaitingOpenAd = false private val handler = Handler(Looper.myLooper() ?: Looper.getMainLooper()) private val timeoutRunnable = Runnable { // 停止自动显示,避免展示主页后自动显示广告打断用户行为 (application as ExampleApplication).appOpenAdManager?.stopAutoShow() noWaitingOpenAd = true } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) installSplashScreen() findViewById(android.R.id.content).run { // 注册绘制监听,暂停一段时间等待开屏广告 viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener { override fun onPreDraw(): Boolean { if (noWaitingOpenAd) { // 不再等待广告,移除绘制监听 viewTreeObserver.removeOnPreDrawListener(this) } return noWaitingOpenAd } }) } (application as ExampleApplication).appOpenAdManager?.showAppOpenAd(this, object : AppOpenAdManager.AppOpenAdShowCallback { override fun onAppOpenAdShow() { // 开屏广告已显示,停止计时线程 handler.removeCallbacks(timeoutRunnable) } override fun onAppOpenAdShowComplete() { // 开屏广告播放完毕(成功或失败),开始绘制主页面 noWaitingOpenAd = true } }, true) handler.postDelayed(timeoutRunnable, 3000) setContentView(R.layout.layout_home_activity) } } 在AndroidManifest中配置主页

效果如图:

appopenad.gif 热启动

在Application中注册ActivityLifecycleCallbacks和LifecycleEventObserver等监听,当接收到进入前台事件时显示开屏广告,具体代码如下:

class ExampleApplication : Application() { var appOpenAdManager: AppOpenAdManager? = null private var currentActivity: Activity? = null private var lastShowOpenAdTime: Long = 0 private var interval = 4 * 60 * 60 * 1000 override fun onCreate() { super.onCreate() appOpenAdManager = AppOpenAdManager() MobileAds.initialize(this) { initializationStatus -> val readyAdapter = initializationStatus.adapterStatusMap.entries.find { it.value.initializationState == AdapterStatus.State.READY } if (readyAdapter != null) { appOpenAdManager?.loadAd(applicationContext) } } // 注册ActivityLifecycleCallbacks来记录当前显示的Activity registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} override fun onActivityStarted(activity: Activity) { // 开屏广告不展示的情况下,记录当前的Activity if (appOpenAdManager?.showingAd != true) { currentActivity = activity } } override fun onActivityResumed(activity: Activity) {} override fun onActivityPaused(activity: Activity) {} override fun onActivityStopped(activity: Activity) {} override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} override fun onActivityDestroyed(activity: Activity) {} }) // 注册LifecycleEventObserver来监听应用进入前台的事件 ProcessLifecycleOwner.get().lifecycle.addObserver(object : LifecycleEventObserver { override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { if (event == Lifecycle.Event.ON_START) { // 应用进入前台,显示开屏广告 currentActivity?.let { // 判断上次显示广告与本次现实广告的时间间隔,避免两次广告之间间隔太短 if (lastShowOpenAdTime == 0L || System.currentTimeMillis() - lastShowOpenAdTime > interval) { appOpenAdManager?.showAppOpenAd(it, object : AppOpenAdManager.AppOpenAdShowCallback { override fun onAppOpenAdShow() { lastShowOpenAdTime = System.currentTimeMillis() } override fun onAppOpenAdShowComplete() {} }) } } } } }) } }

效果如图:

appopenadhot -original-original.gif 原生广告

原生广告通过原生控件(例如TextView、ImageView)来呈现广告素材,可以嵌入到App页面中,与App整体风格更融洽。

官方文档

加载与显示

使用AdLoader加载广告。以NativeAdView作为根布局,结合TextView、ImageView等原生控件呈现广告素材。代码如下:

自定义布局文件,以NativeAdView作为根布局 加载并展示原生广告 class AdmobExampleActivity : AppCompatActivity() { private lateinit var binding: LayoutAdmobExampleActivityBinding private var nativeAdView: NativeAdView? = null private var currentNativeAd: NativeAd? = null private val adListener = object : AdListener() { override fun onAdLoaded() { super.onAdLoaded() // 广告加载成功 // 动态添加NaviteAdView到页面中 nativeAdView?.let { binding.flNativeAdContainer.addView(it) } } override fun onAdFailedToLoad(loadAdError: LoadAdError) { super.onAdFailedToLoad(loadAdError) // 广告加载失败 } override fun onAdOpened() { super.onAdOpened() // 广告页打开 } override fun onAdClicked() { super.onAdClicked() // 广告被点击 } override fun onAdClosed() { super.onAdClosed() // 广告页关闭 } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.layout_admob_example_activity) MobileAds.initialize(this, object : OnInitializationCompleteListener { override fun onInitializationComplete(initializationStatus: InitializationStatus) { val readyAdapter = initializationStatus.adapterStatusMap.entries.find { it.value.initializationState == AdapterStatus.State.READY } if (readyAdapter != null) { loadNativeAd() } } }) } private fun populateNativeAdView() { currentNativeAd?.let { nativeAd -> nativeAdView?.let { binding.flNativeAdContainer.removeView(it) } (LayoutInflater.from(this).inflate(R.layout.layout_admob_native_ad, null) as? NativeAdView)?.run { iconView = findViewById(R.id.iv_ad_app_icon).apply { nativeAd.icon?.let { setImageDrawable(it.drawable) } visibility = if (nativeAd.icon != null) View.VISIBLE else View.GONE } headlineView = findViewById(R.id.tv_ad_headline).apply { text = nativeAd.headline } advertiserView = findViewById(R.id.tv_advertiser).apply { text = nativeAd.advertiser visibility = if (nativeAd.advertiser != null) View.VISIBLE else View.INVISIBLE } starRatingView = findViewById(R.id.rb_ad_stars).apply { nativeAd.starRating?.let { rating = it.toFloat() } visibility = if (nativeAd.starRating != null) View.VISIBLE else View.INVISIBLE } bodyView = findViewById(R.id.tv_ad_body).apply { text = nativeAd.body visibility = if (nativeAd.body != null) View.VISIBLE else View.INVISIBLE } mediaView = findViewById(R.id.mv_ad_media).apply { nativeAd.mediaContent?.let { mediaContent = it it.videoController.videoLifecycleCallbacks = object : VideoController.VideoLifecycleCallbacks() { override fun onVideoStart() { super.onVideoStart() // 视频开始 } override fun onVideoEnd() { super.onVideoEnd() // 视频结束,结束后可以刷新广告 } override fun onVideoPlay() { super.onVideoPlay() // 视频播放 } override fun onVideoPause() { super.onVideoPause() // 视频暂停 } override fun onVideoMute(mute: Boolean) { super.onVideoMute(mute) // 视频是否静音 // mute true 静音 false 非静音 } } } } callToActionView = findViewById(R.id.btn_ad_call_to_action).apply { text = nativeAd.callToAction visibility = if (nativeAd.callToAction != null) View.VISIBLE else View.INVISIBLE } priceView = findViewById(R.id.tv_ad_price).apply { text = nativeAd.price visibility = if (nativeAd.price != null) View.VISIBLE else View.INVISIBLE } storeView = findViewById(R.id.tv_ad_store).apply { text = nativeAd.store visibility = if (nativeAd.store != null) View.VISIBLE else View.INVISIBLE } layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT).apply { gravity = Gravity.BOTTOM } setNativeAd(nativeAd) nativeAdView = this } } } private fun loadNativeAd() { val adLoader = AdLoader.Builder(this, "ca-app-pub-3940256099942544/2247696110") .forNativeAd { nativeAd -> // 如果在页面销毁后触发此回调,需要销毁NativeAd避免内存泄漏 if (isDestroyed || isFinishing || isChangingConfigurations) { nativeAd.destroy() return@forNativeAd } currentNativeAd?.destroy() currentNativeAd = nativeAd populateNativeAdView() } .withNativeAdOptions(NativeAdOptions.Builder() // 设置视频是否静音播放 .setVideoOptions(VideoOptions.Builder().setStartMuted(false).build()) .build()) .withAdListener(adListener) .build() adLoader.loadAd(AdRequest.Builder().build()) } override fun onDestroy() { super.onDestroy() currentNativeAd?.destroy() } }

效果如图:

device-2023-04-08-13 -original-original.gif 不再显示此广告(优化用户体验)

原生广告嵌入在App页面中,用户可能会想要关闭原生广告,可以给用户提供一个关闭按钮关闭广告。AdMob提供了一些相关的API,可以用于标记用户关闭广告的原因,当然不使用相关API直接关闭广告也是可以的。

调整NativeAdOptions,在forNativeAd回调中获取不再显示广告的原因 class AdmobExampleActivity : AppCompatActivity() { private val muteThisAdReason = ArrayList() ... private fun loadNativeAd() { val adLoader = AdLoader.Builder(this, "ca-app-pub-3940256099942544/2247696110") .forNativeAd { nativeAd -> ... // 判断是否支持自定义不再显示广告 if (nativeAd.isCustomMuteThisAdEnabled) { // 获取不再显示广告的原因 muteThisAdReason.addAll(nativeAd.muteThisAdReasons) } } .withNativeAdOptions(NativeAdOptions.Builder() // 设置视频是否静音播放 .setVideoOptions(VideoOptions.Builder().setStartMuted(false).build()) // 设置自定义不再显示广告 .setRequestCustomMuteThisAd(true) .build()) .withAdListener(adListener) .build() adLoader.loadAd(AdRequest.Builder().build()) } } 用户选择关闭广告时,提供原因让用户选择,用户选择后通过API上报原因并移除广告 class AdmobExampleActivity : AppCompatActivity() { private lateinit var binding: LayoutAdmobExampleActivityBinding private val muteThisAdReason = ArrayList() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.layout_admob_example_activity) binding.btnStopNativeAd.setOnClickListener { showChoseMuteNativeAdDialog() } } private fun showChoseMuteNativeAdDialog() { val muteThisAdReasonString = arrayOfNulls(muteThisAdReason.size) for ((index, item) in muteThisAdReason.withIndex()) { muteThisAdReasonString[index] = item.description } AlertDialog.Builder(this) .setTitle("关闭此原生广告的原因是?") .setItems(muteThisAdReasonString) { dialog, which -> if (muteThisAdReason.size > which) { muteNativeAd(muteThisAdReason[which]) } } .create() .show() } private fun muteNativeAd(muteThisAdReason: MuteThisAdReason) { // 可以上报用户关闭广告的原因,便于优化广告 currentNativeAd?.muteThisAd(muteThisAdReason) nativeAdView?.destroy() currentNativeAd?.destroy() binding.flNativeAdContainer.removeAllViews() } }

效果如图:

nativeadclose.gif 示例

在示例Demo中添加了相关的演示代码。

ExampleDemo github

ExampleDemo gitee



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有