侧边栏壁纸
博主头像
火腾

行动起来,活在当下

  • 累计撰写 15 篇文章
  • 累计创建 12 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

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

温馨提示:
本文章权益归属火腾(www.firedance.cn),转载请注明来源于火腾(www.firedance.cn)。

最近做了一个针对 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 工程通常在:

build/jsb-default/frameworks/runtime-src/proj.android-studio

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

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

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 里同步和打包。

插件入口在:

packages/admob-24x/main.js

它做的事情主要包括:

copyAndroidModules()
patchRootBuildGradle()
patchGradleWrapper()
patchGradleProperties()
patchSettingsGradle()
patchApplicationGradle()
patchLibraryGradle()
patchAppActivity()

三、Android 模块如何接入

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

packages/admob-24x/template/android/libadmob
packages/admob-24x/template/android/nativetemplates

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

build/jsb-default/frameworks/runtime-src/proj.android-studio/libadmob
build/jsb-default/frameworks/runtime-src/proj.android-studio/nativetemplates

然后修改 settings.gradle

include ':libadmob'
include ':nativetemplates'

再修改 app 模块依赖:

implementation project(':libadmob')

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


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

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

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 构建结果。

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

appendLineIfMissing(file, "include ':libadmob'");
appendLineIfMissing(file, "include ':nativetemplates'");

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

import com.cocos.admob.AdServiceHub;
AdServiceHub.instance().init(this);
AdServiceHub.instance().destroy();

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


六、AppActivity 生命周期接入

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

插件会修改:

app/src/org/cocos2dx/javascript/AppActivity.java

插入 import:

import com.cocos.admob.AdServiceHub;

在初始化位置插入:

AdServiceHub.instance().init(this);

在销毁位置插入:

AdServiceHub.instance().destroy();

这里有一个迁移坑:
不能假设 3.x 的 Activity 结构和 2.4.x 一样。

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

SDKWrapper.getInstance().init(this);
SDKWrapper.getInstance().onDestroy();

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


七、Gradle / API 35 兼容问题

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

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

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';

然后自动修改:

build.gradle
gradle-wrapper.properties
gradle.properties
app/build.gradle
libadmob/build.gradle
nativetemplates/build.gradle

同时开启:

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,而是优先读取生成工程里的:

local.properties

从里面解析:

ndk.dir=...

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

这样可以避免:

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

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


九、JS 层怎么调用 AdMob

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

assets/script/admob/AdMobSDK.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),
});

激励视频:

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

对应测试脚本在:

script/test/AdmobTestInterstitialAd.ts
script/test/AdmobTestRewarded.ts
script/test/AdmobTestRewardedInterstitialAd.ts
script/test/AdmobTestNative.ts

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


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

在测试项目里,插屏广告并不是插件自动弹出的。
插件只提供能力,真正什么时候弹,要由游戏业务调用:

AdMobSDK.loadAndShowInterstitial(unitId, callbacks);

或者:

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. 构建目录需要动态识别

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

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 常见的是:

SDKWrapper.getInstance().init(this);
SDKWrapper.getInstance().onDestroy();

5. 新版 AdMob 需要 AndroidX

Google Mobile Ads SDK 和 native templates 对 AndroidX 更敏感。
所以必须处理:

android.useAndroidX=true
android.enableJetifier=true

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

旧模板里可能还有:

variant.mergeAssets.doLast

新版 AGP 下需要改成:

variant.mergeAssetsProvider.configure { mergeAssetsTask ->
    mergeAssetsTask.doLast {
        ...
    }
}

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


十三、最终使用流程

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

  1. 用 Cocos Creator 2.4.x 打开项目。
  2. 构建 Android。
  3. 插件自动 patch Android 工程。
  4. 或手动执行菜单:
AdMob 2.4.x > Patch Current Android Build
  1. 打开:
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 的调用,让业务层只关心广告加载、展示和回调。

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

packages/admob-24x

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

这类迁移最关键的经验是:
不要迷信原来的 3.x 插件结构,要先尊重 2.4.x 生成工程的真实形状。
先手动跑通 Android 工程,再把每一步变成可重复、幂等的 patch,才是比较稳的做法。

0

评论区