Cocos Creator 2.4.x 接入 AdMob 插件的迁移实践

最近做了一个针对 Cocos Creator 2.4.x 的 AdMob 插件测试项目,目标是把原本 Cocos Creator 3.x 官方的 AdMob 接入方案,迁移成 2.4.x 项目可以使用的本地插件。

项目地址结构里,真正用于 2.4.x 的插件是:

packages/admob-24x

extensions/GoogleAdMob 更多是从 Cocos 3.x 项目保留下来的参考实现,不能直接照搬。


一、为什么不能直接用 Cocos 3.x 的 AdMob 插件

Cocos Creator 3.x 和 2.4.x 的 Android 构建结构差异很大。

3.x 插件通常依赖:

  • 3.x 的 build hook 系统
  • 3.x 的 Android 工程目录结构
  • 3.x 的 JsbBridge / native bridge 习惯
  • 3.x 生成的 Gradle 工程布局

但 Cocos Creator 2.4.x 生成的 Android 工程通常在:

text 复制代码
build/jsb-default/frameworks/runtime-src/proj.android-studio

它的 AppActivity.javasettings.gradleapp/build.gradle、资源合并逻辑都和 3.x 不一样。

所以这次没有直接复制 3.x 的 BuildTaskAndroid.ts,而是做了一个项目级本地插件:

text 复制代码
packages/admob-24x

它专门针对 Cocos Creator 2.4.x 的构建结果做补丁。


二、插件整体思路

这个插件不是在运行时动态魔改,而是在 Cocos 构建 Android 后,对生成的 Android Studio 工程做 patch

核心流程是:

  1. Cocos Creator 2.4.x 先正常构建 Android。
  2. 插件监听 before-change-files 构建事件。
  3. 找到生成后的 Android 工程目录。
  4. 拷贝 libadmobnativetemplates 两个 Android 模块。
  5. 修改 Gradle 配置。
  6. 修改 AppActivity.java,接入 AdMob 生命周期。
  7. 最后在 Android Studio 里同步和打包。

插件入口在:

text 复制代码
packages/admob-24x/main.js

它做的事情主要包括:

text 复制代码
copyAndroidModules()
patchRootBuildGradle()
patchGradleWrapper()
patchGradleProperties()
patchSettingsGradle()
patchApplicationGradle()
patchLibraryGradle()
patchAppActivity()

三、Android 模块如何接入

插件会把模板目录里的 Android 模块复制到构建工程:

text 复制代码
packages/admob-24x/template/android/libadmob
packages/admob-24x/template/android/nativetemplates

复制到目标工程后,大致会变成:

text 复制代码
build/jsb-default/frameworks/runtime-src/proj.android-studio/libadmob
build/jsb-default/frameworks/runtime-src/proj.android-studio/nativetemplates

然后修改 settings.gradle

gradle 复制代码
include ':libadmob'
include ':nativetemplates'

再修改 app 模块依赖:

gradle 复制代码
implementation project(':libadmob')

这样 AdMob 的 Java 层代码就作为一个 Android Library module 接进了 Cocos 生成的工程。


四、应用 ID 是不是写死的?

测试项目里确实存在 Google 官方测试 ID,例如:

text 复制代码
ca-app-pub-xxxxxxxxxx/xxxxxxxxxx
ca-app-pub-xxxxxxxxxx/xxxxxxxxxx
ca-app-pub-xxxxxxxxxx/xxxxxxxxxx

这些是测试用的 AdMob App ID 和广告单元 ID。

正式上线时需要替换成你自己的:

  • AdMob App ID
  • Banner Unit ID
  • Interstitial Unit ID
  • Rewarded Unit ID
  • App Open Unit ID
  • Native Unit ID

测试 ID 只能用于开发验证,不能直接上线。

比较合理的做法是:

  • 插件层负责 Android 工程接入。
  • 游戏业务层负责传入广告单元 ID。
  • App ID 在 Android Manifest 或构建配置中写入正式值。
  • Demo 场景和测试脚本中保留 Google 官方测试 ID。

这样插件可以复用,具体游戏项目只替换配置。


五、每次构建都会拷贝 admob-24x 吗?

这个插件的设计是:每次 Android 构建时都会重新 patch 当前构建目录

原因是 Cocos Creator 2.4.x 的 Android 构建产物经常会被重新生成。如果只在第一次拷贝,后续重新构建时可能出现:

  • libadmob 被删除
  • settings.gradle 变回原样
  • app/build.gradle 丢失依赖
  • AppActivity.java 里的初始化代码消失
  • Gradle 配置被 Cocos 覆盖

所以当前实现选择每次 patch 构建结果。

不过它不是简单粗暴重复插入,而是做了去重处理,例如:

js 复制代码
appendLineIfMissing(file, "include ':libadmob'");
appendLineIfMissing(file, "include ':nativetemplates'");

