JAR(Java Archive) 플러그인은 주로 Android OS와의 상호 작용을 활성화하거나 C# 스크립트에서 Java로 작성된 메서드를 호출하는 데 사용됩니다.
JAR 플러그인은 Java 코드만 포함할 수 있기 때문에(Android 리소스를 포함할 수 없기 때문에) 사용이 매우 제한적입니다. JAR 플러그인을 프로젝트에 추가하려면 .jar 파일을 프로젝트 폴더 중 하나에 복사합니다. 그리고 Unity의 인스펙터 창에서 임포트 설정을 열고 선택합니다. Android 체크박스를 선택하여 이 .jar 파일을 Unity와 호환 가능으로 표시합니다.
Java 플러그인 사용 Unity는 Java에서 코드를 호출할 때와 네이티브나 C# 스크립트에서 Java나 Java VM(Virtual Machine)과 상호 작용할 때 모두 Java Native Interface (JNI)를 사용합니다.
참고: 본 섹션에서 이 정보는 JNI(Android Java Native Interface)에 대한 고급 지식이 필요합니다.
C 또는 C++ 플러그인에서 Java 코드에 액세스하려면 Java VM에 액세스해야합니다. C/C++ 코드에 다음 메서드를 추가하여 Java VM에 액세스합니다.
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"); // find class definition
jmethodID mid_JavaClass = jni_env->GetMethodID (cls_JavaClass, "<init>", "()V"); // find constructor method
jobject obj_JavaClass = jni_env->NewObject(cls_JavaClass, mid_JavaClass); // create object instance
return jni_env->NewGlobalRef(obj_JavaClass); // return object with a global reference
}
JNI에 대한 자세한 내용은 JNI에 대한 Android 개발자 문서를 참조하십시오.
참고: 본 섹션에서 이 정보는 JNI(Android Java Native Interface)에 대한 고급 지식이 필요합니다.
AndroidJNIHelper
와 AndroidJNI
Unity API 클래스는 “원시” JNI 인터페이스를 감싸는 래퍼로 사용합니다.
AndroidJavaObject 및 AndroidJavaClass Unity API 클래스는 JNI 호출을 사용할 때 많은 작업을 자동화하며 캐싱을 사용하여 Java 호출을 더 빠르게 만듭니다. AndroidJavaObject
와 AndroidJavaClass
의 조합은 AndroidJNI
와 AndroidJNIHelper
를 기반으로 하지만, 몇 가지 추가적인 기능도 가지고 있습니다. 이러한 클래스에는 Java 클래스의 정적 멤버에 액세스하는 데 사용되는 정적 메서드도 있습니다.
C# 스크립트에서 Java JNI 호출을 작성하는 세 가지 방법이 있습니다.
AndroidJNI
메서드를 통한 원시 JNIAndroidJNI
를 수반한 AndroidJNIHelper
클래스AndroidJavaObject
와 AndroidJavaClass
클래스는 가장 편리한 고수준 API입니다.UnityEngine.AndroidJNI는 위에서 설명한대로 C에서 사용할 수 있는 JNI 호출에 대한 래퍼입니다. 클래스의 모든 메서드는 정적이며 Java Native Interface에 1:1로 매핑합니다.
UnityEngine.AndroidJNIHelper는 다음 단계에서 사용되는 헬퍼 함수를 제공합니다. 이 기능은 공용 메서드로 제공되며 특별한 경우 유용할 수 있습니다.
UnityEngine.AndroidJavaObject와 UnityEngine.AndroidJavaClass의 인스턴스는 Java 측의 java.lang.Object 및 java.lang.Class(또는 그 서브클래스)의 인스턴스에 1:1로 매핑합니다. 기본적으로 Java 측과 3가지 타입의 상호 작용을 제공합니다.
메서드 호출
필드 값 획득
필드 값 설정
호출은 ‘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
생성자는 최소한 하나의 파라미터, 즉 인스턴스를 생성할 클래스의 이름을 취합니다. 클래스 이름 뒤의 모든 파라미터는 오브젝트의 생성자 호출을 위한 파라미터이며, 이 경우에는 “some_string” 문자열입니다. 이후의 hashCode() 호출은 ’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;"); // 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);
이 예제는 새로운 오브젝트를 생성하기보다는com.unity3d.player.UnityPlayer
의 정적 멤버에 접근하기 위해 AndroidJavaObject
대신 AndroidJavaClass
로 시작합니다. 그런 다음 정적 필드인 “currentActivity”에 액세스하지만 이번에는 AndroidJavaObject
가 일반 파라미터로 사용됩니다. 실제 필드 타입 android.app.Activity는 java.lang.Object
의 서브 클래스이고, 비기본형은 AndroidJavaObject
로 액세스할 수 있어야 하기 때문입니다. 이 규칙에서 제외되는 것은 Java에서 기본형을 나타내지는 않지만 직접 액세스되는 문자열입니다.
액티비티 오브젝트에 대해 getCacheDir()
를 호출한 다음 캐시 디렉토리를 나타내는 파일 오브젝트를 가져올 수 있으며, getCanonicalPath()
를 호출하여 문자열 표현을 가져올 수 있습니다.
Unity는 Application.temporaryCachePath
및 Application.persistentDataPath
API를 사용하여 애플리케이션의 캐시 및 파일 디렉토리에 대한 액세스를 제공합니다.
이 예제는 UnitySendMessage
를 사용하여 Java에서 Unity로 데이터를 전달하는 방법을 보여줍니다.
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);
}
}
com.unity3d.player.UnityPlayer
Java 클래스는 UnitySendMessage
iOS 메서드와 동등한 정적인 메서드 UnitySendMessage
를 가지고 있습니다. 이것은 Unity에서 데이터를 전달하기 위해 Java에서 사용됩니다.
Unity 내부에서 UnitySendMessage
가 호출되었지만 Java를 사용하여 메시지를 중계하면 Java는 네이티브/Unity 코드를 다시 호출하여 “Main Camera”라는 객체에 메시지를 전달합니다. 이 객체에는 JavaMessage
라는 메서드가 포함된 스크립트가 첨부되어 있습니다.
AndroidJavaObject
와 AndroidJavaClass
는 계산상의 비용이 많이 드는 메서드입니다(원시 JNI를 사용하는 메서드도 마찬가지). 더 나은 성능과 코드 명확성을 위해 관리되는 코드와 네이티브/Java 코드 사이의 전환수를 최소한으로 유지하십시오.
//The first time you call a Java method like
AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", "some_string"); // somewhat expensive
int hash = jo.Call<int>("hashCode"); // first time - expensive
int hash = jo.Call<int>("hashCode"); // second time - not as expensive as we already know the java method and can call it directly
Mono 가비지 컬렉터는 사용 후 생성된 모든 AndroidJavaObject
와 AndroidJavaClass
인스턴스를 릴리스해야 하지만 가능한 빨리 삭제되도록 using(){}
상태에 보관하는 것이 좋습니다. 이것이 없으면 언제 폐기될지 확신할 수 없습니다. AndroidJNIHelper.debug
를 true로 설정하면 가비지 컬렉터의 활동에 대한 기록이 디버그 출력에 표시됩니다.
//Getting the system language safely
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"));
}
}
}