Android 开发¶
使用 CCGO 为 Android 构建 C++ 库的完整指南。
概述¶
CCGO 提供全面的 Android 支持:
- 多架构构建:arm64-v8a、armeabi-v7a、x86、x86_64
- AAR 打包:即用型 Android Archive 格式
- Gradle 集成:与 Android Studio 项目无缝集成
- JNI 支持:自动 JNI 包装器生成
- Maven 发布:发布到 Maven Local、Central 或私有仓库
- Docker 构建:无需本地 Android SDK/NDK 安装即可构建
前置条件¶
选项 1:本地开发¶
必需: - Android SDK(API 级别 21+) - Android NDK(r21+,推荐:r25+) - CMake(3.18+) - Python(3.8+)
安装:
# 安装 Android Studio(包含 SDK)
# 从 https://developer.android.com/studio 下载
# 设置环境变量
export ANDROID_HOME=$HOME/Android/Sdk
export ANDROID_NDK=$ANDROID_HOME/ndk/25.2.9519653
# 验证安装
ccgo check android --verbose
选项 2:基于 Docker 的开发¶
必需: - Docker Desktop
首次构建下载预构建镜像(约 3.5GB,5-10 分钟)。后续构建使用缓存镜像。
快速开始¶
创建新项目¶
# 创建新的 Android 兼容项目
ccgo new my-android-lib
cd my-android-lib/my-android-lib
# 为 Android 构建
ccgo build android
构建单个架构¶
构建多个架构¶
# 为 arm64-v8a 和 armeabi-v7a 构建
ccgo build android --arch arm64-v8a,armeabi-v7a
# 构建所有架构(默认)
ccgo build android
构建选项¶
# Release 构建(优化)
ccgo build android --release
# Debug 构建(带符号)
ccgo build android --debug
# 清理构建
ccgo build android --clean
# Docker 构建
ccgo build android --docker
# 链接类型控制
ccgo build android --build-as static # 仅静态库
ccgo build android --build-as shared # 仅动态库
ccgo build android --build-as both # 两者都有(默认)
输出结构¶
构建后,在 target/android/ 中查找产物:
target/android/
├── MY-ANDROID-LIB_ANDROID_SDK-1.0.0.zip # 主包
├── MY-ANDROID-LIB_ANDROID_SDK-1.0.0-SYMBOLS.zip # 调试符号
└── build_info.json # 构建元数据
主包结构¶
MY-ANDROID-LIB_ANDROID_SDK-1.0.0.zip
├── lib/
│ ├── static/
│ │ ├── armeabi-v7a/
│ │ │ └── libmy-android-lib.a
│ │ ├── arm64-v8a/
│ │ │ └── libmy-android-lib.a
│ │ ├── x86/
│ │ │ └── libmy-android-lib.a
│ │ └── x86_64/
│ │ └── libmy-android-lib.a
│ └── shared/
│ ├── armeabi-v7a/
│ │ └── libmy-android-lib.so
│ ├── arm64-v8a/
│ │ └── libmy-android-lib.so
│ ├── x86/
│ │ └── libmy-android-lib.so
│ └── x86_64/
│ └── libmy-android-lib.so
├── haars/
│ └── my-android-lib-release.aar
├── include/
│ └── my-android-lib/
│ ├── my-android-lib.h
│ └── version.h
└── build_info.json
符号包结构¶
MY-ANDROID-LIB_ANDROID_SDK-1.0.0-SYMBOLS.zip
└── obj/
├── armeabi-v7a/
│ └── libmy-android-lib.so # 未剥离的带调试符号
├── arm64-v8a/
│ └── libmy-android-lib.so
├── x86/
│ └── libmy-android-lib.so
└── x86_64/
└── libmy-android-lib.so
配置¶
CCGO.toml¶
配置 Android 特定设置:
[package]
name = "my-android-lib"
version = "1.0.0"
[library]
type = "both" # 构建静态和动态库
[android]
min_sdk_version = 21 # Android 5.0 (Lollipop)
target_sdk_version = 33 # Android 13
ndk_version = "25.2.9519653" # 特定 NDK 版本
stl = "c++_static" # STL 类型:c++_static 或 c++_shared
architectures = ["arm64-v8a", "armeabi-v7a", "x86_64"] # 可选:限制架构
[build]
cpp_standard = "17"
compile_flags = ["-Wall", "-Wextra"]
Android 配置选项¶
| 选项 | 类型 | 描述 | 默认值 |
|---|---|---|---|
min_sdk_version |
整数 | 最低 Android API 级别 | 21 |
target_sdk_version |
整数 | 目标 Android API 级别 | 33 |
ndk_version |
字符串 | 特定 NDK 版本 | 最新已安装 |
stl |
字符串 | STL 类型:c++_static、c++_shared |
c++_static |
architectures |
数组 | 要构建的目标 ABI | 所有支持的 |
支持的架构¶
| ABI | 架构 | 描述 |
|---|---|---|
arm64-v8a |
ARM 64 位 | 现代 Android 设备(推荐) |
armeabi-v7a |
ARM 32 位 | 旧版 Android 设备 |
x86_64 |
Intel 64 位 | 模拟器、平板、Chrome OS |
x86 |
Intel 32 位 | 旧版模拟器 |
推荐: 为生产应用构建 arm64-v8a 和 armeabi-v7a。
AAR 集成¶
在 Android 项目中使用 AAR¶
1. 将 AAR 复制到项目:
# 从构建输出复制 AAR
cp target/android/MY-ANDROID-LIB_ANDROID_SDK-1.0.0.zip .
unzip MY-ANDROID-LIB_ANDROID_SDK-1.0.0.zip
cp haars/my-android-lib-release.aar android-app/libs/
2. 配置 app/build.gradle.kts:
android {
// ...
}
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar"))))
// 或
implementation(files("libs/my-android-lib-release.aar"))
}
3. 在 Java/Kotlin 中使用:
class MainActivity : AppCompatActivity() {
companion object {
init {
System.loadLibrary("my-android-lib")
}
}
// 声明原生方法
external fun nativeMethod(): String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 调用原生方法
val result = nativeMethod()
Log.d("Native", "Result: $result")
}
}
JNI 集成¶
自动 JNI 包装器¶
CCGO 可以自动生成 JNI 包装器:
C++ 头文件(include/my-android-lib/my-android-lib.h):
#pragma once
#include <string>
namespace my_android_lib {
class MyLib {
public:
static std::string get_version();
static int calculate(int a, int b);
};
} // namespace my_android_lib
生成的 JNI 包装器(自动生成):
#include <jni.h>
#include "my-android-lib/my-android-lib.h"
extern "C" {
JNIEXPORT jstring JNICALL
Java_com_example_mylib_MyLib_getVersion(JNIEnv* env, jclass) {
std::string version = my_android_lib::MyLib::get_version();
return env->NewStringUTF(version.c_str());
}
JNIEXPORT jint JNICALL
Java_com_example_mylib_MyLib_calculate(JNIEnv*, jclass, jint a, jint b) {
return my_android_lib::MyLib::calculate(a, b);
}
} // extern "C"
手动 JNI 实现¶
创建 src/jni/my_jni.cpp:
#include <jni.h>
#include <string>
#include "my-android-lib/my-android-lib.h"
extern "C" {
JNIEXPORT jstring JNICALL
Java_com_example_MyNativeLib_stringFromJNI(JNIEnv* env, jobject /* this */) {
std::string hello = my_android_lib::MyLib::get_version();
return env->NewStringUTF(hello.c_str());
}
JNIEXPORT jint JNICALL
Java_com_example_MyNativeLib_add(JNIEnv*, jobject, jint a, jint b) {
return my_android_lib::MyLib::calculate(a, b);
}
} // extern "C"
Java/Kotlin 端:
package com.example
class MyNativeLib {
external fun stringFromJNI(): String
external fun add(a: Int, b: Int): Int
companion object {
init {
System.loadLibrary("my-android-lib")
}
}
}
Gradle 集成¶
使用 CCGO Gradle 插件¶
CCGO 提供 Gradle 约定插件用于标准化 Android 构建。
settings.gradle.kts:
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
}
app/build.gradle.kts:
plugins {
id("com.android.library")
id("com.mojeter.ccgo.gradle.android.library")
id("com.mojeter.ccgo.gradle.android.library.native")
}
android {
namespace = "com.example.mylib"
compileSdk = 33
defaultConfig {
minSdk = 21
}
ndkVersion = "25.2.9519653"
}
ccgoNative {
projectPath.set(file("../")) // CCGO 项目根路径
buildType.set("release") // 或 "debug"
architectures.set(listOf("arm64-v8a", "armeabi-v7a"))
}
发布¶
Maven Local(开发)¶
# 发布到 Maven Local 进行测试
ccgo publish android --registry local
# 位置:~/.m2/repository/com/example/my-android-lib/1.0.0/
Maven Central(生产)¶
1. 配置凭据:
创建 ~/.gradle/gradle.properties:
mavenCentralUsername=your-username
mavenCentralPassword=your-password
signing.keyId=12345678
signing.password=your-key-password
signing.secretKeyRingFile=/Users/you/.gnupg/secring.gpg
2. 发布:
私有 Maven 仓库¶
使用已发布的库¶
app/build.gradle.kts:
高级主题¶
多模块项目¶
项目结构:
my-project/
├── CCGO.toml
├── lib1/
│ ├── CCGO.toml
│ └── src/
└── lib2/
├── CCGO.toml(依赖于 lib1)
└── src/
lib2/CCGO.toml:
自定义 CMake 配置¶
CMakeLists.txt:
cmake_minimum_required(VERSION 3.18)
# CCGO 自动提供:
# - ${CCGO_CMAKE_DIR}:CCGO cmake 工具路径
# - ${ANDROID_ABI}:当前正在构建的架构
# - ${ANDROID_PLATFORM}:Android API 级别
include(${CCGO_CMAKE_DIR}/CMakeUtils.cmake)
# 自定义 Android 特定配置
if(ANDROID)
# 添加 Android 特定编译器标志
add_compile_options(-fPIC)
# 链接 Android 库
find_library(LOG_LIB log)
find_library(ANDROID_LIB android)
target_link_libraries(${PROJECT_NAME}
${LOG_LIB}
${ANDROID_LIB}
)
endif()
Proguard 规则¶
创建 proguard-rules.pro:
# 保留原生方法
-keepclasseswithmembernames class * {
native <methods>;
}
# 保留 JNI 导出方法
-keep class com.example.mylib.** { *; }
应用大小优化¶
1. 剥离不需要的符号(在 release 构建中自动):
2. 仅使用必需的架构:
3. 启用链接时优化:
4. 按 ABI 拆分 APK:
app/build.gradle.kts:
android {
splits {
abi {
isEnable = true
reset()
include("arm64-v8a", "armeabi-v7a")
isUniversalApk = false
}
}
}
故障排除¶
常见问题¶
未找到 NDK¶
解决方案:
# 通过 Android Studio 安装 NDK:Tools → SDK Manager → SDK Tools → NDK
# 或手动设置
export ANDROID_NDK=$ANDROID_HOME/ndk/25.2.9519653
# 验证
ccgo check android --verbose
架构不匹配¶
解决方案:
确保 AAR 包含设备/模拟器使用的架构:
# 检查 AAR 内容
unzip -l my-android-lib-release.aar | grep "\.so$"
# 使用正确的架构重新构建
ccgo build android --arch arm64-v8a
C++ 标准不匹配¶
解决方案:
确保依赖之间的 C++ 标准一致:
缺少符号¶
解决方案:
检查所有源文件是否已编译:
Docker 构建问题¶
Docker 未运行¶
解决方案:
镜像拉取失败¶
解决方案:
# 使用手动拉取重试
docker pull ccgogroup/ccgo-builder-android:latest
# 或使用本地构建
cd ccgo/dockers/
docker build -t ccgo-builder-android -f Dockerfile.android .
性能问题¶
首次构建缓慢¶
正常: 首次构建编译所有依赖(约 10-30 分钟)。
优化:
# 使用预构建依赖(未来功能)
ccgo install --prebuilt
# 启用 ccache
export USE_CCACHE=1
export CCACHE_DIR=$HOME/.ccache
增量构建不工作¶
最佳实践¶
1. 版本管理¶
2. 架构选择¶
[android]
# 生产:arm64-v8a + armeabi-v7a(覆盖 99%+ 设备)
architectures = ["arm64-v8a", "armeabi-v7a"]
# 开发:仅 arm64-v8a(更快的构建)
# architectures = ["arm64-v8a"]
3. STL 选择¶
[android]
# 首选 c++_static(无运行时依赖)
stl = "c++_static"
# 仅在多个原生库共享 STL 时使用 c++_shared
# stl = "c++_shared"
4. 依赖管理¶
[dependencies]
# 固定到特定版本以确保可重现性
spdlog = { git = "https://github.com/gabime/spdlog.git", tag = "v1.12.0" }
# 使用 CCGO.lock 进行精确的依赖解析
5. 测试¶
6. CI/CD¶
# .github/workflows/android.yml
name: Android Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Android
run: |
pip install ccgo
ccgo build android --docker
- name: Test
run: ccgo test android --docker
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: android-libs
path: target/android/*.zip
示例¶
完整项目¶
参见 ccgo-now 获取完整的 Android 项目示例。
最小示例¶
// include/mylib/mylib.h
#pragma once
#include <string>
namespace mylib {
std::string get_greeting();
int add(int a, int b);
}
// src/mylib.cpp
#include "mylib/mylib.h"
namespace mylib {
std::string get_greeting() {
return "Hello from C++!";
}
int add(int a, int b) {
return a + b;
}
}
构建:
在 Android 中使用:
class MainActivity : AppCompatActivity() {
init {
System.loadLibrary("mylib")
}
external fun getGreeting(): String
external fun add(a: Int, b: Int): Int
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val greeting = getGreeting()
val result = add(2, 3)
Log.d("MyLib", "$greeting, 2+3=$result")
}
}