AppActivity.java 也会先清理旧的 AdMob 代码,再插入新的代码,避免多次构建后重复出现:

java 复制代码
import com.cocos.admob.AdServiceHub;
AdServiceHub.instance().init(this);
AdServiceHub.instance().destroy();

这个设计很重要,否则构建几次后 Java 文件就会变脏。


六、AppActivity 生命周期接入

AdMob 原生层需要绑定 Android Activity 生命周期。

插件会修改:

text 复制代码
app/src/org/cocos2dx/javascript/AppActivity.java

插入 import:

java 复制代码
import com.cocos.admob.AdServiceHub;

在初始化位置插入:

java 复制代码
AdServiceHub.instance().init(this);

在销毁位置插入:

java 复制代码
AdServiceHub.instance().destroy();

这里有一个迁移坑:

不能假设 3.x 的 Activity 结构和 2.4.x 一样。

所以插件里不是盲目用 3.x 的锚点,而是针对 2.4.x 常见结构查找:

java 复制代码
SDKWrapper.getInstance().init(this);
SDKWrapper.getInstance().onDestroy();

然后分别在它们后面插入 AdMob 初始化和销毁逻辑。


七、Gradle / API 35 兼容问题

这个项目还处理了较新的 Android 构建要求,例如 API 35、AndroidX、Jetifier、Gradle wrapper 等。

插件里定义了这些目标版本:

js 复制代码
const TARGET_AGP_VERSION = '8.5.2';
const TARGET_GRADLE_VERSION = '8.7';
const TARGET_COMPILE_SDK = '35';
const TARGET_MIN_SDK = '23';
const TARGET_BUILD_TOOLS = '35.0.0';

然后自动修改:

text 复制代码
build.gradle
gradle-wrapper.properties
gradle.properties
app/build.gradle
libadmob/build.gradle
nativetemplates/build.gradle

同时开启:

properties 复制代码
android.useAndroidX=true
android.enableJetifier=true
android.suppressUnsupportedCompileSdk=35

这里的技术点是:

Cocos 2.4.x 的 Android 模板比较旧,如果要接新版 Google Mobile Ads SDK,就必须让 Gradle、AGP、compileSdk、AndroidX 这些配置对齐。

否则很容易出现:

  • AndroidX 类找不到
  • compileSdk 不兼容
  • Gradle plugin 版本过低
  • native templates 编译失败
  • NDK 版本不一致

八、NDK 版本不要乱改

Cocos 2.4.x 项目对 NDK 版本比较敏感。

插件没有强行写死 NDK,而是优先读取生成工程里的:

text 复制代码
local.properties

从里面解析:

properties 复制代码
ndk.dir=...

然后把同一个 NDK version 写入相关 Gradle 文件。

这样可以避免:

  • app 模块一个 NDK
  • libadmob 一个 NDK
  • libcocos2dx 又是另一个 NDK

NDK 不一致时,Cocos 原生工程非常容易出现奇怪的编译问题。


九、JS 层怎么调用 AdMob

游戏业务侧不直接调用 Java,也不直接碰 jsb.reflection,而是通过封装好的:

text 复制代码
assets/script/admob/AdMobSDK.ts

示例:

ts 复制代码
import AdMobSDK from './admob/AdMobSDK';

AdMobSDK.init();

AdMobSDK.loadAndShowInterstitial('ca-app-pub-xxx/interstitial', {
    onLoaded: () => cc.log('Interstitial loaded'),
    onFailedToLoad: (error) => cc.log('Interstitial failed', error),
    onDismissed: () => cc.log('Interstitial dismissed'),
    onFailedToShow: (error) => cc.log('Interstitial show failed', error),
});

激励视频:

ts 复制代码
AdMobSDK.loadAndShowRewarded('ca-app-pub-xxx/rewarded', {
    onEarn: (rewardType, rewardAmount) => {
        // 玩家完整看完广告,在这里发奖励
    },
    onFailedToLoad: (error) => {
        cc.log('Rewarded failed', error);
    },
    onDismissed: () => {
        cc.log('Rewarded dismissed');
    },
});

这种封装的好处是:

游戏逻辑只关心广告行为,不需要知道 Java 层协议类,比如 LoadRewardedAdREQRewardedAdLoadCallbackNTF


十、支持的广告类型

当前 AdMob 封装覆盖了常见格式:

  • Banner
  • Interstitial
  • Rewarded
  • Rewarded Interstitial
  • App Open
  • Native

对应测试脚本在:

text 复制代码
script/test/AdmobTestInterstitialAd.ts
script/test/AdmobTestRewarded.ts
script/test/AdmobTestRewardedInterstitialAd.ts
script/test/AdmobTestNative.ts

测试场景里也能看到广告单元 ID 配置。


十一、插屏广告什么时候弹出?

在测试项目里,插屏广告并不是插件自动弹出的。

插件只提供能力,真正什么时候弹,要由游戏业务调用:

