一、本文环境.
1. 电脑: Mac mini (Late 2014).
2. 系统: Mac OS X 10.15.1
3. 开发工具:
- Cocos Creator 2.2.1(当下最新的稳定版本,Cocos2d游戏开发工具).
- Visual Studio Code 1.40.2(简称: VSCode,很好用的代码编辑器,搭配Creator进行代码编辑).
- Android Studio 3.5.2(Android 原生开发工具,NDK-r17.2 应 Cocos 文档说明 2.0.9 以下不能安装r18 以上的版本).
4. 本次对接的广告是 穿山甲(v2.5.2.6), 头条-巨量引擎(v3.3.12) 和 腾讯联盟(广点通)(v4.10.19) 两家的.
5. 本次对接的统计是 友盟统计(v8.1.4).
6. 介绍手动打包和使用 Fastlane 工具进行自动化打包.
二、环境安装.
注:会安装环境的,或已经安装好环境的可以直接跳过查看 下一点 - 对接SDK.
安装 Android Studio,由于 Cocos Creator 不同的版本对 Android 环境有不同的要求,所以请移步 Cocos 文档-安装配置原生开发环境 根据文档说明一步步下载安装配置。文档说的很详细,这边就不再说明了.
- 注意:修改右上方的 Version: 2.0,改成跟你的版本对应的版本文档,因为版本不一样,要求很可能也不一样.

三、导出 Android 项目工程(会的可以跳过).
1. 安装好 Android 开发环境后,在 Creator 上打开对应的项目,然后在 Creator 的 偏好设置 -> 原生开发环境 面板设置 NDK 和 Android SDK 的路径.

2. 设置完之后,选择 项目 -> 构建发布(Mac快捷键 Common + Shift + B) 打开构建面板.
- 发布平台选择
Android. - 模板选择
link. - 填入 Android 的包名(一般为三级:
com.xxxx.xxxxx). - Target API Level 这个大概是一个API 等级,选择一个.
- APP ABI 为项目架构,如果要在模拟器上运行,一定要勾起
x86。 如果要在真机上运行,要把上面两个arm勾起来(别问为什么,勾起来就对了). - 设备方向选择游戏对应的方向,依次是正方向竖屏、反方向竖屏、左横屏、右横屏.
- 取消勾选加密脚本,方便调试,而且在 Android 上也可以进行项目混淆加密,当然,如果是为了发布,不需要调试,可以勾选.
- 其他保持默认的就行,不要去乱动,不然报错了不给你 报销 .
- 上面的设置完毕之后,点击构建,构建导入 Android 项目.
当构建完成之后,可以在 项目/build/jsb-link/frameworks/runtime-src/ 目录下看到我们导出的工程。
可以看到除了 Android 工程之外,连 iOS、Mac、Windows 的项目工程也帮我们一起导出了.
3. 导出工程后,先不急着对接,用 Android Studio 打开运行,确定导出的项目工程可以正常运行,没有任何问题后,再继续看下面的 对接SDK.
注: 不会用 Android Studio 打开项目的自行百度,也不难,我就不在一步步说明了.
导出之后,如果运行报错,可以尝试修改
build.gradle里面几个版本配置的值.
默认情况如下:



如果报下面这个错误的,解决方案: 添加录音权限.

<uses-permission android:name="android.permission.RECORD_AUDIO"/>

四、 对接 穿山甲广告SDK - 对接文档.
1. 下载 穿山甲 Android SDK ,下载下来的是一个包含 SDK 和 Demo 的压缩包,双击解压缩.


2. 解压缩之后,回到项目,在 app 目录下 新建 libs 文件夹 ,然后把 open_ad_sdk.aar 复制到 libs 目录下.


3. 然后,打开 app/build.gradle 滚动到文件底部在 depedencies 内添加下面这句代码.
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.6'
添加完,点击右上方的 Sync Now 同步一下,然后运行一下项目,保证不报错,再进行下一步.

Error:Execution failed for task':app:transformDexArchiveWithExternalLibsDexMergerForDebug'大意是资源重复了。这个错误我弄了大半天,最后还是求助我一个做 Android 的朋友远程帮我解决的(他只用了小半个小时就搞定了,大佬就是大佬).
解决方案,大致就是在 app/build.gradle 文件的 android 添加下面这句代码. 添加完别忘了
Sync Now.
multiDexEnabled true

