Cocos2d-x Android Studio 项目配置完全指南:Gradle、NDK 与 JNI 实战

引言

Cocos2d-x 作为经典的跨平台游戏引擎,其 Android 平台的构建配置一直是开发者关注的重点。随着 Android Studio 成为官方推荐的开发环境,掌握 Cocos2d-x 在 Android Studio 中的项目配置变得至关重要。本文将从零开始,详细介绍 Cocos2d-x 3.x 项目在 Android Studio 中的完整配置流程,包括 Gradle 构建、NDK 编译、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
┌─────────────────────────────────────────────────────────────────────┐
│ Cocos2d-x Android Studio 项目结构 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ MyGame/ │
│ ├── cocos2d/ # Cocos2d-x 引擎源码 │
│ │ ├── cocos/ │
│ │ ├── external/ │
│ │ └── ... │
│ ├── Classes/ # 游戏 C++ 源码 │
│ │ ├── AppDelegate.cpp │
│ │ ├── HelloWorldScene.cpp │
│ │ └── ... │
│ ├── Resources/ # 游戏资源文件 │
│ └── proj.android-studio/ # Android Studio 项目目录 │
│ ├── app/ # 游戏主模块 │
│ │ ├── build.gradle │
│ │ ├── src/ │
│ │ ├── jni/ │
│ │ │ ├── Android.mk │
│ │ │ └── Application.mk │
│ │ └── AndroidManifest.xml │
│ ├── settings.gradle │
│ ├── build.gradle │
│ └── gradle.properties │
│ │
└─────────────────────────────────────────────────────────────────────┘

基础配置文件

local.properties(本地环境配置)

local.properties 用于指定本地 Android SDK 和 NDK 路径,该文件不应加入版本控制。

1
2
3
4
5
6
# This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.

# Location of the SDK. This is only used by Gradle.
ndk.dir=C:\\android-ndk-r16b
sdk.dir=C:\\Users\\Administrator\\AppData\\Local\\Android\\Sdk
属性 说明 示例
ndk.dir NDK 安装路径 C:\android-ndk-r16b
sdk.dir Android SDK 路径 C:\Users\...\Android\Sdk

settings.gradle(模块配置)

settings.gradle 定义了项目包含的模块及其路径。注意 libcocos2dx 的相对路径必须正确。

1
2
3
4
5
include ':libcocos2dx'
project(':libcocos2dx').projectDir = new File(settingsDir, '../cocos2d/cocos/platform/android/libcocos2dx')

include ':xxxGame'
project(':xxxGame').projectDir = new File(settingsDir, 'app')

注意事项

  • libcocos2dx 版本不能太低,否则可能出现 No implementation found for void org.cocos2dx.lib.Cocos2dxHelper.nativeSetAudioDeviceInfo 错误
  • xxxGame 替换为实际的游戏项目名称

gradle.properties(项目属性)

1
2
3
4
5
# 目标 SDK 版本
PROP_TARGET_SDK_VERSION=14

# 支持的 ABI 架构
PROP_APP_ABI=armeabi-v7a
属性 说明 常用值
PROP_TARGET_SDK_VERSION Android 平台版本 14, 21, 28
PROP_APP_ABI 目标 CPU 架构 armeabi-v7a, arm64-v8a, x86

Gradle 构建配置

顶层 build.gradle

项目级的 build.gradle 配置构建工具和仓库地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
}
}

allprojects {
repositories {
jcenter()
google()
}
}

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'])
}

游戏主模块 build.gradle

游戏模块的配置最为复杂,需要配置 NDK 编译、签名和资源路径:

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
99
100
import org.apache.tools.ant.taskdefs.condition.Os

apply plugin: 'com.android.application'

android {
compileSdkVersion 24
buildToolsVersion "26.0.2"

// 设置编码为 UTF-8
compileOptions.encoding = "UTF-8"

defaultConfig {
applicationId "com.xxxx.xxxGame"
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

// 配置 NDK 模块路径
def module_paths = [
project.file("../../cocos2d").absolutePath,
project.file("../../cocos2d/cocos").absolutePath,
project.file("../../cocos2d/external").absolutePath
]

// Windows 路径转换
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(':')
}

// 并行编译,使用所有 CPU 核心
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 {
path "jni/Android.mk"
}
}

