Unity 通过 NativePlugin 接入Android SDK 指南

本指南详细讲解 Unity 项目中集成 Android 原生插件(C/C++)的两种实现方式,帮助开发者根据项目需求选择合适方案。


目录

  1. 方案对比
  2. [源码集成方案(仅支持 IL2CPP)](#源码集成方案(仅支持 IL2CPP))
  3. [预编译库方案(兼容 Mono 和 IL2CPP)](#预编译库方案(兼容 Mono 和 IL2CPP))
  4. [.so 文件编译指南](#.so 文件编译指南)
  5. [C# 调用接口实现](# 调用接口实现)
  6. 疑难解答

两种实现方式对比

特性 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(必需)

工作原理

  1. Unity 导出 Android 工程时自动扫描 .cpp 文件
  2. 内置 NDK 将源码编译并静态链接至 libil2cpp.so
  3. 运行时通过 __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: MonoIL2CPP(均支持)

如何编译 .so 文件

准备工作

  1. 安装 Android NDK

  2. 安装 CMake(可选)

方法一: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集成

  1. 新建Native C++项目
  2. 将源码放入app/src/main/cpp/
  3. 修改CMakeLists.txt配置
  4. 执行Build > Make Project
  5. 产物路径: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 架构不匹配

解决方法

  1. 确认 .so 文件存放在 Assets/Plugins/Android/libs/{ABI}/ 目录下
  2. 检查是否编译了目标设备的 CPU 架构(arm64-v8a/armeabi-v7a 等)
  3. 建议改用 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: 如何调试原生代码?

调试方案

  1. Android Studio 调试

    • 导出 Unity 项目为 Android 工程
    • 在 Android Studio 中导入项目
    • 设置断点进行调试
  2. 日志输出

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);
}

参考资源

相关推荐
n***84071 小时前
Springboot-配置文件中敏感信息的加密:三种加密保护方法比较
android·前端·后端
方白羽3 小时前
一次由 by lazy 引发的“数据倒灌”,深入理解 `by`关键字、`lazy`函数的本质
android·kotlin·app
v***55343 小时前
MySQL 中如何进行 SQL 调优
android·sql·mysql
jtymyxmz4 小时前
《Unity Shader》10.2.1 镜子效果
unity·游戏引擎
ellis19704 小时前
Unity打开新项目Package相关报错处理记录
unity
vx_vxbs665 小时前
【SSM高校普法系统】(免费领源码+演示录像)|可做计算机毕设Java、Python、PHP、小程序APP、C#、爬虫大数据、单片机、文案
android·java·python·mysql·小程序·php·idea
微:xsooop5 小时前
iOS 上架4.3a 审核4.3a 被拒4.3a 【灾难来袭】
flutter·unity·ios·uniapp
微光守望者5 小时前
Unity ScriptableObject详解:优化游戏架构的强大工具
unity·游戏引擎
j***82706 小时前
【MyBatisPlus】MyBatisPlus介绍与使用
android·前端·后端