news 2026/10/7 20:17:01

AdMob Android Quickstart 实战指南:基于 AdView 与 InterstitialAd 实现横幅广告和插屏广告

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AdMob Android Quickstart 实战指南:基于 AdView 与 InterstitialAd 实现横幅广告和插屏广告
  • 示例工程

【免费下载链接】quickstart-android

Firebase Quickstart Samples for Android

项目地址:https://gitcode.com/gh_mirrors/qu/quickstart-android
点击查看免费下载

导读

本文以 quickstart-android 仓库中的 admob 示例 为主体,系统讲解如何在 Android 应用中集成 Google Mobile Ads SDK,通过AdRequest+AdView展示横幅广告(Banner Ad)、通过InterstitialAd展示插屏广告(Interstitial Ad)的完整流程。读完本文,你将掌握 AdMob 应用 ID 与广告单元 ID 的配置方法、两类广告的加载与展示调用链、广告生命周期管理,以及 Java / Kotlin 双版本示例的工程结构。

以下截屏为该示例应用在设备上的实际运行效果:顶部为 Firebase 标识与 "SHOW INTERSTITIAL AD" 按钮,底部即为 320×50 的 AdMob 测试横幅广告。

一、示例项目要解决什么问题

admob/README.md 明确指出,该 quickstart 的核心目的是演示两类最常见的 AdMob 广告形式:

  • 横幅广告(Banner Ad):通过AdRequest与AdView组合实现,常驻于页面底部;
  • 插屏广告(Interstitial Ad):通过InterstitialAd实现,以全屏形式在页面切换等时机弹出。

示例运行后,屏幕上会直接展示一条测试横幅广告和一条测试插屏广告——这意味着整个示例在未接入真实广告单元的情况下即可跑通完整的「加载 → 展示 → 关闭 → 重新加载」链路,非常适合作为接入 AdMob 的起点工程。

从源码结构看,项目对 Java 与 Kotlin 提供了完全对等的两份实现:入口 EntryChoiceActivity.kt 在启动时让用户二选一,分别跳转到 java 版 MainActivity 与 kotlin 版 MainActivity,两者的页面导航逻辑(Navigation+nav_graph)完全一致,真正的广告业务代码则集中在两个版本的FirstFragment中。

二、接入前的准备工作

1. 添加 Firebase 到 Android 项目

按官方标准流程将 Firebase 接入 Android 工程(添加 google-services 插件、放置google-services.json),这是后续使用 AdMob 的前提。需要特别留意的一点:AdMob 广告单元 ID 并不存放在google-services.json中,这一点在 FirstFragment.java 的注释中有明确说明——开发者要么将其作为自定义值存入字符串资源,要么直接使用常量。

2. 配置 AdMob 应用 ID(关键步骤)

README 给出了最核心的配置操作:修改src/main/res/values/strings.xml中的admob_app_id字符串,替换为你自己的 AdMob 应用 ID。在仓库中对应文件为 admob/app/src/main/res/values/strings.xml:

<!-- TODO: Replace this with your own Admob App ID! This value is only a sample. --> <string name="admob_app_id">ca-app-pub-3940256099942544~3347511713</string>

README 特别强调:这个 ID 会在两处被使用:

  1. AndroidManifest.xml中的meta-data节点——它是 Google Mobile Ads SDK 初始化所必需的元数据,缺失或配置错误会导致应用直接崩溃(Manifest 注释明确写着 "This metadata is required or your app will crash!"):
<meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="@string/admob_app_id" />
  1. MainActivity / FirstFragment 中的校验逻辑:Java 版 FirstFragment.java 与 Kotlin 版 FirstFragment.kt 都实现了checkIds()方法——若admob_app_id仍是内置测试 ID,会输出警告日志提示开发者参考 README 修改:
private void checkIds() { if (TEST_APP_ID.equals(getString(R.string.admob_app_id))) { Log.w(TAG, "Your admob_app_id is not configured correctly, please see the README"); } }

这可以看作一个"配置未完成"的运行时提醒机制,避免开发者把测试 ID 带上生产环境而不自知。

3. Manifest 中的权限与 AdActivity 声明

AndroidManifest.xml 中还需要声明以下内容(示例均已包含):

<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  • INTERNET:广告请求依赖网络,必须声明;
  • ACCESS_NETWORK_STATE:SDK 用于感知网络状态以决定广告请求策略。

同时必须显式声明 SDK 内部的AdActivity,并为其配置configChanges与透明主题,否则广告展示时可能因配置变更问题出现异常:

<activity android:name="com.google.android.gms.ads.AdActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize" android:theme="@android:style/Theme.Translucent" />

三、横幅广告(Banner Ad)实现

1. 在布局中声明 AdView

横幅广告的核心是com.google.android.gms.ads.AdView。在 fragment_first.xml 中,它被直接声明为布局的一部分,并通过两个自定义属性完成配置:

