背景
Cocos2d-x 虽然现在慢慢被 Creator 取代了,但还是有很多老项目需要维护。把 Cocos2d-x 项目配置到 Android Studio 里开发是个挺折腾的事,涉及 Gradle、NDK、Android.mk 多个环节。这篇文章记录我折腾过程中遇到的各种问题和解决方案。
环境准备
版本要求
| 组件 |
推荐版本 |
说明 |
| Android Studio |
3.0+ |
支持 Gradle 4.x |
| Gradle |
4.1 |
通过 wrapper 配置 |
| Android SDK |
24+ |
compileSdkVersion |
| NDK |
r16+ |
支持 gnustl_static |
| Cocos2d-x |
3.14+ |
较稳定的版本 |
目录结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| cocos2d-x-project/ ├── cocos2d/ # 引擎目录 │ ├── cocos/ │ ├── external/ │ └── cocos/platform/android/libcocos2dx/ ├── Classes/ # 游戏代码 ├── Resources/ # 资源文件 └── proj.android-studio/ # Android Studio 项目 ├── app/ │ ├── build.gradle │ ├── jni/Android.mk │ ├── jni/Application.mk │ └── src/ ├── build.gradle ├── gradle.properties └── settings.gradle
|
完整配置详解
1. 项目级 build.gradle
文件路径: proj.android-studio/build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
buildscript { repositories { jcenter() google() } dependencies { classpath 'com.android.tools.build:gradle:2.3.3'
} }
allprojects { repositories { jcenter() google() } }
|
关键配置说明:
gradle:2.3.3 - 与 Gradle 4.1 兼容的插件版本
google() - 必须添加,用于下载 Android Support 库
2. Gradle 配置
文件路径: proj.android-studio/gradle.properties
1 2
| PROP_TARGET_SDK_VERSION=14 PROP_APP_ABI=armeabi-v7a
|
属性说明:
| 属性 |
说明 |
可选值 |
| PROP_TARGET_SDK_VERSION |
目标平台版本 |
14-28 |
| PROP_APP_ABI |
编译的 CPU 架构 |
armeabi-v7a, arm64-v8a, x86, x86_64 |
注意: armeabi 已在 NDK r17 中移除,请使用 armeabi-v7a。
3. Gradle Wrapper 配置
文件路径: proj.android-studio/gradle/wrapper/gradle-wrapper.properties
1 2 3 4 5
| distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
版本对应关系:
| Gradle 插件版本 |
需要的 Gradle 版本 |
| 2.3.3 |
3.3+ |
| 3.0.0 |
4.1+ |
| 3.1.0 |
4.4+ |
4. App 模块 build.gradle
文件路径: proj.android-studio/app/build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.application'
android { compileSdkVersion 24 buildToolsVersion "26.0.2"
defaultConfig { applicationId "com.game.xxx" minSdkVersion 10 targetSdkVersion 23 versionCode 1 versionName "1.0"
externalNativeBuild { ndkBuild { if (!project.hasProperty("PROP_NDK_MODE") || PROP_NDK_MODE.compareTo('none') != 0) { targets 'cocos2dcpp' arguments 'NDK_TOOLCHAIN_VERSION=4.9' arguments 'APP_PLATFORM=android-'+PROP_TARGET_SDK_VERSION
def module_paths = [project.file("../../cocos2d").absolutePath, project.file("../../cocos2d/cocos").absolutePath, project.file("../../cocos2d/external").absolutePath] if (Os.isFamily(Os.FAMILY_WINDOWS)) { module_paths = module_paths.collect {it.replaceAll('\\\\', '/')} arguments 'NDK_MODULE_PATH=' + module_paths.join(";") } else { arguments 'NDK_MODULE_PATH=' + module_paths.join(':') }
arguments '-j' + Runtime.runtime.availableProcessors() abiFilters.addAll(PROP_APP_ABI.split(':').collect{it as String}) } } } }
sourceSets.main { java.srcDir "src" res.srcDir "res" jniLibs.srcDir "libs" manifest.srcFile "AndroidManifest.xml" assets.srcDir "../../Resources" }
externalNativeBuild { ndkBuild { if (!project.hasProperty("PROP_NDK_MODE") || PROP_NDK_MODE.compareTo('none') != 0) { path "jni/Android.mk" } } }
signingConfigs { release { if (project.hasProperty("RELEASE_STORE_FILE")) { storeFile file(RELEASE_STORE_FILE) storePassword RELEASE_STORE_PASSWORD keyAlias RELEASE_KEY_ALIAS keyPassword RELEASE_KEY_PASSWORD } } }
buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' if (project.hasProperty("RELEASE_STORE_FILE")) { signingConfig signingConfigs.release }
externalNativeBuild { ndkBuild { arguments 'NDK_DEBUG=0' } } }
debug { externalNativeBuild { ndkBuild { arguments 'NDK_DEBUG=1' } } } } }
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile project(':libcocos2dx') }
|
配置要点:
- 跨平台路径处理
1 2 3 4 5 6
| if (Os.isFamily(Os.FAMILY_WINDOWS)) { module_paths = module_paths.collect {it.replaceAll('\\\\', '/')} arguments 'NDK_MODULE_PATH=' + module_paths.join(";") } else { arguments 'NDK_MODULE_PATH=' + module_paths.join(':') }
|
- 动态指定编译线程数
1
| arguments '-j' + Runtime.runtime.availableProcessors()
|
- 多 ABI 支持
1
| abiFilters.addAll(PROP_APP_ABI.split(':').collect{it as String})
|
5. Android.mk 配置
文件路径: proj.android-studio/app/jni/Android.mk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
$(call import-add-path,$(LOCAL_PATH)/../../../cocos2d) $(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/external) $(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/cocos) $(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/cocos/audio/include)
LOCAL_MODULE := cocos2dcpp_shared LOCAL_MODULE_FILENAME := libcocos2dcpp
define walk $(wildcard $(1)) $(foreach e, $(wildcard $(1)/*), $(call walk, $(e))) endef
define uniq $(eval seen :=) $(foreach _,$(1),$(if $(filter ${_},${seen}),,$(eval seen += ${_}))) ${seen} endef
ALLFILES = $(call walk, $(LOCAL_PATH)/../../../Classes)
FILE_LIST := $(filter %.cpp, $(ALLFILES)) LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%)
LOCAL_SRC_FILES += ../../../Classes/Libs/SQLite/sqlite3secure.c
FILES_PATH := $(LOCAL_PATH)/../../../Classes FILE_INCLUDES := $(dir $(foreach src_path,$(FILES_PATH), $(call walk,$(src_path),*/) ) ) FILE_INCLUDES := $(call uniq,$(FILE_INCLUDES))
LOCAL_C_INCLUDES := $(FILE_INCLUDES)
LOCAL_STATIC_LIBRARIES := cocos2dx_static
include $(BUILD_SHARED_LIBRARY)
$(call import-module,.)
|
关键技巧:
自动递归收集源文件
walk 函数递归遍历 Classes 目录
- 自动发现所有
.cpp 文件,无需手动添加
自动收集头文件目录
uniq 函数去重处理
- 自动包含所有子目录作为 include 路径
6. Application.mk 配置
文件路径: proj.android-studio/app/jni/Application.mk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -DCC_ENABLE_CHIPMUNK_INTEGRATION=1 -std=c++11 -fsigned-char APP_LDFLAGS := -latomic
APP_SHORT_COMMANDS := true APP_CPPFLAGS += -Wno-error=format-security
ifeq ($(NDK_DEBUG),1) APP_CPPFLAGS += -DCOCOS2D_DEBUG=1 APP_OPTIM := debug else APP_CPPFLAGS += -DNDEBUG APP_OPTIM := release endif
|
配置说明:
| 配置项 |
说明 |
| APP_STL |
使用 GNU STL 静态库 |
| -frtti |
启用运行时类型信息 |
| -std=c++11 |
启用 C++11 支持 |
| APP_SHORT_COMMANDS |
解决 Windows 命令行长度过长问题 |
| COCOS2D_DEBUG |
Debug 模式宏定义 |
7. libcocos2dx 库配置
文件路径: cocos2d/cocos/platform/android/libcocos2dx/build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| apply plugin: 'com.android.library'
android { compileSdkVersion 24 buildToolsVersion "26.0.2"
defaultConfig { minSdkVersion 10 targetSdkVersion 24 versionCode 1 versionName "1.0" }
sourceSets.main { aidl.srcDir "../java/src" java.srcDir "../java/src" manifest.srcFile "AndroidManifest.xml" }
buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) }
|
常见错误与解决方案
错误信息:
1
| error: format not a string literal and no format arguments [-Werror=format-security]
|
解决方案:
方法 1:修改 NDK 配置(不推荐)
1 2
| TARGET_FORMAT_STRING_CFLAGS := -Wformat
|
方法 2:在 Application.mk 中添加(推荐)
1
| APP_CPPFLAGS += -Wno-error=format-security
|
方法 3:修改源代码(彻底解决)
1 2 3 4 5
| env->GetStringUTFChars(name_, 0);
env->GetStringUTFChars(name_, NULL);
|
错误 2:CreateProcess 参数错误
错误信息:
1
| error: process_begin: CreateProcess(...) make (e=87): 参数错误
|
原因: Windows 对命令行参数数量有限制,Android.mk 中包含的源文件太多导致超限。
解决方案:
1 2
| APP_SHORT_COMMANDS := true
|
这会生成中间文件,避免命令行过长。
错误 3:malloc 未声明
错误信息:
1
| 'malloc' was not declared in this scope
|
解决方案:
错误 4:void* 转换错误
错误信息:
1
| invalid conversion from 'void*' to 'char*'
|
解决方案:
1 2
| char* buffer = (char*)malloc(size);
|
错误 5:ABI 不支持
错误信息:
1 2 3 4
| Android NDK: INTERNAL ERROR: The armeabi ABI should have exactly one architecture definitions.
Error: ABIs [armeabi] are not supported for platform. Supported ABIs are [armeabi-v7a, arm64-v8a, x86, x86_64].
|
解决方案:
1 2 3 4 5 6
|
PROP_APP_ABI=armeabi
PROP_APP_ABI=armeabi-v7a
|
错误 6:Gradle 版本不兼容
错误信息:
1
| Error: Unsupported method: BaseConfig.getApplicationIdSuffix().
|
解决方案:
1 2 3 4 5 6
| buildscript { dependencies { classpath 'com.android.tools.build:gradle:2.3.3' } }
|
编译与调试
命令行编译
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| cd proj.android-studio
./gradlew clean
./gradlew assembleDebug
./gradlew assembleRelease
./gradlew installDebug
|
调试配置
在 Android Studio 中配置 C++ 调试:
- Edit Configurations → 添加 Android Native
- Module: app
- Debugger: Dual (Java + Native)
日志过滤
1 2 3 4 5
| #include "cocos2d.h"
CCLOG("Debug message: %s", message); CCLOGERROR("Error: %d", errorCode);
|
ADB 日志过滤:
1
| adb logcat -s cocos2d-x:D *:S
|
性能优化
编译优化
1 2 3
| APP_CPPFLAGS += -O3 -DNDEBUG APP_OPTIM := release
|
减小 APK 体积
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| android { defaultConfig { ndk { abiFilters 'armeabi-v7a' } }
buildTypes { release { minifyEnabled true shrinkResources true } } }
|
小结
Cocos2d-x 配置到 Android Studio 的几条经验:
- 版本匹配 - Gradle 插件与 Gradle 版本要对应
- 路径处理 - Windows 需要特殊处理路径分隔符
- ABI 选择 - 避免使用已废弃的 armeabi
- 命令长度 - Windows 下使用 APP_SHORT_COMMANDS
- 代码规范 - 修复 format-security 警告,使用显式类型转换
希望这篇文章能帮你少走弯路,顺利把 Cocos2d-x 项目跑起来。