// 签名配置
signingConfigs {
release {
keyAlias 'xxx'
keyPassword 'xxxx'
storeFile file('E:/xxxx.keystore')
storePassword 'xxx'
}
}

buildTypes {
release {
minifyEnabled false
externalNativeBuild {
ndkBuild {
arguments 'NDK_DEBUG=0'
}
}
signingConfig signingConfigs.release
}
debug {
externalNativeBuild {
ndkBuild {
arguments 'NDK_DEBUG=1'
}
}
}
}
}

dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile project(':libcocos2dx')
}
配置项 说明
externalNativeBuild NDK 构建配置
ndkBuild.targets 编译目标名称
arguments '-j' 并行编译线程数
abiFilters 过滤支持的 CPU 架构
assets.srcDir 游戏资源目录路径

NDK 编译配置

Android.mk(自动遍历 C++ 源文件)

Android.mk 是 NDK 构建的核心文件。以下配置实现了自动遍历 Classes 目录下的所有 .cpp 文件:

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
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

# 添加 Cocos2d-x 模块搜索路径
$(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

# 自动收集 Classes 目录下所有文件
ALLFILES = $(call walk, $(LOCAL_PATH)/../../../Classes)

# 过滤出 .cpp 文件
FILE_LIST := $(filter %.cpp, $(ALLFILES))

# 添加额外的 C 源文件
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)

# 链接 Cocos2d-x 静态库
LOCAL_STATIC_LIBRARIES := cocos2dx_static

include $(BUILD_SHARED_LIBRARY)

$(call import-module,.)

核心技巧说明

技巧 作用
walk 递归遍历目录,自动发现所有源文件
uniq 去除重复的头文件包含路径
filter %.cpp 从遍历结果中筛选 C++ 源文件
$(FILE_LIST:$(LOCAL_PATH)/%=%) 将绝对路径转换为相对路径

Application.mk(编译选项配置)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 使用 GNU STL 静态库
APP_STL := gnustl_static

# C++ 编译选项
APP_CPPFLAGS := -frtti -DCC_ENABLE_CHIPMUNK_INTEGRATION=1 -std=c++11 -fsigned-char
APP_LDFLAGS := -latomic

# 短命令模式(Windows 路径长度限制)
APP_SHORT_COMMANDS := true

# 忽略 format-security 警告
APP_CPPFLAGS += -Wno-error=format-security

# Debug/Release 配置
ifeq ($(NDK_DEBUG),1)
APP_CPPFLAGS += -DCOCOS2D_DEBUG=1
APP_OPTIM := debug
else
APP_CPPFLAGS += -DNDEBUG
APP_OPTIM := release
endif
参数 说明
APP_STL C++ 标准库实现,gnustl_staticc++_static
APP_CPPFLAGS 传递给 C++ 编译器的额外参数
APP_SHORT_COMMANDS 解决 Windows 命令行长度限制
NDK_DEBUG Debug 模式开关,由 Gradle 传入

AndroidManifest.xml 配置

游戏主模块 AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xxxx.xxxGame"
android:installLocation="auto">

<!-- 声明 OpenGL ES 2.0 支持 -->
<uses-feature android:glEsVersion="0x00020000" />

<application
android:label="@string/app_name"
android:icon="@drawable/icon">

<!-- 指定 native library 名称 -->
<meta-data android:name="android.app.lib_name"
android:value="cocos2dcpp" />

<activity android:name="com.xxxx.xxxGame.AppActivity"
android:label="@string/app_name"
android:launchMode="singleTop"
android:screenOrientation="sensorPortrait"
android:configChanges="orientation|keyboardHidden"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

<!-- 支持多种屏幕尺寸 -->
<supports-screens
android:anyDensity="true"
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true" />

<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
属性 说明
android.app.lib_name 对应 Android.mk 中的 LOCAL_MODULE_FILENAME
android:screenOrientation 屏幕方向,sensorPortrait 为竖屏
android:configChanges 防止配置变化时 Activity 重建

AppActivity 闪屏实现

自定义 AppActivity

在 Cocos2d-x 资源加载期间,Android 会显示黑屏。通过自定义 AppActivity 添加闪屏图片,可以显著提升用户体验:

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
package com.gamedo.clonehero.pass;

import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import org.cocos2dx.lib.Cocos2dxActivity;

