Version: 2021.1
言語: 日本語
AAR プラグインと Android ライブラリ
UnityPlayerActivity Java コードの拡張

JAR プラグイン

JAR (Java Archive) プラグインは主に、 Android OS との相互作用を可能にしたり、Java で書かれたメソッドを C# スクリプト内から呼び出すために使用されます。

JAR プラグインは Java コードしか含むことができないため (例えば Android リソースは格納できません)、その用途は非常に限定的です。JAR プラグインをプロジェクトに加えるには、任意のプロジェクトフォルダーに .jar ファイルをコピーし、Unity 上でそれを選択し、Inspector ウィンドウの Import Settings (インポート設定) を開いてください。この .jar ファイルを Android と互換性を持つファイルとしてマーキングするには、Android のチェックボックスにチェックを入れます。

Inspector ウィンドウの、JAR プラグインのインポート設定
Inspector ウィンドウの、JAR プラグインのインポート設定

Java プラグインの使用

Unity は、Java からコードを呼び出す場合にも、またネイティブコードや C# スクリプトから Java や Java VM (Virtual Machine) と相互作用する場合にも、Java Native Interface (JNI) を使用します。

プラグインとしての Java ソースファイル

Gradle ビルドシステムを使用する場合に、JAR ファイルの作成を避けることができます。これは以下のように行います。

  1. .java ファイルを Unity プロジェクトにプラグインとして加えます。
  2. プラグインインスペクターで Android プラットフォーム用のプラグインを選択します。
  3. Android 用にビルドする場合は、Build SettingsBuild SystemGradle が設定されていることを確認します。

Unity は Java ファイルを Gradle プロジェクトにコピーし、一緒にビルドします。

ネイティブ (C/C++) コードから Java プラグインを使用

注意: このセクションの内容を理解するには、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 に関するヒント を参照してください。

ヘルパークラスを使って C# スクリプトから Java プラグインを使用

注意: このセクションの内容を理解するには、Android Java Native Interface (JNI) に関する高度な知識が必要です。

Unity API クラス AndroidJNIHelperAndroidJNI は、Raw の JNI インターフェースのラッパーとして使用されます。

Unity API クラスの AndroidJavaObject と AndroidJavaClass は、JNI コールの使用時に、多くのタスクを自動化します。また Java の呼び出しを速くするためにキャッシュを利用します。AndroidJavaObjectAndroidJavaClass の組み合わせは AndroidJNIAndroidJNIHelper に加える形で作成されますが、さらにいくつかの追加機能も持っています。 またこれらのクラスは静的メソッドも持っており、これは Java クラスの静的メソッドへのアクセスに使用されます。

C# スクリプトから JNI の呼び出しを行う方法は 3 つあります。

  • AndroidJNI メソッドで Raw JNI を呼び出します
  • AndroidJNI と一緒に AndroidJNIHelper クラスを呼び出します
  • 最も便利な高レベル API である AndroidJavaObject クラスと AndroidJavaClass クラスを呼び出します

UnityEngine.AndroidJNI は前述の通り、 C で利用可能な JNI コールのラッパーです。このクラスのメソッドは全て静的で、JNI に 1 対 1 でマッピングされます。

UnityEngine.AndroidJNIHelper は次のレベルで使用されるヘルパー機能を提供します。これはパブリックメソッドとしてアクセス可能で、特殊な状況で役立つ場合があります。

UnityEngine.AndroidJavaObjectUnityEngine.AndroidJavaClass のインスタンスは、 Java 側の java.lang.Object と java.lang.Class (あるいはこれらのサブクラス) のインスタンスにそれぞれ 1 対 1 でマッピングされます。これらは、基本的に Java 側と 3 種類の相互作用の方法を提供します。

  • メソッドを呼び出す

  • 変数の値を取得する

  • フィールドの値を設定する

呼び出しは、2 種類に分けられます。void 型のメソッドの呼び出しと、非 void 型の戻り値を返すメソッドの呼び出しです。非 void 型を返すメソッドの戻り値は、ジェネリック型によって表されます。 Get と Set は常にフィールドの型を表すジェネリック型を使用します。

参考例

例 1

 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$LayoutParamsandroid/view/ViewGroup$LayoutParams のように使います。

例 2

この例では、現在のアプリケーションのキャッシュディレクトリを、プラグインを使用せずに 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.Activityjava.lang.Object のサブクラスであり、また全ての 非プリミティブ型AndroidJavaObject としてアクセスされなければならないためです。この規則が当てはまらないのは string です。string はたとえ Java でプリミティブ型でなくても直接アクセスが可能です。

上記を行った上で、getCacheDir() を Activity オブジェクトに呼び出すことにより、キャッシュディレクトリである File オブジェクトを取得できます。その後、 getCanonicalPath() を呼び出して string を取得します。

Unity では、 Application.temporaryCachePath および Application.persistentDataPath API によって、アプリケーションのキャッシュとファイルディレクトリにアクセスできます。

例 3

この例では、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 というメソッドを含むスクリプトが添付されています。

Unity で Java プラグインを上手く使う方法

AndroidJavaObjectAndroidJavaClass は計算に高い負荷の掛かるメソッドです。これは 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 ガベージコレクターは、作成された AndroidJavaObjectAndroidJavaClass のインスタンス全てを、使用後に開放します。ただし、確実に早く開放するようにするためには、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

AAR プラグインと Android ライブラリ
UnityPlayerActivity Java コードの拡張