ts 复制代码
AdMobSDK.loadAndShowInterstitial(unitId, callbacks);

或者:

ts 复制代码
AdMobSDK.loadInterstitial(unitId, callbacks);
AdMobSDK.showInterstitial(unitId);

也就是说,插屏广告的弹出时机应该由游戏控制,例如:

  • 关卡结束后
  • 返回主页前
  • 游戏失败后
  • 每 N 局一次
  • 冷却时间满足后
  • 非新手流程中

这点很重要。

插件不应该擅自决定广告策略,否则很难控制用户体验,也容易违反广告展示节奏要求。


十二、这次迁移踩到的几个坑

1. 不能照搬 Cocos 3.x build hook

3.x 的插件结构和 2.4.x 不同。

最终方案是用 2.4.x 的 packages 本地插件机制,并监听 Builder 事件。

2. 构建目录需要动态识别

不能写死一个路径。插件里会尝试:

text 复制代码
frameworks/runtime-src/proj.android-studio
proj.android-studio
buildDest

只要找到 settings.gradle,就认为这是 Android 工程根目录。

3. patch 必须幂等

构建可能执行很多次。

如果每次都追加代码,很快就会重复 import、重复 init、重复 dependency。

所以解决方案是:

  • settings.gradle 追加前先判断
  • dependency 追加前先判断
  • AppActivity 先清理旧代码再插入
  • Android 模块目录每次重新复制干净版本

4. AppActivity 插入点要基于 2.4.x

3.x 的初始化锚点不一定存在。

2.4.x 常见的是:

java 复制代码
SDKWrapper.getInstance().init(this);
SDKWrapper.getInstance().onDestroy();

5. 新版 AdMob 需要 AndroidX

Google Mobile Ads SDK 和 native templates 对 AndroidX 更敏感。

所以必须处理:

properties 复制代码
android.useAndroidX=true
android.enableJetifier=true

6. Cocos 2.4.x 资源合并逻辑要兼容新 AGP

旧模板里可能还有:

gradle 复制代码
variant.mergeAssets.doLast

新版 AGP 下需要改成:

gradle 复制代码
variant.mergeAssetsProvider.configure { mergeAssetsTask ->
    mergeAssetsTask.doLast {
        ...
    }
}

否则构建阶段可能直接报错。


十三、最终使用流程

实际接入时,推荐流程是:

  1. 用 Cocos Creator 2.4.x 打开项目。
  2. 构建 Android。
  3. 插件自动 patch Android 工程。
  4. 或手动执行菜单:
text 复制代码
AdMob 2.4.x > Patch Current Android Build
  1. 打开:
text 复制代码
build/jsb-default/frameworks/runtime-src/proj.android-studio
  1. 用 Android Studio 同步工程。
  2. 替换正式 AdMob App ID 和广告单元 ID。
  3. 真机测试广告加载、展示、关闭、奖励回调。
  4. 最后再接入正式广告策略。

十四、总结

这次 AdMob for Cocos Creator 2.4.x 的核心不是"把 SDK 放进去"这么简单,而是解决三层问题:

第一层是 Android 原生能力:

libadmobnativetemplates、Google Mobile Ads SDK 接进 Android 工程。

第二层是 Cocos 2.4.x 构建适配:

处理 Gradle、AGP、NDK、AndroidX、AppActivity、资源合并等旧模板问题。

第三层是游戏脚本调用:

AdMobSDK.ts 封装 JS 到 Java 的调用,让业务层只关心广告加载、展示和回调。

最终得到的方案是一个项目级本地插件:

text 复制代码
packages/admob-24x

它可以在 Cocos Creator 2.4.x 构建 Android 后自动修补生成工程,让 2.4.x 老项目也能接入较新的 AdMob 能力。

这类迁移最关键的经验是:
不要迷信原来的 3.x 插件结构,要先尊重 2.4.x 生成工程的真实形状。

先手动跑通 Android 工程,再把每一步变成可重复、幂等的 patch,才是比较稳的做法。

相关推荐
Flynt1 小时前
升级Flutter 3.44,我踩了HCPP和AGP 9的坑
android·flutter·dart
我命由我123454 小时前
C++ - 面向对象 - 常成员函数
android·java·linux·c语言·开发语言·c++·算法
tryqaaa_4 小时前
学习日志(四)【php反序列化魔术方法以及pop构造配实战】
android
Java小学生丶6 小时前
记录一下我的 Gradle 开发环境配置过程
android·java·gradle·maven·安卓
问心无愧05137 小时前
ctf show web 入门256
android·前端·笔记
霸道流氓气质7 小时前
MySQL 索引设计实战指南
android·数据库·mysql
R语言爱好者7 小时前
叠氮酸介绍
android
方白羽7 小时前
Android WebView 中实现第三方 QQ 登录的架构与流程详解
android·app
鹧鸪晏7 小时前
Android GLSurfaceView 完全指南
android·音视频开发