# C# 스크립트에서 Java 및 Kotlin 플러그인 코드 호출

> Interact with Java and Kotlin plug-in code from your C# scripts.

C# 스크립트에서 Java 코드를 호출하기 위해 Unity는 C++를 통해 Android JNI([Java 네이티브 인터페이스](https://developer.android.com/training/articles/perf-jni))와 통신하는 C# API를 제공합니다. Unity는 JNI를 사용하여 Java 코드와 상호 작용하는 데 사용할 수 있는 저수준 API와 고수준 API를 모두 제공합니다.

## 저수준 API

저수준 [AndroidJNI](/engine/6000.0/script-reference/unity-engine/android-jni.md) 클래스는 JNI 호출을 래핑하고 JNI 메서드에 직접 매핑되는 정적 메서드를 제공합니다. [AndroidJNIHelper](/engine/6000.0/script-reference/unity-engine/android-jni-helper.md) API는 주로 고수준 API에서 사용되는 헬퍼 기능을 제공하지만, 이는 특정 상황에서도 유용할 수 있습니다.

## 고수준 API

고수준 [AndroidJavaObject](/engine/6000.0/script-reference/unity-engine/android-java-object.md), [AndroidJavaClass](/engine/6000.0/script-reference/unity-engine/android-java-class.md) 및 [AndroidJavaProxy](/engine/6000.0/script-reference/unity-engine/android-java-proxy.md) API는 JNI 호출에 필요한 많은 작업을 자동화합니다. 또한 캐싱을 사용하여 Java 호출 속도를 단축합니다. `AndroidJavaObject` 및 `AndroidJavaClass`의 조합은 `AndroidJNI` 및 `AndroidJNIHelper` 위에 구축되지만, Java 클래스의 정적 멤버에 액세스하는 데 사용할 수 있는 정적 메서드와 같은 추가 기능도 포함되어 있습니다.

`AndroidJavaObject` 및 `AndroidJavaClass`의 인스턴스는 각각 [java.lang.Object](https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html) 및 [java.lang.Class](https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html)의 인스턴스에 일대일 매핑을 수행합니다. 이들은 다음과 같은 세 가지 유형의 Java/Kotlin 코드 상호 작용을 제공합니다.

* 메서드 [호출](/engine/6000.0/script-reference/unity-engine/android-java-object/call.md)
* 필드 값 [획득](/engine/6000.0/script-reference/unity-engine/android-java-object/get.md)
* 필드 값 [설정](/engine/6000.0/script-reference/unity-engine/android-java-object/set.md)

각 상호 작용에는 정적 버전도 있습니다.

* 정적 메서드를 호출하려면 [CallStatic](/engine/6000.0/script-reference/unity-engine/android-java-object/call-static.md)을 호출합니다.
* 정적 필드의 값을 얻으려면 [GetStatic](/engine/6000.0/script-reference/unity-engine/android-java-object/get-static.md)을 사용합니다.
* 정적 필드의 값을 설정하려면 [SetStatic](/engine/6000.0/script-reference/unity-engine/android-java-object/set-static.md)을 선택합니다.

필드의 값을 가져오거나 값을 반환하는 메서드를 호출하면 [제네릭](https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/types/generics)을 사용하여 반환 유형을 지정합니다. 필드의 값을 설정할 때 제네릭을 사용하여 설정할 필드의 유형을 지정할 수도 있습니다. 값을 반환하지 않는 메서드의 경우 일반, 비제네릭 [호출](/engine/6000.0/script-reference/unity-engine/android-java-object/call.md) 버전이 있습니다.

