文档

Unity Engine

Unity Engine

从 C# 脚本调用 Java 和 Kotlin 插件代码

Interact with Java and Kotlin plug-in code from your C# scripts.
阅读时间8 分钟最后更新于 1 个月前

为了从 C# 脚本调用 Java 代码,Unity 提供了通过 C++ 与 Android Java 原生接口 (JNI) 通信的 C# API。Unity 同时提供低级和高级 API,借助它们就可以使用 JNI 与 Java 代码交互了。

低级 API

低级 AndroidJNI 类封装 JNI 调用并提供直接映射到 JNI 方法的静态方法。AndroidJNIHelper API 提供的 helper 功能主要由高级 API 使用,但在某些情况下很有用。

高级 API

高级 AndroidJavaObject、AndroidJavaClass 和 AndroidJavaProxy API 可自动执行 JNI 调用所需的大量任务。它们还使用缓存来更快地调用 Java。
AndroidJavaObject
和
AndroidJavaClass
的组合构建在
AndroidJNI
和
AndroidJNIHelper
之上,但它们还包含其他功能,例如可用于访问 Java 类的静态成员的静态方法。
AndroidJavaObject
和
AndroidJavaClass
的实例分别与 java.lang.Object 和 java.lang.Class 的实例进行一对一映射。它们提供了三种类型的 Java/Kotlin 代码交互:
每个交互还有一个静态版本:
获取字段的值或调用返回值的方法时,可使用泛型指定返回类型。设置字段的值时,还可使用泛型来指定要设置的字段的类型。对于不返回值的方法,存在常规的非泛型调用版本。
重要信息:必须以
AndroidJavaObject
身份访问任何非原始类型。唯一的例外是字符串,即使字符串不代表 Java 中的原始类型,也可以直接访问它们。

示例

本节包含的代码示例展示了如何使用高级
AndroidJavaObject
和
AndroidJavaClass
API。

获取 Java 字符串的哈希代码

以下代码示例创建了一个通过字符串初始化的 java.lang.String 实例,并检索了该字符串的哈希值)。
using UnityEngine;public class JavaExamples{ public static int GetJavaStringHashCode(string text) { using (AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", text)) { int hash = jo.Call<int>("hashCode"); return hash; } }}
此示例:
  1. 创建一个表示 java.lang.String 的
    AndroidJavaObject
    。
    AndroidJavaObject
    构造函数至少使用一个参数:要构造实例的类的名称。类名后的所有参数都用于对象的构造函数调用,在本例中为来自
    GetJavaStringHashCode
    的
    text
    参数。
  2. 调用 hashCode() 以获取字符串的哈希代码。此调用对
    Call
    使用
    int
    泛型类型参数,因为
    hashCode()
    将哈希代码返回为整数。
注意:不能使用虚线表示法来实例化嵌套的 Java 类。必须使用
$
分隔符来实例化内部类。例如,使用
android.view.ViewGroup$LayoutParams
或
android/view/ViewGroup$LayoutParams
,其中
LayoutParams
类嵌套在
ViewGroup
类中。

获取缓存目录

以下代码示例显示了如何在不使用插件的情况下用 C# 获取当前应用程序的缓存目录。
using UnityEngine;public class JavaExamples{ public static string GetApplicationCacheDirectory() { using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) using (AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) using (AndroidJavaObject javaFile = currentActivity.Call<AndroidJavaObject>("getCacheDir")) { string cacheDirectory = javaFile.Call<string>("getCanonicalPath"); return cacheDirectory; } }}
此示例:
  1. 创建了一个
    AndroidJavaClass
    来表示
    com.unity3d.player.UnityPlayer
    。最佳做法是使用
    AndroidJavaClass
    而不是
    AndroidJavaObject
    来访问静态成员。
  2. 创建了一个
    AndroidJavaObject
    来表示当前活动,这是
    com.unity3d.player.UnityPlayer
    的静态成员。
  3. 对活动对象调用了 getCacheDir(),返回了一个表示缓存目录的文件对象。
  4. 对文件对象调用了 getCanonicalPath(),可以获取字符串形式的缓存目录。
注意:此示例仅供参考。要访问应用程序的缓存和文件目录,请使用 Application.temporaryCachePath 和 Application.persistentDataPath API。

将数据从 Java 传递到 Unity

以下代码示例显示了如何使用
UnitySendMessage
将数据从 Java 传递到 Unity。
using UnityEngine;public class JavaExamples : MonoBehaviour{ private void Start() { AndroidJNIHelper.debug = true; using (AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) { jc.CallStatic("UnitySendMessage", "My GameObject", "JavaMessage", "NewMessage"); } } private void JavaMessage(string message) { Debug.Log("message from java: " + message); }}
此示例:
  1. 创建了一个
    AndroidJavaClass
    来表示
    com.unity3d.player.UnityPlayer
    。
  2. 调用作为
    com.unity3d.player.UnityPlayer
    成员的静态
    UnitySendMessage
    方法。
尽管是在 Unity 内部调用
UnitySendMessage
,但该方法使用 Java 来中继消息,然后 Java 回调 native/Unity 代码,将消息传递给对象(在本示例中是名为 My GameObject 的游戏对象)。此对象附带一个脚本,脚本中包含一个名为
JavaMessage
的方法。

最佳做法

本节介绍从 C# 脚本调用 Java 和 Kotlin 插件代码时需要注意的最佳做法。

最小化 JNI 调用

使用 Java 原生接口 (JNI),通过高级或低级 C# API 会占用大量资源,并且速度可能很慢。为了提高性能和代码清晰度,最佳做法是保持较低的 JNI 调用数量。
为了避免不必要的 JNI 调用,高级 C# API 会缓存调用的每个 Java 方法的 ID。这意味着对同一方法的后续调用不像第一次调用那样耗费资源。调用不需要发生在同一帧期间,甚至不需要来自同一 AndroidJavaObject/AndroidJavaClass 实例。如果使用低级 API 并希望获得此性能优势,则必须自己缓存方法 ID。否则,最好使用高级 API。
注意:Unity 会保留缓存数据,直到应用程序关闭。应用程序在后台时也是如此。

垃圾收集

应该使用
using
语句将所有
AndroidJavaObject
或
AndroidJavaClass
实例封装起来,以确保 Unity 尽快销毁它们。如果不使用
using
,Unity 的垃圾回收器应该仍会释放所有创建的实例,但无法控制何时释放。
以下代码示例展示了如何使用
using
语句以最佳方式获取系统语言:
using UnityEngine;public class LocaleExample : MonoBehaviour{ void Start() { using (AndroidJavaClass cls = new AndroidJavaClass("java.util.Locale")) using (AndroidJavaObject locale = cls.CallStatic<AndroidJavaObject>("getDefault")) { if (locale != null) { Debug.Log("current lang = " + locale.Call<string>("getDisplayLanguage")); } } }}
注意:要在 Android Logcat 中看到垃圾回收器的活动记录,请将 AndroidJNIHelper.debug 设为
true
。