4. 确认 SDK 导入没有问题后,开始进行配置,首先是添加权限,可以根据项目自身情况在 AndroidManifest.xml 文件内添加相关的权限.
<!-- 获取网络权限. -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- 获取网络信息状态权限. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!-- 获取 Wi-Fi 信息状态权限. -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<!-- 上边三个默认帮我们配置好了,我们需要添加以下的权限. -->
<!-- 可选权限. -->
<!-- 获取电话状态权限(敏感权限,上架 Google pay 可能会被拒绝,可以去掉,不影响使用). -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<!-- 允许程序写入外部存储权限. -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 通过WiFi或移动基站的方式获取用户错略的经纬度信息. -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<!-- 请求安装包权限. -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<!-- 允许程序获取当前或最近运行的应用. -->
<uses-permission android:name="android.permission.GET_TASKS"/>
<!-- 可选,通过GPS芯片接收卫星的定位信息,穿山甲将依据此权限投放精准广告. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!-- 如果有视频相关的广告且使用textureView播放,请务必添加,否则黑屏. -->
<!-- 允许程序在手机屏幕关闭后后台进程仍然运行. -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!-- 运行环境配置(运行于 Android4.0 (API Level 14) 及以上版本). -->
<!-- <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="24"/> -->
5. 添加完权限之后,在 <Application> 标签内添加以下代码.
<provider
android:name="com.bytedance.sdk.openadsdk.TTFileProvider"
android:authorities="${applicationId}.TTFileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<provider
android:name="com.bytedance.sdk.openadsdk.multipro.TTMultiProvider"
android:authorities="${applicationId}.TTMultiProvider"
android:exported="false" />
6. 然后在 app/res 目录下添加 xml 文件夹,并在 xml 文件夹内新建 file_paths.xml.

7. 创建完替换文件内容为以下内容.
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--为了适配所有路径可以设置 path = "." -->
<external-files-path name="external_files_path" path="." />
</paths>
8. 然后,为了适配下载和安装相关功能,回到 app/build.gradle 文件,在 dependencies 内添加
implementation 'com.android.support:support-v4:23.0.0'
如果项目还有需要别的权限,可以看 这篇文章 添加对应的权限.
慎重起见,每做完一个步骤,需要运行一下项目,确认没有问题后,再进行下一步操作(后面不再提示)!
这边真机运行可能会报下面这个错误(模拟器太卡没有开...),那么,你很可能是跟我一样,手贱不知道改了什么,这时候,我推荐你删掉重来吧,反正我是这么解决的!

9. 初始化SDK需要在 穿山甲后台 注册账号,注册应用,拿到 AppID ;后续的广告位也是要在这边创建,然后拿到广告代码位,项目里的广告才能正常显示!不懂的这边有教程: 注册认证流程.


10. 在 res/org.cocos2dx.javescript/AppActivity 的 onCreate 方法内添加下面的初始化代码(替换 appID 跟 appName).
// 请求权限.
TTAdSdk.getAdManager().requestPermissionIfNecessary(this);
// appId 应用ID.
// useTextureView 使用 TextureView 控件播放视频, 默认为 SurfaceView, 当有 SurfaceView 冲突的场景,可以使用 TextureView.
// allowShowNotify 是否允许 SDK 展示通知栏提示.// allowShowPageWhenScreenLock 是否在锁屏场景支持展示广告落地页.
// debug 测试阶段打开,可以通过日志排查问题,上线时去除该调用.// directDownloadNetworkType 允许直接下载的网络状态集合.
// supportMultiProcess是否支持多进程,true支持.
TTAdConfig config = new TTAdConfig.Builder()
.appId("5001121")
.useTextureView(false)
.appName("APP测试媒体")
.titleBarTheme(TTAdConstant.TITLE_BAR_THEME_DARK)
.allowShowNotify(true)
.allowShowPageWhenScreenLock(true)
.debug(debug)
.directDownloadNetworkType(TTAdConstant.NETWORK_STATE_WIFI, TTAdConstant.NETWORK_STATE_3G)
.supportMultiProcess(false)
.build();
// 初始化穿山甲SDK.
TTAdSdk.init(this, config);
当在控制台看见下面的 log 信息时,就代表初始化成功,可以开始接入具体广告了.