public class AppActivity extends Cocos2dxActivity {

protected static Handler mUIHandler;
private Context mContext;
private static ImageView mWelcome = null;

/**
* 创建闪屏 ImageView
*/
protected ImageView createLaunchImage() {
mWelcome = new ImageView(mContext);
mWelcome.setImageResource(R.drawable.splash);
mWelcome.setScaleType(ImageView.ScaleType.CENTER_CROP);
return mWelcome;
}

/**
* 移除闪屏(由 C++ 层在资源加载完成后调用)
*/
public static void removeLaunchImage() {
mUIHandler.post(new Runnable() {
@Override
public void run() {
if (mWelcome != null) {
mWelcome.setVisibility(View.GONE);
}
}
});
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

mContext = this;
mUIHandler = new Handler();

// 添加闪屏视图覆盖黑屏
addContentView(
createLaunchImage(),
new WindowManager.LayoutParams(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
);

// 初始化 JNI 服务
JniService.initWithActivity(this);

// 延迟 5 秒后自动移除闪屏(兜底方案)
new Thread() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
removeLaunchImage();
}
}.start();
}

static {
System.loadLibrary("cocos2dcpp");
}
}

C++ 层调用移除闪屏

AppDelegate.cpp 的资源加载完成后,调用 Java 方法移除闪屏:

1
2
3
4
5
6
7
8
9
10
11
#include "platform/android/jni/JniHelper.h"

void AppDelegate::applicationDidFinishLaunching() {
// 初始化导演、场景等...

// 资源加载完成,通知 Java 层移除闪屏
cocos2d::JniHelper::callStaticVoidMethod(
"com/gamedo/clonehero/pass/AppActivity",
"removeLaunchImage"
);
}

常见问题与解决方案

问题一:No implementation found for nativeSetAudioDeviceInfo

原因libcocos2dx 模块版本过低,缺少对应的 native 方法实现。

解决方案:升级 libcocos2dx 模块到与引擎版本匹配的最新版本。

问题二:Windows 路径过长导致编译失败

原因:Windows 命令行有最大长度限制。

解决方案:在 Application.mk 中开启短命令模式:

1
APP_SHORT_COMMANDS := true

问题三:format-security 编译错误

原因:NDK 默认将 format 警告视为错误。

解决方案:修改 NDK 的 default-build-commands.mk 文件:

1
2
3
4
5
# 原配置
TARGET_FORMAT_STRING_CFLAGS := -Wformat -Werror=format-security

# 修改为
TARGET_FORMAT_STRING_CFLAGS := -Wformat

或在 Application.mk 中添加:

1
APP_CPPFLAGS += -Wno-error=format-security

问题四:gradle sync 失败

原因:Gradle 版本与 Android Gradle Plugin 不兼容。

解决方案:在 gradle-wrapper.properties 中指定兼容的 Gradle 版本:

1
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip

配置对照速查表

文件 作用 关键配置
local.properties 本地 SDK/NDK 路径 ndk.dir, sdk.dir
settings.gradle 模块定义 include, projectDir
gradle.properties 构建属性 PROP_TARGET_SDK_VERSION, PROP_APP_ABI
build.gradle (顶层) 项目级构建 classpath, repositories
build.gradle (app) 应用级构建 externalNativeBuild, signingConfigs
Android.mk NDK 源文件配置 LOCAL_SRC_FILES, LOCAL_C_INCLUDES
Application.mk NDK 编译选项 APP_STL, APP_CPPFLAGS
AndroidManifest.xml 应用清单 activity, uses-permission

总结

Cocos2d-x Android Studio 项目配置的核心要点:

  1. 路径配置:正确设置 local.properties 中的 SDK/NDK 路径,以及 settings.gradle 中的模块路径
  2. Gradle 配置:主模块 build.gradle 需配置 NDK 编译参数、模块路径和签名信息
  3. 自动遍历:利用 Android.mk 中的 walk 宏自动收集 Classes 目录下的源文件,避免手动维护文件列表
  4. 闪屏处理:通过自定义 AppActivity 添加闪屏视图,在 C++ 层资源加载完成后移除
  5. 平台适配:Windows 平台注意路径分隔符转换和命令行长度限制

通过合理的配置,可以在 Android Studio 中高效地开发和调试 Cocos2d-x 游戏项目。