<com.google.android.gms.ads.AdView android:id="@+id/adView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" ads:adSize="BANNER" ads:adUnitId="@string/banner_ad_unit_id" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" />
  • ads:adSize="BANNER":指定广告尺寸为标准横幅(320×50 dp);
  • ads:adUnitId="@string/banner_ad_unit_id":指定横幅广告单元 ID。

对应的广告单元 ID 同样定义在 strings.xml:

<string name="banner_ad_unit_id">ca-app-pub-3940256099942544/6300978111</string> <string name="interstitial_ad_unit_id">ca-app-pub-3940256099942544/1033173712</string>

这两个 ID 均为 Google 提供的专用测试广告单元(ca-app-pub-3940256099942544前缀),与测试应用 ID 配套使用,保证返回的一定是测试广告,不会产生真实计费。

2. 初始化 SDK 并加载横幅

在 FirstFragment.java(Kotlin 版见 FirstFragment.kt)中,onViewCreated完成了三件事:

// Initialize the Google Mobile Ads SDK MobileAds.initialize(getContext()); mAdView = binding.adView; requestNewInterstitial();
  • MobileAds.initialize(context):SDK 初始化入口,应在应用早期调用;
  • 拿到binding.adView引用:对应布局中的AdView;
  • 调用requestNewInterstitial():该方法内同时完成横幅与插屏的加载。

横幅的加载逻辑封装在同方法的开头:

private void requestNewInterstitial() { AdRequest adRequest = new AdRequest.Builder().build(); mAdView.loadAd(adRequest); ... }

调用链非常清晰:AdRequest.Builder().build()构造一个空的(无定向参数的)广告请求对象,随后AdView.loadAd(adRequest)异步发起横幅广告请求。从源码结构看,示例刻意保持了最简单的请求构造方式,便于开发者先跑通全链路,再按业务需要为AdRequest.Builder()追加年龄、性别、关键词等定向参数。

四、插屏广告(Interstitial Ad)实现

1. 异步加载与回调

插屏广告与横幅的关键区别在于:插屏是一个独立的全屏对象,需要先异步加载、再在合适的时机手动展示。示例在requestNewInterstitial()中完成加载(见 FirstFragment.java):

InterstitialAd.load(getContext(), getString(R.string.interstitial_ad_unit_id), adRequest, new InterstitialAdLoadCallback() { @Override public void onAdLoaded(@NonNull InterstitialAd interstitialAd) { super.onAdLoaded(interstitialAd); mInterstitialAd = interstitialAd; // Ad received, ready to display if (mLoadInterstitialButton != null) { mLoadInterstitialButton.setEnabled(true); } ... } @Override public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) { super.onAdFailedToLoad(loadAdError); mInterstitialAd = null; Log.w(TAG, "onAdFailedToLoad:" + loadAdError.getMessage()); } });

值得学习的工程实践:

  • onAdLoaded:将加载成功的InterstitialAd实例暂存,同时启用"显示插屏"按钮——按钮初始是禁用的(mLoadInterstitialButton.setEnabled(mInterstitialAd != null)),只有广告真正就绪用户才能点击,避免空白插屏;
  • onAdFailedToLoad:将实例置空并记录LoadAdError的报错信息,便于排查填充失败原因(如网络问题、广告单元配置错误、政策违规等)。

2. 展示插屏与关闭后处理

按钮点击时判断广告是否可用(FirstFragment.java):

mLoadInterstitialButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mInterstitialAd != null) { mInterstitialAd.show(getActivity()); } else { beginSecondActivity(); } } });

即:广告已加载 → 全屏展示插屏;未加载 → 直接跳过广告跳转下一页面。这种"优雅降级"策略保证用户操作永远有响应。

展示期间通过FullScreenContentCallback感知全屏内容的生命周期(FirstFragment.java):