后面就是一些具体的广告组件了,对着官方的 接入文档 拷贝代码就行了,没有太大的难度,毕竟我这个完全不会安卓的都接完了(代码质量请忽略).
我是将穿山甲 SDK 的广告组件等的实现代码封装成了工具类,方便公司内部同事使用;由于是内部使用代码,这边就不公开了!下面是我封装过程中遇到的一些问题的解决方案:
JsonObject 解析值为 null.
Android 中获取当前正在显示的 Activity 实例.
Android 在布局中动态添加 view 的两种方法.
Android 控件 view 的可见,不可见,隐藏的设置和区别.
Android 获取控件宽高和屏幕宽高.
Android中实现延时执行操作的几种方法.
五、对接 头条-巨量引擎 - 对接文档.
1. 导入SDK.
1.1 这边采取手动引入的方式,因为远端引入的方式这边报错了,首先下载SDK文件: 下载地址.


RangersAppLog-Lite-cn-3.3.12.aar 文件拷贝到 项目/app/libs/ 目录下.

jcenter()。

2. 导入SDK后,可以开始初始化SDK了!
在 Application 中 onCreate 中初始化 RangersAppLog (初始化需要尽可能早)
提示: SDK默认支持多进程初始化.
/* 初始化开始. */
// appid和渠道,appid须保证与广告后台申请记录一致.
// 渠道可自定义,如有多个马甲包建议设置渠道号唯一标识一个马甲包.
final InitConfig config = new InitConfig("App ID", "Channel");
/**
* 域名默认国内: DEFAULT, 新加坡:SINGAPORE, 美东:AMERICA.
* 注意:国内外不同vendor服务注册的did不一样。由DEFAULT切换到SINGAPORE或者AMERICA,会发生变化.
* 切回来也会发生变化。因此vendor的切换一定要慎重,随意切换导致用户新增和统计的问题,需要自行评估.
*/
config.setUriConfig(UriConfig.DEFAULT);
// 是否在控制台输出日志,可用于观察用户行为日志上报情况,建议仅在调试时使用,release版本请设置为false.
AppLog.setEnableLog(true);
// 设置 AppName 与在后台申请 AppID 时填的名称一样.
config.setAppName("App Name");
// 游戏模式,YES会开始 playSession 上报,每隔一分钟上报心跳日志.
config.setEnablePlay(true);
// 是否在控制台输出日志,可用于观察用户行为日志上报情况,建议仅在调试时使用,release版本请设置为false.
AppLog.setEnableLog(true);
// 日志加密,release 版本请设置为 true.
AppLog.setEncryptAndCompress(true);
// 调用初始化函数.
AppLog.init(this, config);
/* 初始化结束. */
// 自定义 “用户公共属性”(可选,初始化后调用, key相同会覆盖).
Map<String,Object> headerMap = new HashMap<String, Object>();
headerMap.put("level", 8);
headerMap.put("gender", "female");
AppLog.setHeaderInfo((HashMap<String, Object>)headerMap);
3. 初始化完成后,就是上报埋点的 API 调用了, 比较简单, 这边就不做说明了, 结合自身情况接入即可! 没有什么难度, 基本就是拷贝粘贴代码! 请参照: 文档 -> Android 端 SDK 使用说明.
六、 对接 友盟统计SDK - 对接文档.
1. 导入 SDK,由于官方支持 自动集成(推荐) 和 手动集成 两种方式;我这边选择 自动集成 方式,避免了手动下载 SDK 导入项目工程的麻烦。如果你要 手动集成 可以打开的 对接文档-手动集成 照着文档操作即可.
1.1 在工程 build.gradle 配置脚本中 buildscript 和 allprojects 段中添加【友盟+】SDK 新 maven 仓库地址,添加完记得点击 Sync Now 同步.
maven { url 'https://dl.bintray.com/umsdk/release' }

