Version: 5.6
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);                      // オブジェクトをグローバル参照とともに返すreturn object with a global reference
}

JNI に関する詳細は Android 開発者向けドキュメンテーションの JNI に関する項 をご参照ください。

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

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

Unity API クラス AndroidJNIHelper および AndroidJNI は、「raw (生)」 の JNI インターフェースのラッパーとして使用されます。

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

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

  • AndroidJNI メソッドで raw JNI を呼び出す
  • AndroidJNIHelper クラスと AndroidJNI の併用
  • 最も便利な高レベル API である AndroidJavaObject クラスおよび AndroidJavaClass クラス

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

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

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

  • メソッドを呼び出す

  • フィールドの値を取得する

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

呼び出しは、 void メソッドの呼び出しと、非 void 型の戻り値を返すメソッドの呼び出しの 2 種類に分けられます。非 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$LayoutParams またはandroid/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 としてアクセスされなければならないためです。この規則が当てはまらないのは文字列です。文字列はたとえ Java でプリミティブ型でなくても直接アクセスが可能です。

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

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

例 3

この例では、UnitySendMessage を使ってデータを Java から Unity に渡す方法をご確認いただけます。

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")); 

        } 
    } 
}



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