본문 바로가기
Unity/Solution

Unity IAP JNI 에러 해결 사례

by Dev_카페인 2024. 11. 28.
반응형

Unity IAP JNI 에러 해결 사례

Unity를 사용해 인앱 결제를 개발하면서 다양한 상황에서 문제가 발생할 수 있습니다. 특히 Android 환경에서 Unity IAP를 활용하는 과정 중 JNI: Init'd AndroidJavaObject with null ptr! 에러를 만나게 되면 문제를 파악하고 해결하는 데 시간이 걸릴 수 있습니다. 이번 포스트에서는 제가 겪었던 문제와 이를 해결한 사례를 공유하고자 합니다.


문제 상황: Unity IAP에서 발생한 JNI 에러

Unity IAP를 사용해 결제를 처리하던 중, android logcat에서 아래와 같은 JNI 관련 에러를 확인했습니다.

Error Unity Exception: JNI: Init'd AndroidJavaObject with null ptr!
Error Unity   at UnityEngine.AndroidJavaObject..ctor (System.IntPtr jobject) [0x0001d] in \home\bokken\build\output\unity\unity\Modules\AndroidJNI\AndroidJava.cs:344 
Error Unity   at UnityEngine._AndroidJNIHelper.GetSignature (System.Object obj) [0x001b6] in \home\bokken\build\output\unity\unity\Modules\AndroidJNI\AndroidJava.cs:1781 
Error Unity   at UnityEngine._AndroidJNIHelper.GetSignature (System.Object[] args) [0x00039] in \home\bokken\build\output\unity\unity\Modules\AndroidJNI\AndroidJava.cs:1839 
Error Unity   at UnityEngine._AndroidJNIHelper.GetMethodID (System.IntPtr jclass, System.String methodName, System.Object[] args, System.Boolean isStatic) [0x00004] in \home\bokken\build\output\unity\unity\Modules\AndroidJNI\AndroidJava.cs:1639 
Error Unity   at UnityEngine.AndroidJNIHelper.GetMethodID (System.IntPtr jclass, System.String methodName, System.Object[] args, System.Boolean isStatic) [0x00005] in \home\bokken\build\output\unity\unity\Modules\AndroidJNI\AndroidJNI.bindings.cs:166 
Error Unity   at UnityEngine.

이 에러는 결제 트랜잭션을 처리하고 Pending 상태를 완료하는 과정에서 발생했습니다. 특히, IAP 자체는 정상적으로 작동했지만, 서버에 영수증을 저장하고 완료하는 과정에서 문제가 발생했습니다.

결제 프로세스는 아래와 같은 흐름으로 설계되었습니다:

  1. 사용자가 결제를 진행하고 Google Play로부터 영수증을 획득합니다.
  2. 서버와 통신하여 영수증을 저장하고 검증합니다.
  3. 검증이 완료되면 Pending 상태를 ConfirmPendingPurchase로 완료합니다.

문제 증상

  • 결제 과정에서 발생한 트랜잭션을 완료하지 못했습니다.
  • Unity IAP가 "이미 보유하고 있는 아이템입니다"라는 문구를 노출하며 결제 트랜잭션을 처리하지 못했습니다.
  • 비동기 소켓 통신 중 스레드 풀(Thread Pool)에서 Unity IAP와 Android 간 충돌이 발생했습니다.


원인 분석: Unity IAP와 Android JNI 충돌

Unity IAP는 내부적으로 Android JNI(Java Native Interface)를 사용하여 Google Play와 통신합니다. 이 과정에서 Unity와 Android Java 객체 간의 상호작용은 반드시 메인 스레드(Main Thread)에서 이루어져야 합니다. 하지만 비동기 통신 중 스레드 풀에서 Unity IAP 메서드를 호출하면서 JNI 충돌이 발생한 것입니다.


해결 방법: MainThreadDispatcher 구현

이 문제를 해결하기 위해 MainThreadDispatcher를 구현하여 Unity IAP 호출을 메인 스레드에서 실행하도록 변경했습니다. Unity의 API는 기본적으로 메인 스레드에서 실행되어야 하기 때문에, 아래와 같은 단계를 통해 문제를 해결했습니다.

MainThreadDispatcher 구현

  1. MainThreadDispatcher 스크립트 생성
using System;
using System.Collections.Concurrent;
using UnityEngine;

public class MainThreadDispatcher : MonoBehaviour
{
    private static readonly ConcurrentQueue<Action> _executionQueue = new ConcurrentQueue<Action>();

    public static void Enqueue(Action action)
    {
        if (action == null)
        {
            throw new ArgumentNullException(nameof(action));
        }

        _executionQueue.Enqueue(action);
    }

    private void Update()
    {
        while (_executionQueue.TryDequeue(out var action))
        {
            action.Invoke();
        }
    }
}
  1. MainThreadDispatcher 초기화
    • Unity의 Scene에 빈 GameObject를 생성하고, MainThreadDispatcher 스크립트를 추가합니다.
    • 이 스크립트는 Update() 메서드를 통해 메인 스레드에서 큐에 담긴 작업을 실행합니다.
  2. IAP 호출을 메인 스레드로 이동

결제 프로세스의 Pending 처리를 다음과 같이 수정했습니다:

public void ProcessPendingPurchase(Product purchasedProduct)
{
    MainThreadDispatcher.Enqueue(() =>
    {
        Debug.Log($"ProcessPendingPurchase, ProductID: {purchasedProduct.definition.id}");
        controller.ConfirmPendingPurchase(purchasedProduct);
    });
}

결과

이 방식을 적용한 후 JNI 관련 에러가 더 이상 발생하지 않았습니다. 결제 과정에서 비동기 통신과 Unity IAP 호출이 충돌하지 않도록 안전하게 처리할 수 있었습니다. 이제 결제 트랜잭션이 정상적으로 완료되고, 사용자 경험도 개선되었습니다.


교훈 및 팁

  1. Unity API는 반드시 메인 스레드에서 호출해야 합니다.
    • Unity의 API는 대부분 메인 스레드에서 실행되도록 설계되어 있습니다. 비동기 작업을 사용할 때 메인 스레드로 작업을 전환해야 JNI 충돌을 방지할 수 있습니다.
  2. 결제 프로세스 설계에서 Pending 상태를 고려해야 합니다.
    • Google Play의 결제 시스템은 Pending 상태를 활용합니다. 트랜잭션을 적절히 완료하지 못하면, 사용자에게 "이미 보유하고 있는 아이템" 메시지가 표시될 수 있습니다.
  3. MainThreadDispatcher는 다양한 비동기 작업에서 활용 가능합니다.
    • 결제 처리뿐 아니라, 다른 비동기 소켓 통신, 네트워크 요청, 또는 데이터 처리에서도 메인 스레드 전환을 손쉽게 구현할 수 있습니다.

이번 사례를 통해 비동기 작업과 Unity API 호출 간의 관계를 이해하고, 문제를 해결하는 데 도움되길 바랍니다. Unity IAP를 사용하면서 비슷한 문제를 겪는 분들께 유용한 가이드가 되길 바랍니다!

반응형