mInterstitialAd.setFullScreenContentCallback(new FullScreenContentCallback() { @Override public void onAdDismissedFullScreenContent() { super.onAdDismissedFullScreenContent(); requestNewInterstitial(); // 立即预加载下一条插屏 beginSecondActivity(); // 继续原本的页面跳转 } });

这里体现了一个关键最佳实践:插屏广告是一次性资源,展示并关闭后即失效。因此在onAdDismissedFullScreenContent(用户关闭插屏)回调中,示例同时做了两件事:调用requestNewInterstitial()为下一次展示预加载新广告,并调用beginSecondActivity()继续被插屏打断的导航流程(通过 Navigation 组件跳转到SecondFragment)。Kotlin 版在 FirstFragment.kt 中的写法完全对应。

3. 生命周期兜底:onResume 重新加载

在 FirstFragment.java 的onResume中还有一层保障:

@Override public void onResume() { super.onResume(); if (mAdView != null) { mAdView.resume(); } if (mInterstitialAd == null) { requestNewInterstitial(); } }

当mInterstitialAd为空(例如加载失败、已展示完毕)时,页面恢复可见即自动发起重新加载,确保插屏随时可用。

五、广告生命周期管理与内存释放

横幅广告AdView与 Activity/Fragment 的生命周期强相关,示例在三个回调点做了对称处理(Java 版见 FirstFragment.java,Kotlin 版见 FirstFragment.kt):

生命周期回调必须执行的操作作用
onPause()adView.pause()暂停广告刷新,节省资源
onResume()adView.resume()恢复广告刷新
onDestroy()adView.destroy()释放广告资源,防止内存泄漏
@Override public void onPause() { if (mAdView != null) { mAdView.pause(); } super.onPause(); } @Override public void onDestroy() { if (mAdView != null) { mAdView.destroy(); } super.onDestroy(); }

这是 AdMob 接入中极易被遗漏但官方强制的规范:不随生命周期正确暂停/恢复/销毁AdView,轻则浪费流量与电量,重则引发内存问题。Kotlin 版额外在onDestroyView中将_binding置空以解除 ViewBinding 引用,属于 Kotlin 侧的配套清理。

六、运行验证与预期结果

完成配置后,在 Android 设备或模拟器上运行(仓库提供gradlew与gradlew.bat,settings.gradle.kts已通过include(":internal:lintchecks")等声明依赖仓库内置的 lint 检查模块)。运行后即可看到 README 所述的预期结果:

  1. 测试横幅广告:页面底部显示 320×50 的 AdMob 测试横幅(文案为 "Nice job! You're displaying a 320 x 50 test ad from AdMob.");
  2. 测试插屏广告:点击 "SHOW INTERSTITIAL AD" 按钮后全屏展示测试插屏,关闭后自动导航到第二页面,并同步预加载下一条插屏。

若在onAdFailedToLoad的日志中看到失败原因,可优先核对:应用 ID 是否已替换、网络是否可用、广告单元 ID 格式是否正确。

七、注意事项与最佳实践小结

  1. 测试 ID 与生产 ID 分离:示例内置的ca-app-pub-3940256099942544系列 ID 仅用于测试(且不可用于其他用途),上线前必须替换为 AdMob 后台创建的真实应用 ID 与广告单元 ID,并移除checkIds()的测试 ID 告警路径。
  2. 插屏"预加载 + 用完即弃":插屏广告应提前加载、展示一次即失效、关闭后立即重新加载,示例的requestNewInterstitial()复用设计就是这一模式的范本。
  3. 广告与用户流程解耦:示例在广告未就绪时直接放行用户操作(跳过插屏继续导航),避免广告加载阻塞业务,这是插屏接入的通用兜底策略。
  4. 严格跟随生命周期:AdView的pause() / resume() / destroy()必须与 Fragment/Activity 生命周期对齐,Manifest 中的AdActivity与APPLICATION_ID元数据也缺一不可。

八、进一步探索仓库

想继续深入,可在仓库中对照阅读以下文件:

  • Java 版广告实现:FirstFragment.java、MainActivity.java
  • Kotlin 版广告实现:FirstFragment.kt、MainActivity.kt
  • 资源配置与导航:strings.xml、fragment_first.xml、nav_graph_kotlin.xml
  • 构建入口:settings.gradle.kts、gradle.properties

这些文件共同构成了一个"配置 → 加载 → 展示 → 生命周期管理"完整的 AdMob 接入参考实现,可直接作为业务项目中横幅与插屏广告集成的蓝本。

  • 示例工程

【免费下载链接】quickstart-android

Firebase Quickstart Samples for Android

项目地址:https://gitcode.com/gh_mirrors/qu/quickstart-android
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 20:16:39

胶液在上胶过程裹入空气,胶面存在气泡?

在胶液上胶过程中&#xff0c;如果空气裹入胶液中&#xff0c;会引发气泡的出现。这些气泡通常是由多种因素造成的、如搅拌方式和上胶速度以及环境条件。气泡不光会影响胶面的外观&#xff0c;还可能降低产品的整体质量和粘接强度。本篇文章将详细探讨气泡的形成原因&#xff0…

作者头像 李华
网站建设 2026/10/7 20:16:35

提醒功能上架第一天起一次都没响过,而代码、日志和 UI 都说它一切正常|HarmonyOS NEXT 提醒代理 reminderAgent 通知授权与 publishReminder 踩坑

首发于华为开发者论坛&#xff1a;https://developer.huawei.com/consumer/cn/blog/topic/03226498246684304提醒、通知、日历事件这类功能&#xff0c;结果是系统侧异步兑现的&#xff0c;所以 API 的返回值说的是「请求已受理」&#xff0c;不是「事情已发生」。我把这两句话…

作者头像 李华
网站建设 2026/10/7 20:16:04

2026年9月:雷神笔记本官方专业维修服务

在使用雷神笔记本过程中&#xff0c;难免会遇到硬件故障、软件问题等糟心事&#xff0c;可维修渠道众多&#xff0c;不知哪里靠谱&#xff0c;既担心技术不行修不好&#xff0c;又害怕被漫天要价&#xff0c;实在让人头疼。挑选雷神笔记本维修店铺&#xff0c;要考虑多方面因素…

作者头像 李华