Version: Unity 6.0 (6000.0)
语言 : 中文
Java 和 Kotlin 源代码插件
Java Native Interface APIs in Unity

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

为了从 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__Unity 场景中的基础对象,可以表示角色、道具、风景、摄像机、路径点等。GameObject 的功能由所附的组件决定。更多信息
See in Glossary
的游戏对象)。此对象附带一个脚本,脚本中包含一个名为 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。

Java 和 Kotlin 源代码插件
Java Native Interface APIs in Unity