Version: 2017.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 のチェックボックスにチェックを入れます。

インスペクターウィンドウの、 JAR プラグインのインポート設定
インスペクターウィンドウの、 JAR プラグインのインポート設定

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

ネイティブ (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 のインスタンスを作成し、その文字列の hash value を回収します。

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;"); // or any baseclass thereof! 
 // 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")); 

        } 
    } 
}



  • 2017–05–18 編集レビュー 無しにパブリッシュされたページ

  • 5.5 のアップデート機能

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