JAR (Java Archive) プラグインは主に、 Android OS との相互作用を可能にしたり、Java で書かれたメソッドを C# スクリプト内から呼び出すために使用されます。
JAR プラグインは Java コードしか含むことができないため (例えば Android リソースは格納できません)、その用途は非常に限定的です。JAR プラグインをプロジェクトに加えるには、任意のプロジェクトフォルダーに .jar ファイルをコピーし、Unity 上でそれを選択し、Inspector ウィンドウの Import Settings (インポート設定) を開いてください。この .jar ファイルを Android と互換性を持つファイルとしてマーキングするには、Android のチェックボックスにチェックを入れます。
Unity は、Java からコードを呼び出す場合にも、またネイティブコードや C# スクリプトから Java や Java VM (Virtual Machine) と相互作用する場合にも、Java Native Interface (JNI) を使用します。
Gradle ビルドシステムを使用する場合に、JAR ファイルの作成を避けることができます。これは以下のように行います。
Unity は Java ファイルを Gradle プロジェクトにコピーし、一緒にビルドします。
注意: このセクションの内容を理解するには、Android Java Native Interface (JNI) に関する高度な知識が必要です。
C や C++ のプラグインから Java コードにアクセスするためには、 Java VM にアクセスする必要があります。Java VM にアクセスするには、以下のメソッドを C/C++ コードに追加してください。
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* jni_env = 0;
vm->AttachCurrentThread(&jni_env, 0);
return JNI_VERSION_1_6;
}
JNI をすべて説明することはこのページの目的から外れているため、ここでは行いません。しかし、下の例で示すように、このメソッドは通常、クラスの定義を検索し、コンストラクター (<init>) のメソッドを解決し、新しいオブジェクトインスタンスを作成することに関わっています。
jobject createJavaObject(JNIEnv* jni_env) {
jclass cls_JavaClass = jni_env->FindClass("com/your/java/Class"); // クラスの定義を検索します
jmethodID mid_JavaClass = jni_env->GetMethodID (cls_JavaClass, "<init>", "()V"); // コンストラクターのメソッドを検索します
jobject obj_JavaClass = jni_env->NewObject(cls_JavaClass, mid_JavaClass); // オブジェクトのインスタンスを作成します
return jni_env->NewGlobalRef(obj_JavaClass); // オブジェクトをグローバル参照とともに返します
}
JNI に関する詳細は Android 開発者向けドキュメンテーションの JNI に関するヒント を参照してください。
注意: このセクションの内容を理解するには、Android Java Native Interface (JNI) に関する高度な知識が必要です。
Unity API クラス AndroidJNIHelper
と AndroidJNI
は、Raw の JNI インターフェースのラッパーとして使用されます。
Unity API クラスの AndroidJavaObject と AndroidJavaClass は、JNI コールの使用時に、多くのタスクを自動化します。また Java の呼び出しを速くするためにキャッシュを利用します。AndroidJavaObject
と AndroidJavaClass
の組み合わせは AndroidJNI
と AndroidJNIHelper
に加える形で作成されますが、さらにいくつかの追加機能も持っています。 またこれらのクラスは静的メソッドも持っており、これは Java クラスの静的メソッドへのアクセスに使用されます。
C# スクリプトから JNI の呼び出しを行う方法は 3 つあります。
AndroidJNI
メソッドで Raw JNI を呼び出しますAndroidJNI
と一緒に AndroidJNIHelper
クラスを呼び出しますAndroidJavaObject
クラスと AndroidJavaClass
クラスを呼び出しますUnityEngine.AndroidJNI は前述の通り、 C で利用可能な JNI コールのラッパーです。このクラスのメソッドは全て静的で、JNI に 1 対 1 でマッピングされます。
UnityEngine.AndroidJNIHelper は次のレベルで使用されるヘルパー機能を提供します。これはパブリックメソッドとしてアクセス可能で、特殊な状況で役立つ場合があります。
UnityEngine.AndroidJavaObject と UnityEngine.AndroidJavaClass のインスタンスは、 Java 側の java.lang.Object と java.lang.Class (あるいはこれらのサブクラス) のインスタンスにそれぞれ 1 対 1 でマッピングされます。これらは、基本的に Java 側と 3 種類の相互作用の方法を提供します。
メソッドを呼び出す
変数の値を取得する
フィールドの値を設定する
呼び出しは、2 種類に分けられます。void 型のメソッドの呼び出しと、非 void 型の戻り値を返すメソッドの呼び出しです。非 void 型を返すメソッドの戻り値は、ジェネリック型によって表されます。 Get と Set は常にフィールドの型を表すジェネリック型を使用します。
AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", "some_string");
// jni.FindClass("java.lang.String");
// jni.GetMethodID(classID, "<init>", "(Ljava/lang/String;)V");
// jni.NewStringUTF("some_string");
// jni.NewObject(classID, methodID, javaString);
int hash = jo.Call<int>("hashCode");
// jni.GetMethodID(classID, "hashCode", "()I");
// jni.CallIntMethod(objectID, methodID);
この例では、任意の 文字列 で初期化した java.lang.String のインスタンスを作成し、その文字列の ハッシュ値 を取得します。
AndroidJavaObject
コンストラクターは最低 1 つのパラメーター (インスタンスを作成するクラスの名前) を必要とします。そのクラス名に続くパラメーターは全て、オブジェクトのコンストラクターの呼び出し用です。ここでは “some_string” 文字列がそれに当たります。 続く hashCode() の呼び出しは ‘int’ を返します。この ‘int’ は、この例では、呼び出しメソッドのジェネリック型パラメーターとして使用されています。
注: 入れ子になった Java クラスはドット表記法を使ってインスタンス化することができません。内側に入ったクラスは $ セパレーターを使わなければなりません。 LayoutParams クラスが ViewGroup クラスの内側に入れ子になっている場合は、 android.view.ViewGroup$LayoutParams
か android/view/ViewGroup$LayoutParams
のように使います。
この例では、現在のアプリケーションのキャッシュディレクトリを、プラグインを使用せずに C# で取得する方法を示しています。
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
// jni.FindClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject jo = jc.GetStatic <AndroidJavaObject>("currentActivity");
// jni.GetStaticFieldID(classID, "Ljava/lang/Object;");
// jni.GetStaticObjectField(classID, fieldID);
// jni.FindClass("java.lang.Object");
Debug.Log(jo.Call <AndroidJavaObject>("getCacheDir").Call<string>("getCanonicalPath"));
// jni.GetMethodID(classID, "getCacheDir", "()Ljava/io/File;"); // または、他の基本クラスのみ!
// jni.CallObjectMethod(objectID, methodID);
// jni.FindClass("java.io.File");
// jni.GetMethodID(classID, "getCanonicalPath", "()Ljava/lang/String;");
// jni.CallObjectMethod(objectID, methodID);
// jni.GetStringUTFChars(javaString);
この例は、AndroidJavaObject
ではなく AndroidJavaClass
で始まっています。これは、新しいオブジェクトを作成するのではなく com.unity3d.player.UnityPlayer
の静的メンバーにアクセスするためです。その後、静的フィールド “currentActivity” にアクセスしますが、ジェネリックパラメーターとして AndroidJavaObject
が使用されています。これは何故かというと、 実際のフィールド型 android.app.Activity は java.lang.Object
のサブクラスであり、また全ての 非プリミティブ型 は AndroidJavaObject
としてアクセスされなければならないためです。この規則が当てはまらないのは string です。string はたとえ Java でプリミティブ型でなくても直接アクセスが可能です。
上記を行った上で、getCacheDir()
を Activity オブジェクトに呼び出すことにより、キャッシュディレクトリである File オブジェクトを取得できます。その後、 getCanonicalPath()
を呼び出して string を取得します。
Unity では、 Application.temporaryCachePath
および Application.persistentDataPath
API によって、アプリケーションのキャッシュとファイルディレクトリにアクセスできます。
この例では、UnitySendMessage
を使ってデータを Java から Unity に渡す方法を紹介します。
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour {
void Start () {
AndroidJNIHelper.debug = true;
using (AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
jc.CallStatic("UnitySendMessage", "Main Camera", "JavaMessage", "NewMessage");
}
}
void JavaMessage(string message) {
Debug.Log("message from java: " + message);
}
}
Java クラス com.unity3d.player.UnityPlayer
には、 iOS メソッド UnitySendMessage
に相当する静的メソッド UnitySendMessage
があります。これは、Java でデータを Unity に渡すために使用されます。
UnitySendMessage
は Unity 内から呼び出されますが、Java を使ってメッセージを伝えます。それから Java がネイティブあるいは Unity のコードにコールバックして “Main Camera” という名前のオブジェクトにメッセージを伝達します。このオブジェクトには JavaMessage
というメソッドを含むスクリプトが添付されています。
AndroidJavaObject
と AndroidJavaClass
は計算に高い負荷の掛かるメソッドです。これは Raw JNI を使用する他の多くのメソッドも同様です。より良いパフォーマンスとコードの明瞭化のために、マネージコードとネイティブ/Java コードの間の変遷の数を最小限に抑えるようにします。
//Java メソッドの初回呼び出し
AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", "some_string"); //少々負荷が高い
int hash = jo.Call<int>("hashCode"); // 初回 - 負荷が高い
int hash = jo.Call<int>("hashCode"); // 2 回目 - この Java メソッドを既に使用済みであり直接呼び出せるため、初回よりは負荷が低い
Mono ガベージコレクターは、作成された AndroidJavaObject
と AndroidJavaClass
のインスタンス全てを、使用後に開放します。ただし、確実に早く開放するようにするためには、using(){}
ステートメント内に入れておくようにします。そうでないと、いつ破棄されるかが把握できないからです。AndroidJNIHelper.debug
を True に設定すると、デバッグ出力でガベージコレクターの処理記録を確認できます。
//システムからのメッセージを安全に取得
void Start () {
using (AndroidJavaClass cls = new AndroidJavaClass("java.util.Locale")) {
using(AndroidJavaObject locale = cls.CallStatic<AndroidJavaObject>("getDefault")) {
Debug.Log("current lang = " + locale.Call<string>("getDisplayLanguage"));
}
}
}
2018–03–20 公開ページ
5.5 のアップデート機能
2018.2にAndroid Player用に追加されたJavaソースファイルプラグインのサポート NewIn20182