1.2 同步完之后,在工程 App 对应 build.gradle 配置脚本 dependencies 段中添加 基础组件库和 统计 SDK 库依赖!同样的添加完记得点击 Sync Now 同步.
implementation 'com.umeng.umsdk:analytics:8.1.4'
implementation 'com.umeng.umsdk:common:2.1.8'
注意:这边是在 app 目录下,不是上边那个项目工程的!文件名一样,容易搞混.

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- 必要的权限. -->
<!-- 检测联网方式,在网络异常状态下避免数据发送,节省流量和电量. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!-- 获取用户设备的 IMEI,通过 IMEI 对用户进行唯一标识,以便提供统计分析服务. -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<!-- 获取 WIFI mac 地址,在平板设备或电视盒子上,无法通过 IMEI 标识设备,我们会将 WIFI mac 地址作为用户的唯一标识,以便正常提供统计分析服务. -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<!-- 允许应用程序联网和发送统计数据的权限,以便提供统计分析服务. --><uses-permission android:name="android.permission.INTERNET"/>
<!-- 可选的权限. -->
<!-- 为开发者提供反作弊功能,剔除作弊设备,让统计数据(如新增用户数)更加准确. -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 为开发者提供反作弊功能,剔除作弊设备,让统计数据(如新增用户数)更加准确. -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- 通过获取位置信息,为开发者提供反作弊功能,剔除作弊设备;同时校正用户的地域分布数据,使报表数据更加准确. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!-- 通过获取位置信息,为开发者提供反作弊功能,剔除作弊设备;同时校正用户的地域分布数据,使报表数据更加准确. -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

2. 添加完权限就可以开始初始化 SDK 了,在 app/src/ 下的 AppActivity 的 onCreate 方法内添加初始化代码.
/**
* context 上下文环境(this).
* appkey 在友盟后台创建App后得到的应用唯一标识符.
* channel 渠道字符串,不要用中文.
* deviceType 设备类型,UMConfigure.DEVICE_TYPE_PHONE 为手机、UMConfigure.DEVICE_TYPE_BOX 为盒子,默认为手机.
* pushSecret 推送业务的 secret,需要集成 Push 功能时必须传入 Push 的 secret,否则传空.
*/
UMConfigure.init(this, "appkey", "channel", UMConfigure.DEVICE_TYPE_PHONE, null);
// 使用 MANUAL 页面采集模式.
MobclickAgent.setPageCollectionMode(MobclickAgent.PageMode.MANUAL);
// 支持在子进程中统计自定义事件.
UMConfigure.setProcessEvent(true);
3. 设置 log 日志打印开关,如果想要查看初始化过程中的 log,一定要在调用初始化方法前将 log 开关打开.
UMConfigure.setLogEnabled(true);
4. Session启动、App使用时长等基础数据统计接口API.\
在 App 中每个 Activity 的 onResume 方法中调用 MobclickAgent.onResume(context), onPause 方法中调用 MobclickAgent.onPause(context).
@Override
public void onResume() {
super.onResume();
MobclickAgent.onResume(this);
}
@Override
public void onPause() {
super.onPause();
MobclickAgent.onPause(this);
}
5. 发送自定义事件,比较麻烦的就是需要在应用后台创建统计事件,拿到 事件ID(eventId) !只有在后台创建的事件才会统计.
/**
* 发送自定义事件.
* @param context 上下文.
* @param eventId 事件ID.
* @param name 事件名称.
*/
MobclickAgent.onEvent(context, eventId, name);
6. 发送自定义事件 - 自定义参数.
/**
* 发送自定义事件.
* @param context 上下文.
* @param eventId 事件ID.
* @param map 自定义参数.
*/
MobclickAgent.onEventObject(activity, eventId, map);
7. 发送自定义事件 - 自定义计数.
/**
* 发送自定义事件.
* @param context 上下文.
* @param eventId 事件ID.
* @param map 自定义参数.
* @param count 自定义计数.
*/
MobclickAgent.onEventObject(activity, eventId, map, count);
统计使用到的内容不多,到此就结束了.
七、对接 腾讯联盟(广点通) - 接入文档.
- 因为最近工作繁忙,腾讯联盟(广点通) 部分暂时没空整理!后续空出时间了会再更新!
八、打包导出 APK.
当要接的 SDK 都接完之后,就是打包 APK 了!
1. 选择工具栏的 Build -> Generate Signed Bundle / APK, 打开 Generate Signed Bundle / APK 面板.

