{
  "schemaVersion": "0.11.0",
  "canonical": "https://www.pystone.net/notes/unity-csharp-java-native-interop/",
  "atlas": "https://www.pystone.net/?node=unity-csharp-java-native-interop#knowledge-atlas",
  "markdown": "https://www.pystone.net/notes/unity-csharp-java-native-interop.md",
  "context": "https://www.pystone.net/notes/unity-csharp-java-native-interop.context.json",
  "knowledgeVersion": "224c990773de.5fa8af6e39fa",
  "build": {
    "siteCommit": "224c990773de166d23a886306577dd90379529ce",
    "notesCommit": "5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a",
    "builtAt": "1970-01-01T00:00:00.000Z",
    "version": "224c990773de.5fa8af6e39fa"
  },
  "id": "note:unity-csharp-java-native-interop",
  "slug": "unity-csharp-java-native-interop",
  "title": "Unity跨语言编程精华：CSharp与Java、Native代码的链式调用实践",
  "type": "note",
  "visibility": "public",
  "idStability": "rename-stable",
  "author": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "publisher": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "aliases": [],
  "summary": "本文深入剖析Unity中C 与Java/Native代码互调技术，详解JNI动态绑定、AndroidStudio插件工程配置及P/Invoke内存管理机制，提供链式调用实战案例（C →Java→C++→Java）。涵盖JNIEnv线程绑定策略、JVM内存模型对跨语言调用的影响，帮助你打通Unity Android原生层开发之路。",
  "contentRole": "unspecified",
  "isMoc": false,
  "mocRecognition": "none",
  "generated": false,
  "attribution": "unspecified",
  "domain": "10-计算机、信息技术与工程",
  "tags": [],
  "mocs": [],
  "contentHash": "c960a11a682b4510f7e59b4076516aa60f114040a4b7ecd8e7b67de74897d2d5",
  "assets": [
    {
      "reference": "assets/image-20240607183651124.png",
      "url": "/media/76861ec96789743d969f.png",
      "mediaType": "image/png",
      "contentHash": "76861ec96789743d969f7eb6a871adc6f3560e9fcbb02a569556f48cac3b6ca9",
      "byteLength": 92636,
      "width": 1670,
      "height": 551
    }
  ],
  "headings": [
    {
      "depth": 1,
      "text": "Unity跨语言编程精华：CSharp与Java、Native代码的链式调用实践",
      "anchor": "unity跨语言编程精华csharp与javanative代码的链式调用实践",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#unity%E8%B7%A8%E8%AF%AD%E8%A8%80%E7%BC%96%E7%A8%8B%E7%B2%BE%E5%8D%8Ecsharp%E4%B8%8Ejavanative%E4%BB%A3%E7%A0%81%E7%9A%84%E9%93%BE%E5%BC%8F%E8%B0%83%E7%94%A8%E5%AE%9E%E8%B7%B5"
    },
    {
      "depth": 2,
      "text": "Unity C#调用Java代码",
      "anchor": "unity-c调用java代码",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#unity-c%E8%B0%83%E7%94%A8java%E4%BB%A3%E7%A0%81"
    },
    {
      "depth": 4,
      "text": "1. Java 代码",
      "anchor": "1-java-代码",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#1-java-%E4%BB%A3%E7%A0%81"
    },
    {
      "depth": 4,
      "text": "2. Unity C# 代码",
      "anchor": "2-unity-c-代码",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#2-unity-c-%E4%BB%A3%E7%A0%81"
    },
    {
      "depth": 2,
      "text": "Unity C#调用Native代码",
      "anchor": "unity-c调用native代码",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#unity-c%E8%B0%83%E7%94%A8native%E4%BB%A3%E7%A0%81"
    },
    {
      "depth": 2,
      "text": "Java与Native代码互相调用",
      "anchor": "java与native代码互相调用",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#java%E4%B8%8Enative%E4%BB%A3%E7%A0%81%E4%BA%92%E7%9B%B8%E8%B0%83%E7%94%A8"
    },
    {
      "depth": 2,
      "text": "知识储备",
      "anchor": "知识储备",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#%E7%9F%A5%E8%AF%86%E5%82%A8%E5%A4%87"
    },
    {
      "depth": 3,
      "text": "JVM 的底层实现",
      "anchor": "jvm-的底层实现",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#jvm-%E7%9A%84%E5%BA%95%E5%B1%82%E5%AE%9E%E7%8E%B0"
    },
    {
      "depth": 4,
      "text": "1. 线程和调度",
      "anchor": "1-线程和调度",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#1-%E7%BA%BF%E7%A8%8B%E5%92%8C%E8%B0%83%E5%BA%A6"
    },
    {
      "depth": 4,
      "text": "2. 内存管理",
      "anchor": "2-内存管理",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#2-%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86"
    },
    {
      "depth": 4,
      "text": "3. JNI 的底层机制",
      "anchor": "3-jni-的底层机制",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#3-jni-%E7%9A%84%E5%BA%95%E5%B1%82%E6%9C%BA%E5%88%B6"
    },
    {
      "depth": 3,
      "text": "JNIEnv 和 JavaVM 的底层机制",
      "anchor": "jnienv-和-javavm-的底层机制",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#jnienv-%E5%92%8C-javavm-%E7%9A%84%E5%BA%95%E5%B1%82%E6%9C%BA%E5%88%B6"
    },
    {
      "depth": 4,
      "text": "1. JNIEnv 的本质",
      "anchor": "1-jnienv-的本质",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#1-jnienv-%E7%9A%84%E6%9C%AC%E8%B4%A8"
    },
    {
      "depth": 4,
      "text": "2. JavaVM 的本质",
      "anchor": "2-javavm-的本质",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#2-javavm-%E7%9A%84%E6%9C%AC%E8%B4%A8"
    },
    {
      "depth": 4,
      "text": "线程的附加和分离",
      "anchor": "线程的附加和分离",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#%E7%BA%BF%E7%A8%8B%E7%9A%84%E9%99%84%E5%8A%A0%E5%92%8C%E5%88%86%E7%A6%BB"
    },
    {
      "depth": 2,
      "text": "Java代码调用Native代码",
      "anchor": "java代码调用native代码",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#java%E4%BB%A3%E7%A0%81%E8%B0%83%E7%94%A8native%E4%BB%A3%E7%A0%81"
    },
    {
      "depth": 2,
      "text": "Native代码调用Java代码",
      "anchor": "native代码调用java代码",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#native%E4%BB%A3%E7%A0%81%E8%B0%83%E7%94%A8java%E4%BB%A3%E7%A0%81"
    },
    {
      "depth": 3,
      "text": "基础",
      "anchor": "基础",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#%E5%9F%BA%E7%A1%80"
    },
    {
      "depth": 3,
      "text": "进阶-以JavaVM为中介",
      "anchor": "进阶-以javavm为中介",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#%E8%BF%9B%E9%98%B6-%E4%BB%A5javavm%E4%B8%BA%E4%B8%AD%E4%BB%8B"
    },
    {
      "depth": 2,
      "text": "Android Studio工程配置",
      "anchor": "android-studio工程配置",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#android-studio%E5%B7%A5%E7%A8%8B%E9%85%8D%E7%BD%AE"
    },
    {
      "depth": 4,
      "text": "1. 创建新的 Android Studio 工程",
      "anchor": "1-创建新的-android-studio-工程",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#1-%E5%88%9B%E5%BB%BA%E6%96%B0%E7%9A%84-android-studio-%E5%B7%A5%E7%A8%8B"
    },
    {
      "depth": 4,
      "text": "2. 配置工程结构",
      "anchor": "2-配置工程结构",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#2-%E9%85%8D%E7%BD%AE%E5%B7%A5%E7%A8%8B%E7%BB%93%E6%9E%84"
    },
    {
      "depth": 5,
      "text": "创建 Java 代码结构",
      "anchor": "创建-java-代码结构",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#%E5%88%9B%E5%BB%BA-java-%E4%BB%A3%E7%A0%81%E7%BB%93%E6%9E%84"
    },
    {
      "depth": 5,
      "text": "创建 Native 代码结构",
      "anchor": "创建-native-代码结构",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#%E5%88%9B%E5%BB%BA-native-%E4%BB%A3%E7%A0%81%E7%BB%93%E6%9E%84"
    },
    {
      "depth": 4,
      "text": "3. 配置 build.gradle 文件",
      "anchor": "3-配置-buildgradle-文件",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#3-%E9%85%8D%E7%BD%AE-buildgradle-%E6%96%87%E4%BB%B6"
    },
    {
      "depth": 4,
      "text": "4. 编译项目",
      "anchor": "4-编译项目",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#4-%E7%BC%96%E8%AF%91%E9%A1%B9%E7%9B%AE"
    },
    {
      "depth": 4,
      "text": "5. 将生成的文件集成到 Unity 中",
      "anchor": "5-将生成的文件集成到-unity-中",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#5-%E5%B0%86%E7%94%9F%E6%88%90%E7%9A%84%E6%96%87%E4%BB%B6%E9%9B%86%E6%88%90%E5%88%B0-unity-%E4%B8%AD"
    },
    {
      "depth": 2,
      "text": "案例：CS->Java->Native->Java",
      "anchor": "案例cs-java-native-java",
      "citation": "https://www.pystone.net/notes/unity-csharp-java-native-interop/#%E6%A1%88%E4%BE%8Bcs-java-native-java"
    }
  ],
  "claims": [],
  "outgoing": [],
  "incoming": [
    {
      "id": "note:programming-languages-and-runtimes",
      "title": "编程语言与运行时",
      "url": "https://www.pystone.net/notes/programming-languages-and-runtimes/",
      "atlas": "https://www.pystone.net/?node=programming-languages-and-runtimes#knowledge-atlas",
      "label": "编程语言与运行时",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "本层中的“Unity跨语言编程精华：CSharp与Java、Native代码的链式调用实践”导航项",
      "citation": "https://www.pystone.net/notes/programming-languages-and-runtimes/#%E6%9C%AC%E5%B1%82"
    }
  ],
  "contentMarkdown": "本文深入剖析Unity中C#与Java/Native代码互调技术，详解JNI动态绑定、AndroidStudio插件工程配置及P/Invoke内存管理机制，提供链式调用实战案例（C#→Java→C++→Java）。涵盖JNIEnv线程绑定策略、JVM内存模型对跨语言调用的影响，帮助你打通Unity-Android原生层开发之路。\n\n# Unity跨语言编程精华：CSharp与Java、Native代码的链式调用实践\n* **JNI (Java Native Interface)** 是一种编程框架，使得 Java 代码可以与使用其他编程语言（例如 C、C++、汇编语言）编写的应用程序和库进行交互。\n\n## Unity C#调用Java代码\n\n#### 1. Java 代码\n```java\npackage com.prin.myplugin;\n\nimport android.util.Log;\n\npublic class TestJavaClass {\n    public static String myJavaMethod(String message) {\n        Log.d(\"MyJavaClass\", message);\n        return \"Response from Java: \" + message;\n    }\n\n    public static int multiply(int a, int b) {\n        return a * b;\n    }\n}\n\n```\n\n可以直接将Java代码放置在Unity工程的Android目录中： `Assets/Plugins/Android/`  。\n也可以在Android Studio中将Java代码打成Jar/aar包后，再拷贝到 `Assets/Plugins/Android/` 中。\n\n#### 2. Unity C# 代码\n在 Unity 项目中创建一个新的 C# 脚本文件（例如 `TestCallJava.cs`），并编写如下代码：\n```cs\nusing System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\n\npublic class TestCallJava : MonoBehaviour\n{\n    public void CallJava()\n    {\n        // 获取 UnityPlayer 类\n        AndroidJavaClass unityPlayer = new AndroidJavaClass(\"com.unity3d.player.UnityPlayer\");\n        // 获取当前活动\n        AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>(\"currentActivity\");\n        // 调用自定义 Java 类的方法\n        AndroidJavaClass myJavaClass = new AndroidJavaClass(\"com.prin.myplugin.TestJavaClass\");\n\n        // 调用 Java 静态方法并获取返回值\n        string response = myJavaClass.CallStatic<string>(\"myJavaMethod\", \"Hello from Unity!\");\n        Debug.Log(response);\n\n        // 调用 Java 静态方法 Multiply\n        int res = myJavaClass.CallStatic<int>(\"Multiply\", 3, 4);\n        Debug.Log(\"Multiply result: \" + res);\n    }\n}\n\n```\n\n\n## Unity C#调用Native代码\n- **P/Invoke 声明**：在 C# 中使用 `[DllImport(\"library-name\")]` 属性来声明外部的 C 函数，`library-name` 是不带前缀 `lib` 和后缀 `.so` 的库名。\n- 参数传递: `Marshal` 通常用于处理非托管代码中的复杂数据类型，比如字符串、结构体、指针等，而基本数据类型的传递可以通过 P/Invoke 直接完成。\n- **内存管理**：由于 Native 代码返回的字符串在堆上分配，需要在 C# 中调用相应的释放函数来释放内存，防止内存泄漏。\n\n\nNative代码：\n```cpp\n#include <cstring>\n#include <string>\n\nextern \"C\" {\n    const char* stringFromNative(const char* input) {\n        std::string response = \"Response from Native: \";\n        response += input;\n        char* result = new char[response.size() + 1];\n        std::strcpy(result, response.c_str());\n        return result;\n    }\n\n    void freeNativeString(const char* str) {\n        delete[] str;\n    }\n\n    int add(int a, int b) {\n        return a + b;\n    }\n}\n\n```\n\nCMAKE配置：\n```cmake\ncmake_minimum_required(VERSION 3.4.1)\n\nadd_library(experimental-native-lib SHARED experimental-native-lib.cpp)\n\nfind_library(log-lib log)\n\ntarget_link_libraries(experimental-native-lib ${log-lib})\n\n```\n\nAndroid Studio中构建so代码后,放入Unity工程的 `Assets/Plugins/Android/` 目录下。\n\nC#代码:\n```cs\nusing System;\nusing System.Runtime.InteropServices;\nusing UnityEngine;\n\npublic class TestCallNative : MonoBehaviour\n{\n    // 使用 P/Invoke 声明 C++ 函数\n    [DllImport(\"experimental-native-lib\")]\n    private static extern IntPtr stringFromNative(string input);\n\n    [DllImport(\"experimental-native-lib\")]\n    private static extern void freeNativeString(IntPtr str);\n\n    [DllImport(\"experimental-native-lib\")]\n    private static extern int add(int a, int b);\n\n    public void CallNative()\n    {\n        Debug.Log(\"Call native----------------------------------------\");\n\n        IntPtr responsePtr = stringFromNative(\"Hello from Unity!\");\n        string response = Marshal.PtrToStringAnsi(responsePtr);\n        Debug.Log(response);\n\n        // 释放返回的指针\n        freeNativeString(responsePtr);\n\n        int c = add(3, 4);\n        Debug.Log(\"native 3+4: \" + c);\n    }\n}\n\n```\n\n\n## Java与Native代码互相调用\n## 知识储备\n### JVM 的底层实现\n\n#### 1. 线程和调度\n\nJVM 在启动时会创建多个线程，包括主线程、垃圾收集线程、编译线程等。每个 Java 线程都对应一个操作系统级别的线程。操作系统负责线程的创建、调度和销毁。\n\n- **Java 线程**：在 Java 程序中，每个通过 `new Thread()` 创建的线程都是一个独立的线程，它们共享相同的内存空间。\n- **操作系统线程**：Java 线程被映射为操作系统线程。操作系统负责管理这些线程，包括调度、上下文切换和优先级管理。\n-\n#### 2. 内存管理\nJVM 的内存区域主要包括：\n- **堆 (Heap)**：用于存储对象实例。垃圾收集器负责回收不再使用的对象。\n- **栈 (Stack)**：每个线程都有自己的栈空间，用于存储局部变量和方法调用信息。\n- **方法区 (Method Area)**：存储类信息、常量、静态变量等。\n- **本地方法栈 (Native Method Stack)**：用于支持本地方法执行。\n#### 3. JNI 的底层机制\nJNI 是 Java 调用本地代码的接口，允许 Java 程序与本地应用程序和库进行交互。JNI 使得 Java 程序可以调用用 C/C++ 编写的代码，反之亦然。\n\n### JNIEnv 和 JavaVM 的底层机制\n\n#### 1. JNIEnv 的本质\n\n`JNIEnv` 是一个指向函数表的指针，这些函数表定义了 JNI 提供的所有功能。`JNIEnv` 是线程私有的，每个线程都有一个独立的 `JNIEnv` 实例。\n\n```java\nstruct JNIEnv {\n    const struct JNINativeInterface* functions;\n};\n\nstruct JNINativeInterface {\n    // 定义了所有的 JNI 函数\n    jint (*GetVersion)(JNIEnv *env);\n    jclass (*FindClass)(JNIEnv *env, const char *name);\n    jmethodID (*GetMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig);\n    // 其他函数省略\n};\n\n```\n#### 2. JavaVM 的本质\n\n`JavaVM` 是 JVM 实例的一个抽象表示，通常只有一个全局的 `JavaVM` 指针。`JavaVM` 提供了一些全局操作，比如创建和销毁 JVM，附加和分离线程等。\n\n```java\nstruct JavaVM {\n    const struct JNIInvokeInterface* functions;\n};\n\nstruct JNIInvokeInterface {\n    jint (*DestroyJavaVM)(JavaVM *vm);\n    jint (*AttachCurrentThread)(JavaVM *vm, JNIEnv **p_env, void *thr_args);\n    jint (*DetachCurrentThread)(JavaVM *vm);\n    // 其他函数省略\n};\n\n```\n\n#### 线程的附加和分离\nJava 线程是由 JVM 管理的线程，用于并发执行任务。每个 Java 线程都有一个关联的 `JNIEnv` 指针，这是线程与 JVM 交互的接口。\nNative 线程是由操作系统管理的线程，它们通常用来执行与平台相关的任务。Native 线程可以通过 JNI 与 JVM 进行交互。\n`JNIEnv` 是一个指针，指向 JVM 提供的一组函数表，用于 Native 代码与 JVM 交互。每个 Java 线程都有一个关联的 `JNIEnv`，但 Native 线程默认情况下没有 `JNIEnv`。\n当一个 Native 线程需要与 JVM 交互时，它需要首先附加到 JVM 上，以获得一个 `JNIEnv`。\n当一个 Native 线程附加到 JVM 时，JVM 会为该线程分配一个新的 `JNIEnv` 结构，并将其存储在线程局部存储 (Thread Local Storage, TLS) 中。`AttachCurrentThread` 会执行以下操作：\n- 分配一个新的 `JNIEnv` 实例，并初始化其函数表。\n- 将 `JNIEnv` 指针存储在当前线程的 TLS 中。\n- 将线程注册到 JVM 的线程管理系统中。\n\n```cpp\nJNIEnv* env;\nint getEnvStat = gJvm->GetEnv((void**)&env, JNI_VERSION_1_6);\nif (getEnvStat == JNI_EDETACHED) {\n    if (gJvm->AttachCurrentThread(&env, NULL) != 0) {\n        // 处理附加失败\n    }\n    needDetach = true;\n}\n\n```\n\n## Java代码调用Native代码\n\n当 Java 调用 Native 方法时，JVM 会执行以下步骤：\n- 查找并加载包含 Native 方法实现的共享库。\n- 获取方法的本地函数指针。\n- 创建一个 Native 帧并推送到当前线程的调用栈。\n- 切换到 Native 模式，执行本地函数。\n- 当本地函数返回时，切换回 Java 模式，并弹出 Native 帧。\n\n\n1. 创建一个 Native C++ 库并定义一个带返回值的方法。\n```cpp\n// native-lib.cpp\n#include <jni.h>\n#include <string>\n\nextern \"C\"\nJNIEXPORT jstring JNICALL\nJava_com_example_unityplugin_NativeLib_stringFromJNI(JNIEnv *env, jobject /* this */, jstring input) {\n    const char *inputStr = env->GetStringUTFChars(input, 0); // JNI 提供的函数，用于将 `jstring` 类型的 `input` 转换为 C 风格的字符串 (`const char*`)。\n\n    std::string hello = \"Response from C++: \";\n    hello += inputStr;\n    env->ReleaseStringUTFChars(input, inputStr); //释放之前通过 `GetStringUTFChars` 获取的内存, 是必要的,防止内存泄漏.\n\n    return env->NewStringUTF(hello.c_str());\n}\n\n```\n\n- `env->GetStringUTFChars`  JNI 提供的函数，将 `jstring` 类型的 `input` 转换为 C 风格的字符串 (`const char*`)。\n- `ReleaseStringUTFChars` 释放之前通过 `GetStringUTFChars` 获取的内存, 防止内存泄漏.\n* `hello.c_str()` 返回 C++ 字符串对象 `hello` 的底层 C 风格字符串指针。\n* `env->NewStringUTF` 将 C 风格的字符串 (`const char*`) 转换为 `jstring`，以便返回给 Java 层。\n\n> **UTF (Unicode Transformation Format)** 是一种字符编码形式，用于将 Unicode 字符转换为字节序列。**常见的 UTF 编码格式**：UTF-8, UTF-16等. JNI 提供了函数将 `jstring`（Java 字符串）转换为 UTF-8 编码的 C 字符串，并且允许将 C 字符串转换为 `jstring`。\n\n3. 在 Java 代码中调用 Native 方法并处理返回值。\n```java\n// MainActivity.java\npackage com.example.unityplugin;\n\nimport android.os.Bundle;\nimport android.widget.TextView;\nimport androidx.appcompat.app.AppCompatActivity;\n\npublic class MainActivity extends AppCompatActivity {\n    static {\n        System.loadLibrary(\"native-lib\");\n    }\n\n    public native String stringFromJNI(String input);\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n\n        TextView tv = findViewById(R.id.sample_text);\n        String response = stringFromJNI(\"Hello from Java!\");\n        tv.setText(response);\n    }\n}\n\n```\n\n`extern \"C\"`\n- **含义**：用来告诉编译器按 C 语言的函数命名规则（而不是 C++ 的命名规则）来生成符号。\n- **目的**：防止 C++ 编译器对函数名进行名称改编（name mangling），使其能够与 Java 的 JNI 调用正确匹配。由于 JNI 是以 C 的方式调用方法，所以需要使用 `extern \"C\"`。\n\n JNIEXPORT`\n- **含义**：一个宏，通常在 `jni.h` 中定义，用来修饰 JNI 函数，表示该函数会被导出，使 Java 虚拟机 (JVM) 能够访问到此函数。\n- **目的**：使函数可以被 JVM 调用。根据具体的平台，`JNIEXPORT` 会展开为相应的导出修饰符，例如在 Windows 上它可能展开为 `__declspec(dllexport)`。\n\n`JNICALL`\n- **含义**：一个宏，通常在 `jni.h` 中定义，用来修饰 JNI 函数，表示该函数使用的调用约定 (calling convention)。\n- **目的**：保证函数使用正确的调用约定，使 JVM 能够正确地调用此函数。在 Windows 上，这通常展开为 `__stdcall`。\n\n`jstring`\n- **含义**：这是 JNI 提供的一个类型，用于表示 Java 中的 `String` 类型。\n- **目的**：用来表示从 Java 传递过来的字符串，或者返回一个 Java 字符串给 Java 层。\n\n`JNIEnv *env`\n- **含义**：JNI 环境指针，指向一个指针数组，包含了许多 JNI 函数指针。\n- **目的**：通过这个指针可以调用 JNI 提供的各种函数，例如将 C++ 字符串转换为 Java 字符串等。\n\n`jobject /* this */`\n- **含义**：代表 Java 对象的引用。在非静态方法中，这个参数是必需的，指向调用这个 JNI 函数的 Java 对象。\n- **目的**：在 C++ 代码中可以通过这个引用访问调用该方法的 Java 对象。对于静态方法，则为 `jclass`，表示类的引用。\n\n`Java_com_example_unityplugin_NativeLib_callJavaMethod`\n- **含义**：这是 JNI 函数的名称，按照 JNI 的命名规范，由 `Java_` 前缀、包名、类名和方法名组成。\n- **目的**：通过这种命名方式，JVM 能够根据 Java 代码中的 `NativeLib.callJavaMethod` 方法正确地找到对应的 JNI 实现。\n\n\n## Native代码调用Java代码\n\n当 Native 代码调用 Java 方法时，执行以下步骤：\n- 使用 `JNIEnv` 查找 Java 类和方法 ID。\n- 准备方法调用参数并调用相应的 JNI 函数。\n- JNI 函数会创建一个 Java 帧并推送到当前线程的调用栈。\n- JVM 解释或编译执行该 Java 方法。\n- 当 Java 方法返回时，JNI 函数会弹出 Java 帧，并返回结果给 Native 代码。\n\n### 基础\n1. 在 Java 类中定义一个带返回值的静态方法。\n```java\n// MyJavaClass.java\npackage com.example.unityplugin;\n\nimport android.util.Log;\n\npublic class MyJavaClass {\n    public static String myJavaMethod(String message) {\n        Log.d(\"MyJavaClass\", message);\n        return \"Response from Java: \" + message;\n    }\n}\n\n```\n\n2. 在 Native C++ 代码中调用 Java 方法并处理返回值。\n```cpp\n// native-lib.cpp\n#include <jni.h>\n#include <string>\n\nextern \"C\"\nJNIEXPORT jstring JNICALL\nJava_com_example_unityplugin_NativeLib_callJavaMethod(JNIEnv *env, jobject /* this */, jstring input) {\n    jclass javaClass = env->FindClass(\"com/example/unityplugin/MyJavaClass\");\n    jmethodID methodID = env->GetStaticMethodID(javaClass, \"myJavaMethod\", \"(Ljava/lang/String;)Ljava/lang/String;\");\n    jstring response = (jstring) env->CallStaticObjectMethod(javaClass, methodID, input);\n    return response;\n}\n\n```\n\n### 进阶-以JavaVM为中介\n\nJava调用Native层时,Native层将JavaVM指针存储下来.\n```cpp\nstatic JavaVM* gJvm = NULL;\nstatic jobject gNativeCrashHandler = NULL;\n\nvoid initializeNativeCrashHandler(JNIEnv* env, jobject obj) {\n    LOGI(\"NativeCrashWatcher->initializeNativeCrashHandler\");\n    env->GetJavaVM(&gJvm);\n    gNativeCrashHandler = env->NewGlobalRef(obj);\n\n    installSignalHandlers();\n}\n```\n\n\nNative层需要回调Java代码时,通过JavaVM\n\n```cpp\nvoid handleSignal(int signal, siginfo_t* info, void* context) {\n    LOGI(\"NativeCrashWatcher->handleSignal Caught signal %d\", signal);\n\n    JNIEnv* env;\n    int getEnvStat = gJvm->GetEnv((void**)&env, JNI_VERSION_1_6);\n    bool needDetach = false;\n\n    if (getEnvStat == JNI_EDETACHED) {\n        if (gJvm->AttachCurrentThread(&env, NULL) != 0) {\n            LOGE(\"Failed to attach current thread\");\n            return;\n        }        needDetach = true;\n    } else if (getEnvStat == JNI_OK) {\n        needDetach = true;\n//        LOGD(\"Thread already attached\");\n    } else {\n        LOGE(\"Failed to get the environment: %d\", getEnvStat);\n        return;\n    }\n    jclass handlerClass = env->GetObjectClass(gNativeCrashHandler);\n    if (handlerClass == NULL) {\n        LOGE(\"Failed to get handler class\");\n        gJvm->DetachCurrentThread();\n        return;\n    }\n    jmethodID methodID = env->GetStaticMethodID(handlerClass, \"reportException\", \"(Ljava/lang/String;)V\");\n    if (methodID == NULL) {\n        LOGE(\"Failed to get method ID for reportException\");\n        gJvm->DetachCurrentThread();\n        return;\n    }//    LOGD(\"NativeCrashWatcher->handleSignal Got method id\");\n\n    char message[256];\n    snprintf(message, sizeof(message), \"Caught signal %d\", signal);\n//    LOGD(\"NativeCrashWatcher->handleSignal generate msg: %s\", message);\n\n    jstring exceptionMessage = env->NewStringUTF(message);\n    if (exceptionMessage == NULL) {\n        LOGE(\"Failed to create new string\");\n        gJvm->DetachCurrentThread();\n        return;\n    }\n    env->CallStaticVoidMethod(handlerClass, methodID, exceptionMessage);\n//    LOGD(\"NativeCrashWatcher->handleSignal Called java reportException\");\n\n    if (env->ExceptionCheck()) {\n        env->ExceptionDescribe();\n        env->ExceptionClear();\n        //LOG输出Java层异常信息\n        LOGE(\"Exception occurred while calling Java method\");\n\n    }\n    env->DeleteLocalRef(exceptionMessage);\n//    LOGD(\"NativeCrashWatcher->handleSignal Deleted local ref\");\n    if (needDetach) {\n        LOGD(\"Detaching thread...\");\n        gJvm->DetachCurrentThread();\n        LOGD(\"NativeCrashWatcher->handleSignal Detached current thread\");\n    }}\n```\n\n\n## Android Studio工程配置\n\n在 Android Studio 中创建用于 Unity 插件的工程\n\n#### 1. 创建新的 Android Studio 工程\n\n- **选择 \"No Activity\" 模板**，点击 `Next`。\n- Language: 选择 `Java`。\n- Minimum API level: 选择适合您的项目的最低 API 级别，建议使用 `API 16: Android 4.1 (Jelly Bean)` 或更高版本。\n\n#### 2. 配置工程结构\n\n##### 创建 Java 代码结构\n- **创建 Java 包**：在 `src/main/java` 目录下，右键选择 `New > Package`，输入包名，例如 `com.prin.myplugin`。\n- **创建 Java 类**：在新创建的包中，右键选择 `New > Java Class`，输入类名 `TestJavaClass`，然后编写 Java 代码。\n\n```java\npackage com.prin.myplugin;\n\nimport android.util.Log;\n\npublic class TestJavaClass {\n    public static String myJavaMethod(String message) {\n        Log.d(\"MyJavaClass\", message);\n        return \"Response from Java: \" + message;\n    }\n\n    public static int Multiply(int a, int b) {\n        return a * b;\n    }\n}\n```\n\n\n##### 创建 Native 代码结构\n\n- **创建 C++ 目录**：在 `src/main` 目录下，右键选择 `New > Directory`，输入目录名 `cpp`。\n- **创建 C++ 文件**：在 `cpp` 目录下，右键选择 `New > C++ Source File`，输入文件名 `native-lib`。\n\n编写示例 C++ 代码：\n```cpp\n#include <jni.h>\n\nextern \"C\" JNIEXPORT jint JNICALL\nJava_com_prin_myplugin_TestJavaClass_multiply(JNIEnv *env, jclass clazz, jint a, jint b) {\n    return a * b;\n}\n\n```\n\n配置 CMake 构建脚本\n在 `cpp` 目录下，右键选择 `New > File`，输入文件名 `CMakeLists.txt`，并编写以下内容：\n```cmake\ncmake_minimum_required(VERSION 3.4.1)\n\nadd_library( native-lib SHARED native-lib.cpp )\n\nfind_library( log-lib log )\n\ntarget_link_libraries( native-lib ${log-lib} )\n\n```\n\n\n#### 3. 配置 build.gradle 文件\n\n在 `app/build.gradle` 文件中\n* 配置 CMake 和 JNI 支持。\n```cpp\nandroid {\n    ...\n    defaultConfig {\n        ...\n        externalNativeBuild {\n            cmake {\n                cppFlags \"-std=c++11\"\n            }\n        }\n    }\n    externalNativeBuild {\n        cmake {\n            path \"src/main/cpp/CMakeLists.txt\"\n        }\n    }\n}\n\n```\n\n添加依赖以生成 Jar 包：\n```groovy\ntask createJar {\n    doFirst {\n        logger.lifecycle(\"Starting to create Jar file...\")\n    }\n\n    doLast {\n        def classesDir = file(\"$buildDir/intermediates/javac/debug/classes\")\n        def jarDir = file(\"$buildDir/libs\")\n        def jarFilePath = \"$jarDir/ExperimentalJavaLib.jar\"\n\n        if (!classesDir.exists()) {\n            logger.lifecycle(\"Classes directory does not exist: $classesDir\")\n\n            return\n        }\n\n        if (!jarDir.exists()) {\n            jarDir.mkdirs()\n        }\n\n        ant.jar(destfile: jarFilePath, basedir: classesDir) {\n            exclude(name: \"**/R.class\")\n            exclude(name: '**/R$*.class')\n            exclude(name: \"**/BuildConfig.class\")\n            exclude(name: \"**/Manifest.class\")\n        }\n\n        def jarFile = file(jarFilePath)\n        if (jarFile.exists()) {\n            logger.lifecycle(\"Jar file created successfully: ${jarFile.absolutePath}\")\n        } else {\n            logger.lifecycle(\"Jar file creation failed.\")\n        }\n    }\n}\n\n```\n\n确保 `createJar` 任务在 `assembleDebug` 或其他具体的 assemble 任务完成之后执行。\n```cpp\nafterEvaluate {\n    tasks.getByName(\"assembleDebug\").finalizedBy(createJar)\n}\n```\n\n#### 4. 编译项目\n\n* 点击 `Build > Make Project`\n* 构建完成后，生成的 Jar 包位于 `app/build/libs/MyPlugin.jar`，SO 库位于 `app/build/intermediates/cmake/debug/obj/<架构>/libnative-lib.so`。\n\n#### 5. 将生成的文件集成到 Unity 中\n\n- 将 `MyPlugin.jar` 复制到 `Assets/Plugins/Android/` 目录。\n- 将 `libnative-lib.so` 复制到 `Assets/Plugins/Android/libs/<架构>/` 目录。\n- 在 Unity 中调用 Java 和 Native 方法\n\n\n## 案例：CS->Java->Native->Java\n\n- 在Unity中，通过C#代码调用Java方法`CallNativeMethod`。\n- Java方法`CallNativeMethod`调用Native方法`stringFromJNI`。\n- Native方法`stringFromJNI`调用Java方法`myJavaMethod`。\n- Java方法`myJavaMethod`返回结果给Native方法`stringFromJNI`。\n- Native方法`stringFromJNI`处理返回结果并返回给Java方法`CallNativeMethod`。\n- Java方法`CallNativeMethod`返回最终结果给C#代码。\n\n1. C#代码\n```cs\n\nusing System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\n\npublic class TestCallJavaCallNativeCallJava : MonoBehaviour\n{\n    public void CallJavaCallNativeCallJava()\n    {\n        Debug.Log(\"CallJavaCallNativeCallJava----------------------------------------\");\n\n        // 获取 UnityPlayer 类\n        AndroidJavaClass unityPlayer = new AndroidJavaClass(\"com.unity3d.player.UnityPlayer\");\n\n        // 获取当前活动\n        AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>(\"currentActivity\");\n\n        // 调用自定义 Java 类的方法\n        AndroidJavaClass myJavaClass = new AndroidJavaClass(\"com.experimental.javalib.TestJavaCallNativeClass\");\n\n        // 调用 Java 静态方法并获取返回值\n        string response = myJavaClass.CallStatic<string>(\"CallNativeMethod\", \"Hello from Unity!\");\n        Debug.Log(response);\n    }\n}\n\n```\n\n\n2. Java代码\n```java\npackage com.experimental.javalib;\n\npublic class TestJavaCallNativeClass {\n    static {\n        System.loadLibrary(\"experimental-native-lib\");\n    }\n\n    // 声明本地方法\n    public static native String stringFromJNI(String input);\n\n    // 调用本地方法\n    public static String CallNativeMethod(String input) {\n        String responseFromNative = stringFromJNI(input);\n        return \"Response from Java(CallNativeMethod): \" + responseFromNative;\n    }\n}\n\n```\n\n3. Native代码\n```cpp\n#include <jni.h>\n#include <string>\n\njstring callJavaMethod(JNIEnv *env, jclass clazz, jstring input) {\n    // 获取 Java 类\n    jclass javaClass = env->FindClass(\"com/experimental/javalib/TestJavaClass2\");\n\n    // 获取 Java 静态方法ID\n    jmethodID methodID = env->GetStaticMethodID(javaClass, \"myJavaMethod\", \"(Ljava/lang/String;)Ljava/lang/String;\");\n\n    // 调用 Java 静态方法并获取返回值\n    jstring response = (jstring) env->CallStaticObjectMethod(javaClass, methodID, input);\n\n    return response;\n}\n\nextern \"C\"\nJNIEXPORT jstring JNICALL\nJava_com_experimental_javalib_TestJavaCallNativeClass_stringFromJNI(JNIEnv *env, jclass clazz, jstring input) {\n    // 调用 Java 方法\n    jstring javaMethodRes = callJavaMethod(env, clazz, input);\n\n    // 将 Java 返回的字符串转换为 C++ 字符串并拼接\n    const char *tmpStr = env->GetStringUTFChars(javaMethodRes, 0);\n    std::string hello = \"Response from C++(stringFromJNI): \";\n    hello += tmpStr;\n    env->ReleaseStringUTFChars(javaMethodRes, tmpStr);\n\n    // 返回拼接后的字符串\n    return env->NewStringUTF(hello.c_str());\n}\n\n```\n\n4. Java代码\n```java\npackage com.experimental.javalib;\n\nimport android.util.Log;\n\npublic class TestJavaClass2 {\n    // 被Native代码调用的Java静态方法\n    public static String myJavaMethod(String message) {\n        Log.d(\"MyJavaClass\", message);\n        return \"Response from Java(myJavaMethod): \" + message;\n    }\n}\n\n```\n\n![](assets/image-20240607183651124.png)\n"
}