**중요**: 모든 [비기본형](http://developer.android.com/reference/java/lang/Class.html)을 `AndroidJavaObject`로 액세스해야 합니다. 유일한 예외는 Java에서 기본 유형을 나타내지 않더라도 직접 액세스할 수 있는 문자열입니다.

### 예제

이 섹션에는 고수준 `AndroidJavaObject` 및 `AndroidJavaClass` API를 사용하는 방법을 보여 주는 코드 샘플이 포함되어 있습니다.

#### Java 문자열의 해시 코드 가져오기

다음 코드 샘플은 [문자열](http://developer.android.com/reference/java/lang/String.html#String\(java.lang.StringBuilder\))로 초기화된 [java.lang.String](http://developer.android.com/reference/java/lang/String.html)의 인스턴스를 생성하고 해당 문자열에 대한 [해시 값](http://developer.android.com/reference/java/lang/String.html#hashCode\(\))을 가져옵니다.

```c#
using UnityEngine;
public class JavaExamples
{
    public static int GetJavaStringHashCode(string text)
    {
        using (AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", text))
        {
            int hash = jo.Call<int>("hashCode");
            return hash;
        }
    }
}
```

예시는 다음과 같습니다.

1. [java.lang.String](http://developer.android.com/reference/java/lang/String.html)을 나타내는 `AndroidJavaObject`를 만듭니다. `AndroidJavaObject` 생성자는 최소 하나의 파라미터를 사용하며, 이는 인스턴스를 구성할 클래스의 이름입니다. 클래스명 뒤의 모든 파라미터는 오브젝트의 생성자 호출에 해당하며, 이 경우는 `GetJavaStringHashCode`의 `text` 파라미터입니다.
2. 문자열의 해시 코드를 가져오기 위해 [hashCode()](https://developer.android.com/reference/java/lang/String.html#hashCode\(\))를 호출합니다. 이 호출은 `hashCode()`가 해시 코드를 정수로 반환하므로 `Call`에 대한 `int` 제네릭 유형 파라미터를 사용합니다.

**참고**: 점 표기법을 사용하여 중첩된 Java 클래스를 인스턴스화할 수 없습니다. `$` 구분 기호를 사용하여 내부 클래스를 인스턴스화해야 합니다. 예를 들어 `LayoutParams` 클래스가 `ViewGroup` 클래스에 중첩된 `android.view.ViewGroup$LayoutParams` 또는 `android/view/ViewGroup$LayoutParams`를 사용합니다.

#### 캐시 디렉토리 가져오기

다음 코드 샘플은 플러그인을 사용하지 않고 C#에서 현재 애플리케이션의 캐시 디렉토리를 가져오는 방법을 보여줍니다.

```c#
using UnityEngine;

public class JavaExamples
{
    public static string GetApplicationCacheDirectory()
    {
       using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
        using (AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
        using (AndroidJavaObject javaFile = currentActivity.Call<AndroidJavaObject>("getCacheDir"))
        {
            string cacheDirectory = javaFile.Call<string>("getCanonicalPath");
            return cacheDirectory;
        }
    }
}
```

예시는 다음과 같습니다.

1. `com.unity3d.player.UnityPlayer`를 나타내는 `AndroidJavaClass`를 만듭니다. 정적 멤버에 액세스하기 위해 `AndroidJavaObject` 대신 `AndroidJavaClass`를 사용하는 것이 가장 좋습니다.
2. 현재 [활동](http://developer.android.com/reference/android/app/Activity.html)을 나타내는 `AndroidJavaObject`를 생성하며, 이는 `com.unity3d.player.UnityPlayer`의 정적 멤버입니다.
3. Activity 오브젝트에서 [getCacheDir()](http://developer.android.com/reference/android/content/Context.html#getCacheDir\(\))를 호출하여 캐시 디렉토리를 나타내는 [File](https://developer.android.com/reference/java/io/File.html) 오브젝트를 반환합니다.
4. File 오브젝트에서 [getCanonicalPath()](http://developer.android.com/reference/java/io/File.html#getCanonicalPath\(\))를 호출하면 캐시 디렉토리를 문자열로 가져올 수 있습니다.

**참고**: 이 예시는 참조용입니다. 대신 애플리케이션의 캐시 및 파일 디렉토리에 액세스하려면 [Application.temporaryCachePath](/engine/6000.0/script-reference/unity-engine/application/temporary-cache-path.md) 및 [Application.persistentDataPath](/engine/6000.0/script-reference/unity-engine/application/persistent-data-path.md) API를 사용하십시오.

#### Java에서 Unity로 데이터 전달

다음 코드 샘플은 `UnitySendMessage`를 사용하여 Java에서 Unity로 데이터를 전달하는 방법을 보여 줍니다.

```c#
using UnityEngine;

public class JavaExamples : MonoBehaviour
{

    private void Start()
    {
        AndroidJNIHelper.debug = true;
        using (AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
        {
            jc.CallStatic("UnitySendMessage", "My GameObject", "JavaMessage", "NewMessage");
        }
    }

    private void JavaMessage(string message)
    {
        Debug.Log("message from java: " + message);
    }
}
```

예시는 다음과 같습니다.

1. `com.unity3d.player.UnityPlayer`를 나타내는 `AndroidJavaClass`를 만듭니다.
2. `com.unity3d.player.UnityPlayer` 멤버인 정적`UnitySendMessage` 메서드를 호출합니다.

Unity 내에서 `UnitySendMessage`를 호출하면 Java를 사용하여 메시지를 중계하고, Java는 네이티브/Unity 코드를 다시 호출하여 오브젝트에 메시지를 전달합니다. 이 예시에서는 `JavaMessage`라는 메서드가 포함된 스크립트가 연결된 ‘My GameObject’라는 게임 오브젝트입니다.

## 베스트 프랙티스

이 섹션에서는 C# 스크립트에서 Java 및 Kotlin 플러그인 코드를 호출할 때 주의해야 할 베스트 프랙티스에 대해 설명합니다.

### JNI 호출 최소화

고수준 또는 저수준 C# API를 통해 JNI(Java 네이티브 인터페이스)를 사용하면 리소스를 많이 소모하고 속도가 느릴 수 있습니다. 성능을 향상시키고 코드 명확성을 높이기 위해 JNI 호출 수를 적게 유지하는 것이 가장 좋습니다.

불필요한 JNI 호출을 방지하기 위해 고수준 C# API는 호출하는 각 Java 메서드의 [ID](/engine/6000.0/script-reference/unity-engine/android-jni/get-method-id.md)를 캐싱합니다. 즉, 동일한 메서드에 대한 후속 호출은 첫 번째 호출만큼 리소스를 많이 소모하지 않습니다. 호출은 같은 프레임 동안 또는 동일한 AndroidJavaObject/AndroidJavaClass 인스턴스에서 발생할 필요가 없습니다. 저수준 API를 사용하고 성능상의 이점을 원하는 경우 메서드 ID를 직접 캐싱해야 합니다. 그렇지 않으면 고수준 API를 사용하는 것이 좋습니다.

**참고**: Unity는 애플리케이션이 [종료](https://support.google.com/android/answer/9079646?hl=en-GB)될 때까지 캐시를 유지합니다. 여기에는 애플리케이션이 백그라운드 상태인 경우가 포함됩니다.

### 가비지 컬렉션

Unity가 가능한 한 빨리 삭제하도록 `AndroidJavaObject` 또는 `AndroidJavaClass`의 모든 인스턴스를 `using` 문으로 래핑해야 합니다. `using`을 사용하지 않으면 Unity의 [가비지 컬렉터](/engine/6000.0/manual/analysis/performance-memory-overview/managed-memory/garbage-collector.md)는 여전히 생성된 모든 인스턴스를 릴리스해야 하지만, 언제 릴리스될지를 제어할 수는 없습니다.

다음 코드 샘플은 `using` 문을 사용하여 최적의 방식으로 시스템 언어를 가져오는 방법을 보여 줍니다.

```c#
using UnityEngine;

public class LocaleExample : MonoBehaviour
{
    void Start()
    {
        using (AndroidJavaClass cls = new AndroidJavaClass("java.util.Locale"))
        using (AndroidJavaObject locale = cls.CallStatic<AndroidJavaObject>("getDefault"))
        {
            if (locale != null)
            {
                Debug.Log("current lang = " + locale.Call<string>("getDisplayLanguage"));
            }
        }
    }
}
```

**참고**: 가비지 컬렉터의 활동 기록을 [Android 로그캣](https://docs.unity3d.com/Packages/com.unity.mobile.android-logcat@latest/index.html)에서 보려면 [AndroidJNIHelper.debug](/engine/6000.0/script-reference/unity-engine/android-jni-helper/debug.md)를 `true`로 설정합니다.
