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

高级 AndroidJavaObjectAndroidJavaClassAndroidJavaProxy API 可自动执行 JNI 调用所需的大量任务。它们还使用缓存来更快地调用 Java。AndroidJavaObjectAndroidJavaClass 的组合构建在 AndroidJNIAndroidJNIHelper 之上,但它们还包含其他功能,例如可用于访问 Java 类的静态成员的静态方法。

AndroidJavaObjectAndroidJavaClass 的实例分别与 java.lang.Objectjava.lang.Class 的实例进行一对一映射。它们提供了三种类型的 Java/Kotlin 代码交互:

每个交互还有一个静态版本:

获取字段的值或调用返回值的方法时,可使用泛型指定返回类型。设置字段的值时,还可使用泛型来指定要设置的字段的类型。对于不返回值的方法,存在常规的非泛型调用版本。

重要信息:必须以 AndroidJavaObject 身份访问任何非原始类型。唯一的例外是字符串,即使字符串不代表 Java 中的原始类型,也可以直接访问它们。

示例

本节包含的代码示例展示了如何使用高级 AndroidJavaObjectAndroidJavaClass 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.StringAndroidJavaObjectAndroidJavaObject 构造函数至少使用一个参数:要构造实例的类的名称。类名后的所有参数都用于对象的构造函数调用,在本例中为来自 GetJavaStringHashCodetext 参数。
  2. 调用 hashCode() 以获取字符串的哈希代码。此调用对 Call 使用 int 泛型类型参数,因为 hashCode() 将哈希代码返回为整数。

注意:不能使用虚线表示法来实例化嵌套的 Java 类。必须使用 $ 分隔符来实例化内部类。例如,使用 android.view.ViewGroup$LayoutParamsandroid/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.temporaryCachePathApplication.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 语句将所有 AndroidJavaObjectAndroidJavaClass 实例封装起来,以确保 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