本指南详细讲解 Unity 项目中集成 Android 原生插件(C/C++)的两种实现方式,帮助开发者根据项目需求选择合适方案。
目录
- 方案对比
- [源码集成方案(仅支持 IL2CPP)](#源码集成方案(仅支持 IL2CPP))
- [预编译库方案(兼容 Mono 和 IL2CPP)](#预编译库方案(兼容 Mono 和 IL2CPP))
- [.so 文件编译指南](#.so 文件编译指南)
- [C# 调用接口实现](# 调用接口实现)
- 疑难解答
两种实现方式对比
| 特性 | C++ 源文件 | 预编译 .so 文件 |
|---|---|---|
| IL2CPP 支持 | ✅ | ✅ |
| Mono 支持 | ❌ | ✅ |
| DllImport 参数 | __Internal |
"库名称" |
| NDK 编译需求 | Unity 自动处理 | 需手动编译 |
| 调试便捷性 | 高(支持源码直接修改) | 低(需重新编译) |
| 部署复杂度 | 简单 | 中等 |
| 适用场景 | 纯 IL2CPP 项目 | 需兼容 Mono 环境 |
实现方案一:C++ 源文件(IL2CPP 专用)
最佳适用场景
- 采用 IL2CPP 作为唯一脚本后端
- 追求简化的部署流程
- 需要频繁迭代原生代码
项目结构
Assets/Plugins/Android/
└── SampleSDKBridge.cpp
C# 调用方式
csharp
// 使用 __Internal 标识符调用静态链接函数
[DllImport("__Internal")]
private static extern void Init(string projectId, ...);
Unity 配置要求
Player Settings > Other Settings > Configuration
- Scripting Backend: IL2CPP(必需)
工作原理
- Unity 导出 Android 工程时自动扫描
.cpp文件 - 内置 NDK 将源码编译并静态链接至
libil2cpp.so - 运行时通过
__Internal调用已链接的本地函数
实现方案二:预编译动态库(双后端兼容)
最佳适用场景
- 需同时支持 Mono 和 IL2CPP
- 需要保护源码不外泄
- 需针对不同 CPU 架构进行优化
项目结构
Assets/Plugins/Android/
├── libs/
│ ├── arm64-v8a/
│ │ └── libSampleSDKBridge.so
│ ├── armeabi-v7a/
│ │ └── libSampleSDKBridge.so
│ └── x86/
│ └── libSampleSDKBridge.so
└── SampleSDKBridge.cpp.meta (可选占位文件)
C# 调用方式
csharp
// 使用动态库名称(不含 lib 前缀和 .so 后缀)
[DllImport("SampleSDKBridge")]
private static extern void Init(string projectId, ...);
Unity 配置要求
Player Settings > Other Settings > Configuration
- Scripting Backend: Mono 或 IL2CPP(均支持)
如何编译 .so 文件
准备工作
-
安装 Android NDK
- 版本要求:NDK r21+
- 下载地址:https://developer.android.com/ndk/downloads
- 配置环境变量:
export ANDROID_NDK_HOME=/path/to/ndk
-
安装 CMake(可选)
- macOS:
brew install cmake - Windows: 官网下载安装包 https://cmake.org/download/
- macOS:
方法一:ndk-build(推荐方案)
1. 创建目录结构
jni/
├── Android.mk
├── Application.mk
└── SampleSDKBridge.cpp
2. 配置 Android.mk
makefile
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# 输出库名称
LOCAL_MODULE := SampleSDKBridge
# 源文件列表
LOCAL_SRC_FILES := SampleSDKBridge.cpp
# 依赖的系统库
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
3. 配置 Application.mk
makefile
# 支持的CPU架构
APP_ABI := armeabi-v7a arm64-v8a x86
# 使用静态C++库
APP_STL := c++_static
# 最低API级别
APP_PLATFORM := android-21
# C++11标准
APP_CPPFLAGS := -std=c++11
4. 执行编译
bash
cd /project/path
$ANDROID_NDK_HOME/ndk-build
5. 输出目录
libs/
├── arm64-v8a/
│ └── libSampleSDKBridge.so
├── armeabi-v7a/
│ └── libSampleSDKBridge.so
└── x86/
└── libSampleSDKBridge.so
方法二:CMake方案
1. 项目结构
project/
├── CMakeLists.txt
├── src/
│ └── SampleSDKBridge.cpp
└── build/
2. 配置CMakeLists.txt
cmake
cmake_minimum_required(VERSION 3.10)
project(SampleSDKBridge)
set(CMAKE_CXX_STANDARD 11)
add_library(
SampleSDKBridge
SHARED
src/SampleSDKBridge.cpp
)
target_link_libraries(
SampleSDKBridge
log
)
3. 编译脚本
bash
#!/bin/bash
NDK=$ANDROID_NDK_HOME
BUILD_DIR=build
ABIS=("armeabi-v7a" "arm64-v8a" "x86")
for ABI in "${ABIS[@]}"; do
echo "Building $ABI..."
mkdir -p $BUILD_DIR/$ABI
cd $BUILD_DIR/$ABI
cmake ../.. \
-DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=$ABI \
-DANDROID_PLATFORM=android-21 \
-DCMAKE_BUILD_TYPE=Release
make -j4
cd ../..
done
4. 执行编译
bash
chmod +x build_android.sh
./build_android.sh
方法三:Android Studio集成
- 新建Native C++项目
- 将源码放入
app/src/main/cpp/ - 修改CMakeLists.txt配置
- 执行Build > Make Project
- 产物路径:
app/build/intermediates/cmake/
C#调用实现
通用调用方式
csharp
using System.Runtime.InteropServices;
public static class SampleSDKBridge
{
#if UNITY_ANDROID && !UNITY_EDITOR
#if ENABLE_IL2CPP
const string LibraryName = "__Internal";
#else
const string LibraryName = "SampleSDKBridge";
#endif
#elif UNITY_IOS && !UNITY_EDITOR
const string LibraryName = "__Internal";
#else
const string LibraryName = "__Internal";
#endif
[DllImport(LibraryName)]
public static extern void Init(
string projectId,
string appId,
string sourceFlag,
string serverUrl,
int mode
);
}
预编译库专用方式
csharp
public static class SampleSDKBridge
{
#if UNITY_IOS && !UNITY_EDITOR
const string LibraryName = "__Internal";
#elif UNITY_ANDROID && !UNITY_EDITOR
const string LibraryName = "SampleSDKBridge";
#else
const string LibraryName = "__Internal";
#endif
[DllImport(LibraryName)]
public static extern void Init(...);
}
完整示例项目
目录结构
OneAnalyticsSDKBridge/
├── jni/
│ ├── Android.mk
│ ├── Application.mk
│ └── SampleSDKBridge.cpp
├── build.sh
└── output/
核心代码实现
cpp
#include <jni.h>
static JavaVM* g_JavaVM = nullptr;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
g_JavaVM = vm;
return JNI_VERSION_1_6;
}
extern "C" {
void Init(const char* projectId, const char* appId,
const char* sourceFlag, const char* serverUrl, int mode) {
// JNI实现代码
}
}
编译脚本 build.sh
bash
#!/bin/bash
# 配置 NDK 路径
NDK_PATH=${ANDROID_NDK_HOME:-~/Library/Android/sdk/ndk/21.4.7075529}
# 验证 NDK 是否存在
if [ ! -d "$NDK_PATH" ]; then
echo "Error: NDK directory not found at $NDK_PATH"
echo "Please ensure ANDROID_NDK_HOME is properly set"
exit 1
fi
# 清理历史构建文件
rm -rf libs obj
# 执行 NDK 编译
$NDK_PATH/ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=jni/Android.mk
# 验证构建结果
if [ -d "libs" ]; then
echo -e "\nBuild successful! Output:"
find libs -name "*.so" -exec ls -lh {} \;
# 自动部署到 Unity 项目
UNITY_PLUGIN_PATH="../Unity/Assets/Plugins/Android/libs"
if [ -d "$UNITY_PLUGIN_PATH" ]; then
cp -r libs/* $UNITY_PLUGIN_PATH/
echo -e "\nSuccessfully deployed to Unity project: $UNITY_PLUGIN_PATH"
fi
else
echo "Build failed!"
exit 1
fi
常见问题解答
Q1: 运行时报错 "Unable to find SampleSDKBridge"
可能原因:
- Mono 构建环境下缺少预编译的 .so 文件
- .so 文件存放路径不正确
- 目标设备 CPU 架构不匹配
解决方法:
- 确认 .so 文件存放在
Assets/Plugins/Android/libs/{ABI}/目录下 - 检查是否编译了目标设备的 CPU 架构(arm64-v8a/armeabi-v7a 等)
- 建议改用 IL2CPP 构建方式
Q2: JNI 调用出现崩溃
常见原因:
- JNIEnv 指针失效
- 类名或方法签名不匹配
- 未正确释放本地引用
最佳实践:
cpp
// 安全获取 JNIEnv 实例
JNIEnv* GetJNIEnv() {
JNIEnv* env = nullptr;
if (g_JavaVM->GetEnv((void**)&env, JNI_VERSION_1_6) == JNI_EDETACHED) {
g_JavaVM->AttachCurrentThread(&env, nullptr);
}
return env;
}
// 规范使用本地引用
void SampleFunction() {
JNIEnv* env = GetJNIEnv();
jclass cls = env->FindClass("com/example/MyClass");
// ... 业务逻辑 ...
env->DeleteLocalRef(cls); // 及时释放资源
}
Q3: 如何查看支持的 CPU 架构?
Unity 设置路径 : Player Settings > Other Settings > Target Architectures
主流架构说明:
armeabi-v7a: 32位 ARM(兼容旧设备)arm64-v8a: 64位 ARM(主流设备,符合 Google Play 要求)x86: 32位 x86(模拟器适用)x86_64: 64位 x86(模拟器适用)
Q4: 如何调试原生代码?
调试方案:
-
Android Studio 调试:
- 导出 Unity 项目为 Android 工程
- 在 Android Studio 中导入项目
- 设置断点进行调试
-
日志输出:
cpp
#include <android/log.h>
#define LOG_TAG "NativeDebug"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
void DebugFunction(...) {
LOGD("Function called with params: %s", param);
}