从 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。 和 的组合构建在 和 之上,但它们还包含其他功能,例如可用于访问 Java 类的静态成员的静态方法。
AndroidJavaObjectAndroidJavaClassAndroidJNIAndroidJNIHelperAndroidJavaObjectAndroidJavaClass每个交互还有一个静态版本:
- 调用某一静态方法的 CallStatic。
- 获取某一静态字段的值的 GetStatic。
- 设置某一静态字段的值的SetStatic。
重要信息:必须以 身份访问任何非原始类型。唯一的例外是字符串,即使字符串不代表 Java 中的原始类型,也可以直接访问它们。
AndroidJavaObject示例
本节包含的代码示例展示了如何使用高级 和 API。
AndroidJavaObjectAndroidJavaClass获取 Java 字符串的哈希代码
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; } }}
此示例:
- 创建一个表示 java.lang.String 的 。
AndroidJavaObject构造函数至少使用一个参数:要构造实例的类的名称。类名后的所有参数都用于对象的构造函数调用,在本例中为来自AndroidJavaObject的GetJavaStringHashCode参数。text - 调用 hashCode() 以获取字符串的哈希代码。此调用对 使用
Call泛型类型参数,因为int将哈希代码返回为整数。hashCode()
注意:不能使用虚线表示法来实例化嵌套的 Java 类。必须使用 分隔符来实例化内部类。例如,使用 或 ,其中 类嵌套在 类中。
$android.view.ViewGroup$LayoutParamsandroid/view/ViewGroup$LayoutParamsLayoutParamsViewGroup获取缓存目录
以下代码示例显示了如何在不使用插件的情况下用 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; } }}
此示例:
- 创建了一个 来表示
AndroidJavaClass。最佳做法是使用com.unity3d.player.UnityPlayer而不是AndroidJavaClass来访问静态成员。AndroidJavaObject - 创建了一个 来表示当前活动,这是
AndroidJavaObject的静态成员。com.unity3d.player.UnityPlayer - 对活动对象调用了 getCacheDir(),返回了一个表示缓存目录的文件对象。
- 对文件对象调用了 getCanonicalPath(),可以获取字符串形式的缓存目录。
注意:此示例仅供参考。要访问应用程序的缓存和文件目录,请使用 Application.temporaryCachePath 和 Application.persistentDataPath API。
将数据从 Java 传递到 Unity
以下代码示例显示了如何使用 将数据从 Java 传递到 Unity。
UnitySendMessageusing 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); }}
此示例:
- 创建了一个 来表示
AndroidJavaClass。com.unity3d.player.UnityPlayer - 调用作为 成员的静态
com.unity3d.player.UnityPlayer方法。UnitySendMessage
尽管是在 Unity 内部调用 ,但该方法使用 Java 来中继消息,然后 Java 回调 native/Unity 代码,将消息传递给对象(在本示例中是名为 My GameObject 的游戏对象)。此对象附带一个脚本,脚本中包含一个名为 的方法。
UnitySendMessageJavaMessage最佳做法
本节介绍从 C# 脚本调用 Java 和 Kotlin 插件代码时需要注意的最佳做法。
最小化 JNI 调用
使用 Java 原生接口 (JNI),通过高级或低级 C# API 会占用大量资源,并且速度可能很慢。为了提高性能和代码清晰度,最佳做法是保持较低的 JNI 调用数量。
为了避免不必要的 JNI 调用,高级 C# API 会缓存调用的每个 Java 方法的 ID。这意味着对同一方法的后续调用不像第一次调用那样耗费资源。调用不需要发生在同一帧期间,甚至不需要来自同一 AndroidJavaObject/AndroidJavaClass 实例。如果使用低级 API 并希望获得此性能优势,则必须自己缓存方法 ID。否则,最好使用高级 API。
注意:Unity 会保留缓存数据,直到应用程序关闭。应用程序在后台时也是如此。
垃圾收集
应该使用 语句将所有 或 实例封装起来,以确保 Unity 尽快销毁它们。如果不使用 ,Unity 的垃圾回收器应该仍会释放所有创建的实例,但无法控制何时释放。
usingAndroidJavaObjectAndroidJavaClassusing以下代码示例展示了如何使用 语句以最佳方式获取系统语言:
usingusing 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")); } } }}