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

JAR プラグイン

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

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

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

Using Java plug-ins

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

Java source files as plug-ins

When using the Gradle build system, you can avoid creating JAR files. To do this:

  1. Drop the .java file into your Unity project as a plug-in.
  2. In the Plugin Inspector, mark the plug-in for the Android platform.
  3. When building for Android, make sure Gradle is set as your Build System in the Build Settings.

Unity copies the java file to the Gradle project and builds with it.

ネイティブ (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 のインスタンスを作成し、その文字列の ハッシュ値 を取得します。

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 としてアクセスされなければならないためです。この規則が当てはまらないのは 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 のアップデート機能

  • Support for Java source file plug-ins added for Android player in 2018.2 NewIn20182

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