Version: 2023.1
言語: 日本語
Java と Kotlin のソースプラグイン
Unity を Android アプリケーションに統合

C# スクリプトから Java や Kotlin のプラグインコードを呼び出す

C# スクリプトから Java コードを呼び出すために、Unity は C++ を通じて Android Java Native Interface (JNI) と通信する C# API を提供します。Unity は、JNI を使用して Java コードと相互作用するために使用できる低レベルと高レベルの両方の API を提供します。

低レベル API

低レベルの AndroidJNI クラスは、JNI 呼び出しをラップして、JNI メソッドに直接マップする静的メソッドを提供します。AndroidJNIHelper API は、主に高レベルの API で使用されるヘルパー機能を提供しますが、特定の状況で役に立つことがあります。

高レベル API

高レベルの AndroidJavaObjectAndroidJavaClassAndroidJavaProxy の API は、JNI 呼び出しに必要な多くのタスクを自動化します。また、キャッシュを利用して Java への呼び出しを高速化します。 AndroidJavaObjectAndroidJavaClass の組み合わせは、 AndroidJNIAndroidJNIHelper の上に構築されていますが、Java クラスの静的メンバーにアクセスするために使える静的メソッドなどの追加機能が含まれています。

AndroidJavaObjectAndroidJavaClass のインスタンスは、それぞれjava.lang.Objectjava.lang.Class のインスタンスと 1 対 1 の対応関係を持っています。これらは、Java/Kotlin のコードとの 3 種類のインタラクションが可能です。

  • メソッドを呼び出し (Call) ます。
  • フィールドの値を取得 (Get) します。
  • フィールドの値を設定 (Set) します。

各インタラクションには静的バージョンもあります。

  • 静的メソッドを呼び出し (CallStatic) ます。
  • 静的フィールドの値を取得 (GetStatic) します。
  • 静的フィールドの値を設定 (SetStatic) します。

フィールドの値を取得するとき、または値を返すメソッドを呼び出すときは、ジェネリックス を使用して戻り値の型を指定します。フィールドの値を設定する場合も、ジェネリックスを使用して設定するフィールドの型を指定します。値を返さないメソッドについては、通常の非ジェネリックバージョンである 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. AndroidJavaObject を作成します。これは、java.lang.String を表します。AndroidJavaObject コンストラクターは、少なくとも 1 つのパラメーター (これは、インスタンスを構築するクラスの名前) を取ります。クラス名の後のパラメーターは、オブジェクトのコンストラクター呼び出しのためのものです。この場合、GetJavaStringHashCodetext パラメーターです。
  2. hashCode() を呼び出し、文字列のハッシュコードを取得します。この呼び出しでは、Callint ジェネリック型パラメーターを使用します。hashCode() はハッシュコードを整数で返すからです。

ノート: ネスト状の Java クラスのインスタンス化にはドット付き表記を使用できません。内部クラスをインスタンス化するには、$ セパレーターを使用する必要があります。例えば、LayoutParams クラスが ViewGroup クラスにネストされている場合は、android.view.ViewGroup$LayoutParamsandroid/view/ViewGroup$LayoutParams を使用します。

キャッシュディレクトリを取得

以下のコードサンプルは、プラグインを使用せずに、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 を表現します。静的メンバーにアクセスするには、AndroidJavaObject の代わりに AndroidJavaClass を使用するのがもっとも効率的です。
  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. AndroidJavaClass を作成し、com.unity3d.player.UnityPlayer を表します。
  2. com.unity3d.player.UnityPlayer のメンバーである静的な UnitySendMessage メソッドを呼び出します。

UnitySendMessage は Unity 内から呼び出されますが、Java を使ってメッセージを伝えます。それから Java がネイティブあるいは Unity のコードにコールバックしてオブジェクト (この例では、“My GameObject” という名前のゲームオブジェクト) にメッセージを伝えます。このオブジェクトには JavaMessage というメソッドを含むスクリプトが添付されています。

ベストプラクティス

ここでは、C# スクリプトから Java や Kotlin のプラグインコードを呼び出す際に知っておくとよい効果的な方法を説明します。

JNI 呼び出しの最小化

Java Native Interface (JNI) を、高レベルまたは低レベルの C# API を介して使用すると、リソースを消費し、速度が低下する可能性があります。パフォーマンスを向上させ、またコードを明確にするために、JNI 呼び出しの数を少なくすることが効率的です。

不必要な JNI 呼び出しを避けるため、高レベルの C# API は、呼び出した各 Java メソッドの ID をキャッシュします。これは、同じメソッドへの後続の呼び出しが、最初の呼び出しほどリソースを消費しないことを意味します。呼び出しは、同じフレーム内である必要はなく、同じ AndroidJavaObject/AndroidJavaClass インスタンスからであってもかまいません。低レベル API を使用していて、このパフォーマンス上の利点を求める場合は、自分でメソッド ID をキャッシュする必要があります。そうでない場合は、高レベルの API を使用するのがより効率的です。

ノート: Unity は、アプリケーションが 閉じる まで、キャッシュを維持します。これには、アプリケーションがバックグラウンドで動作している間も含まれます。

ガベージコレクション

AndroidJavaObject または AndroidJavaClass のインスタンスを 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.debugtrue に設定してください。

Java と Kotlin のソースプラグイン
Unity を Android アプリケーションに統合