Version: 2019.2
AAR 플러그인과 Android 라이브러리
UnityPlayerActivity Java 코드의 확장

JAR 플러그인

JAR(Java Archive) 플러그인은 주로 Android OS와의 상호 작용을 활성화하거나 C# 스크립트에서 Java로 작성된 메서드를 호출하는 데 사용됩니다.

JAR 플러그인은 Java 코드만 포함할 수 있기 때문에(Android 리소스를 포함할 수 없기 때문에) 사용이 매우 제한적입니다. JAR 플러그인을 프로젝트에 추가하려면 .jar 파일을 프로젝트 폴더 중 하나에 복사합니다. 그리고 Unity의 인스펙터 창에서 임포트 설정을 열고 선택합니다. Android 체크박스를 선택하여 이 .jar 파일을 Unity와 호환 가능으로 표시합니다.

인스펙터 창에 표시되는 JAR 플러그인 임포트 설정
인스펙터 창에 표시되는 JAR 플러그인 임포트 설정

Java 플러그인 사용

Unity는 Java에서 코드를 호출할 때와 네이티브나 C# 스크립트에서 Java 나 Java VM(Virtual Machine)과 상호 작용할 때 모두 Java Native Interface (JNI)를 사용합니다.

Java 소스 파일을 플러그인으로 사용

Gradle 빌드 시스템을 사용하는 경우 다음 단계를 따르면 JAR 파일을 만들지 않을 수 있습니다.

  1. .java 파일을 Unity 프로젝트에 플러그인으로 넣습니다.
  2. 플러그인 인스펙터에서 Android 플랫폼용 플러그인을 표시합니다.
  3. Android용으로 빌드하는 경우 빌드 설정에서 GradleBuild System 으로 설정했는지 확인합니다.

Unity가 java 파일을 Gradle 프로젝트에 복사한 후 빌드하는 데 사용합니다.

네이티브 (C/C++)코드에서 Java 플러그인 사용

참고: 본 섹션에서 이 정보는 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 개발자 문서를 참조하십시오.

C# 스크립트의 Java 플러그인을 헬퍼 클래스와 함께 사용

참고: 본 섹션에서 이 정보는 JNI(Android Java Native Interface)에 대한 고급 지식이 필요합니다.

AndroidJNIHelperAndroidJNI Unity API 클래스는 “원시” JNI 인터페이스를 감싸는 래퍼로 사용합니다.

AndroidJavaObject 및 AndroidJavaClass Unity API 클래스는 JNI 호출을 사용할 때 많은 작업을 자동화하며 캐싱을 사용하여 Java 호출을 더 빠르게 만듭니다. AndroidJavaObjectAndroidJavaClass의 조합은 AndroidJNIAndroidJNIHelper를 기반으로 하지만, 몇 가지 추가적인 기능도 가지고 있습니다. 이러한 클래스에는 Java 클래스의 정적 멤버에 액세스하는 데 사용되는 정적 메서드도 있습니다.

C# 스크립트에서 Java JNI 호출을 작성하는 세 가지 방법이 있습니다.

  • AndroidJNI 메서드를 통한 원시 JNI
  • AndroidJNI를 수반한 AndroidJNIHelper 클래스
  • AndroidJavaObjectAndroidJavaClass 클래스는 가장 편리한 고수준 API입니다.

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가 아닌 반환 타입이 있는 메서드에 대한 호출의 두 가지 범주로 구분됩니다. 일반 타입은 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 생성자는 최소한 하나의 파라미터, 즉 인스턴스를 생성할 클래스의 이름을 취합니다. 클래스 이름 뒤의 모든 파라미터는 오브젝트의 생성자 호출을 위한 파라미터이며, 이 경우에는 “some_string” 문자열입니다. 이후의 hashCode() 호출은 ’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);

이 예제는 새로운 오브젝트를 생성하기보다는com.unity3d.player.UnityPlayer의 정적 멤버에 접근하기 위해 AndroidJavaObject 대신 AndroidJavaClass로 시작합니다. 그런 다음 정적 필드인 “currentActivity”에 액세스하지만 이번에는 AndroidJavaObject가 일반 파라미터로 사용됩니다. 실제 필드 타입 android.app.Activityjava.lang.Object의 서브 클래스이고, 비기본형AndroidJavaObject로 액세스할 수 있어야 하기 때문입니다. 이 규칙에서 제외되는 것은 Java에서 기본형을 나타내지는 않지만 직접 액세스되는 문자열입니다.

액티비티 오브젝트에 대해 getCacheDir()를 호출한 다음 캐시 디렉토리를 나타내는 파일 오브젝트를 가져올 수 있으며, getCanonicalPath()를 호출하여 문자열 표현을 가져올 수 있습니다.

Unity는 Application.temporaryCachePathApplication.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); 
    }
}

com.unity3d.player.UnityPlayer Java 클래스는 UnitySendMessage iOS 메서드와 동등한 정적인 메서드 UnitySendMessage를 가지고 있습니다. 이것은 Unity에서 데이터를 전달하기 위해 Java에서 사용됩니다.

Unity 내부에서 UnitySendMessage가 호출되었지만 Java를 사용하여 메시지를 중계하면 Java는 네이티브/Unity 코드를 다시 호출하여 “Main Camera”라는 객체에 메시지를 전달합니다. 이 객체에는 JavaMessage라는 메서드가 포함된 스크립트가 첨부되어 있습니다.

Unity에서 Java 플러그인을 사용하는 최상의 방법

AndroidJavaObjectAndroidJavaClass는 계산상의 비용이 많이 드는 메서드입니다(원시 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 가비지 컬렉터는 사용 후 생성된 모든 AndroidJavaObjectAndroidJavaClass 인스턴스를 릴리스해야 하지만 가능한 빨리 삭제되도록 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")); 

        } 
    } 
}



  • 2018–03–20

  • Unity 5.5에서 업데이트된 기능

  • 2018.2에서 Java 소스 파일 플러그인에 대한 지원이 Android 플레이어에 추가됨 NewIn20182

AAR 플러그인과 Android 라이브러리
UnityPlayerActivity Java 코드의 확장