2. 选择 APK,然后点击 Next 按钮进入下一步.

3. 接着进入签名密钥信息界面.

Create new... 按钮进入创建面板创建一个密钥文件.
4. 进入 New Key Store 面板.
- Key store path: 密钥文件的导出路径,点击后面的文件夹图标选择存放路径.
- Password: 文件密码.
- Confirm: 确认密码.
- Alias: 别名(无特殊要求,保持默认即可).
- Password && Confirm: 密码,最好跟上面的密码一致.
- Validity(years): 密钥文件的有效年限.
- Certificate Info: 证书信息,可以不填.
- 填完信息点击
OK按钮生成密钥文件.
这边的密码可以设置的简单好记一点,以后还会用到!比如:123456


OK 按钮之后,可能会报这个错误,根据提示运行命令行即可.
- 打开命令行界面,输入以下命令:
keytool -importkeystore -srckeystore 原文件路径 -destkeystore 导出文件路径 -deststoretype pkcs12
- 原文件路径与导出文件路径可以是同一个,会自动帮我们生成一个 .old 的备份文件.
- 输完命令回车之后,要求输入密钥文件的密码(就是上面生成文件时输入的密码), 输完密码出现以下输出就成功迁移到 PKCS12 了.

5. 生成完密钥文件会自动回到上一个 签名密钥信息 界面.
- 可以看到自动帮我们填完了相关信息,直接点击
Next按钮进入下一步骤即可.
6. 下一步后进入编译面板,选择完相关选项之后,点击 Finish 按钮,之后会自动关闭这个弹出面板, 进入打包状态,然后等就行了.

7. 可以在 app/build.gradle 文件末尾加入以下代码修改导出的 APK 文件名.
def releaseTime() {
// GMT+8是因为北京时间和GMT有8个小时时差.
return new Date().format("yyyy-MM-dd HHMM", TimeZone.getTimeZone("GMT+8"))
}
android.applicationVariants.all {
variant ->
variant.outputs.all {
// 这里修改apk文件名.
def fileName = "package_v${variant.versionName}_${variant.name}_${releaseTime()}.apk"
outputFileName = fileName
}
}


8. 前面打包一直报错 java.io.IOException: Please correct the above warnings first.,百度找了很多资料都不对,最后请教了一下大佬才解决.

就是在 app/proguard-rules.pro 文件内加入以下代码忽略警告:
-ignorewarnings

9. 使用 Fastlane 打包.
没有听过或用过 Fastlane 这个工具的,可以看 这篇文章 了解,或自行百度也行,网上关于 Fastlane 的介绍还是很多的.
总的来说, Fastlane 就是一个自动化打包的命令行工具, 只要配置好项目, 一句命令行就能帮你做好签名、编译、导出、上传等的事情, 不用每次打包都需要你一步步手动操作的一个工具.
然而,Fastlane 对 Android 平台支持有限;Fastlane 更适用于 iOS!Android 只适合上传到 Google Play。
用法参考资料:
Fastlane 官方文档 - Android.
fastlane 实现 Android 自动化打包.
Fastlane Actions - gradle.
Fastlane Actions - build_android_app.
Fastfile:
- 其中签名文件需要在项目 根目录 和 app 目录下都放一个,否则会报错.
default_platform(:android)
platform :android do
lane :product do
puts "=====> 开始打包 APK."
gradle(task: "clean")
gradle(
task: "assemble",
build_type: "Release",
properties: {
"android.injected.signing.store.file" => "签名文件.",
"android.injected.signing.store.password" => "文件密码.",
"android.injected.signing.key.alias" => "别名.",
"android.injected.signing.key.password" => "别名密码.",
}
)
end
end
配置完,终端执行以下命令,然后等待执行完成即可.
fastlane product

fastlane.tools finished successfully 🎉 就代表命令执行完成,并且打包成功!
而且上面也显示了执行总时长与各 Action 的执行时长.
之后就可以在项目的 app/build/outputs/apk/release 目录下看到我们导出的 APK 安装包了.

