版本:Unity 6 (6000.0)
语言:English
Java 和 Kotlin 源代码插件
将 Unity 集成到 Android 应用程序中

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

要从 C# 脚本调用 Java 代码,Unity 提供了 C# API,这些 API 通过 C++ 与 Android 的 Java 本地接口 (JNI) 进行通信。Unity 提供了低级和高级 API,您可以使用它们通过 JNI 与 Java 代码进行交互。

低级 API

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

高级 API

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

AndroidJavaObjectAndroidJavaClass 的实例分别与 java.lang.Objectjava.lang.Class 的实例一一对应。它们提供三种与 Java/Kotlin 代码交互的方式

每次交互也都有一个静态版本

当您获取字段的值或调用返回值的方法时,您使用 泛型 指定返回类型。当您设置字段的值时,您也使用泛型来指定要设置的字段的类型。对于不返回值的方法,有一个常规的非泛型版本的 Call

重要:您必须将任何 非基本类型 作为 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 类中。

获取缓存目录

以下代码示例演示了如何在不使用插件在 Unity 之外创建的一组代码,用于在 Unity 中创建功能。在 Unity 中可以使用两种类型的插件:托管插件(使用 Visual Studio 等工具创建的托管 .NET 程序集)和原生插件(特定于平台的原生代码库)。 更多信息
请参阅 词汇表
的情况下以 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. 创建一个表示 com.unity3d.player.UnityPlayerAndroidJavaClass。最佳实践是使用 AndroidJavaClass 而不是 AndroidJavaObject 来访问静态成员。
  2. 创建一个表示当前 活动AndroidJavaObject,它是 com.unity3d.player.UnityPlayer 的静态成员。
  3. 在 Activity 对象上调用 getCacheDir(),它返回一个表示缓存目录的 File 对象。
  4. 在 File 对象上调用 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. 创建一个表示 com.unity3d.player.UnityPlayerAndroidJavaClass
  2. 调用静态 UnitySendMessage 方法,该方法是 com.unity3d.player.UnityPlayer 的成员。

虽然您从 Unity 内部调用 UnitySendMessage,但它使用 Java 传递消息,然后 Java 回调到原生/Unity 代码以将消息传递给对象,在本例中是名为“My GameObject”的游戏对象Unity 场景中的基本对象,可以表示角色、道具、场景、摄像机、路径点等。游戏对象的功能由附加在其上的组件定义。 更多信息
请参阅 词汇表
,该对象附加了一个包含名为 JavaMessage 的方法的脚本。

最佳实践

本节介绍在从 C# 脚本一段代码,允许您创建自己的组件、触发游戏事件、随时间推移修改组件属性并以任何您喜欢的方式响应用户输入。 更多信息
请参阅 词汇表
调用 Java 和 Kotlin 插件代码时需要注意的最佳实践。

最大程度减少 JNI 调用

通过高级或低级 C# API 使用 Java 本地接口 (JNI) 资源密集且速度可能较慢。为了提高性能并提高代码清晰度,最佳实践是尽量减少 JNI 调用的数量。

为了避免不必要的 JNI 调用,高级 C# API 会缓存您调用的每个 Java 方法的 ID。这意味着对同一方法的后续调用不会像第一次调用那样消耗大量资源。这些调用不需要在同一帧内甚至来自同一 AndroidJavaObject/AndroidJavaClass 实例。如果您使用低级 API 并希望获得此性能优势,则必须自己缓存方法 ID。否则,最佳实践是使用高级 API。

注意:Unity 会保留缓存,直到应用程序 关闭。这包括应用程序在后台运行时。

垃圾回收

您应该将任何 AndroidJavaObjectAndroidJavaClass 实例包装在 using 语句中,以确保 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 源代码插件
将 Unity 集成到 Android 应用程序中