콘텐츠로 이동

Android에서 UniWebView가 NoClassDefFoundError로 동작하지 않는 문제

Android 빌드에서 UniWebView로 웹뷰 팝업을 띄우면 아래 예외가 발생하며 웹뷰가 동작하지 않았다.

AndroidJavaException: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/onevcat/uniwebview/UniWebViewInterface$Companion$prepare$1;
java.lang.NoClassDefFoundError: Failed resolution of: Lcom/onevcat/uniwebview/UniWebViewInterface$Companion$prepare$1;
com.onevcat.uniwebview.UniWebViewInterface$Companion.prepare(UniWebViewInterface.kt:18)
com.onevcat.uniwebview.UniWebViewInterface.prepare(Unknown Source:2)
com.unity3d.player.UnityPlayer.nativeRender(Native Method)
com.unity3d.player.UnityPlayer.access$300(Unknown Source:0)
com.unity3d.player.UnityPlayer$e$1.handleMessage(Unknown Source:95)
android.os.Handler.dispatchMessage(Handler.java:102)
android.os.Looper.loop(Looper.java:246)
com.unity3d.player.UnityPlayer$e.run(Unknown Source:20)
Caused by: java.lang.ClassNotFoundException: com.onevcat.uniwebview.UniWebViewInterface$Companion$prepare$1
... 8 more
UnityEngine.AndroidJNISafe.CheckException () (at <00000000000000000000000000000000>:0)
UnityEngine.AndroidJavaObject._CallStatic (System.String methodName, System.Object[] args) (at <00000000000000000000000000000000>:0)
UniWebView.Awake () (at <00000000000000000000000000000000>:0)
UnityEngine.Object.Instantiate[T] (T original) (at <00000000000000000000000000000000>:0)
Game.UI.Popup.PopupSystem+<LoadPopups>d__27.MoveNext () (at <00000000000000000000000000000000>:0)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at <00000000000000000000000000000000>:0)
Rethrow as TypeInitializationException: The type initializer for 'UniWebViewInterface' threw an exception.
UniWebView.Awake () (at <00000000000000000000000000000000>:0)
UnityEngine.Object.Instantiate[T] (T original) (at <00000000000000000000000000000000>:0)
Game.UI.Popup.PopupSystem+<LoadPopups>d__27.MoveNext () (at <00000000000000000000000000000000>:0)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at <00000000000000000000000000000000>:0)
UnityEngine.Object:Instantiate(T)
Game.UI.Popup.<LoadPopups>d__27:MoveNext()
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
AndroidJavaException: java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/jvm/internal/Intrinsics;
java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/jvm/internal/Intrinsics;
com.onevcat.uniwebview.UniWebViewInterface$Companion.hide(Unknown Source:2)
com.onevcat.uniwebview.UniWebViewInterface.hide(Unknown Source:7)
com.unity3d.player.UnityPlayer.nativeRender(Native Method)
com.unity3d.player.UnityPlayer.access$300(Unknown Source:0)
com.unity3d.player.UnityPlayer$e$1.handleMessage(Unknown Source:95)
android.os.Handler.dispatchMessage(Handler.java:102)
android.os.Looper.loop(Looper.java:246)
com.unity3d.player.UnityPlayer$e.run(Unknown Source:20)
Caused by: java.lang.ClassNotFoundException: kotlin.jvm.internal.Intrinsics
... 8 more
UnityEngine.AndroidJNISafe.CheckException () (at <00000000000000000000000000000000>:0)
UnityEngine.AndroidJNISafe.CallStaticBooleanMethod (System.IntPtr clazz, System.IntPtr methodID, UnityEngine.jvalue[] args) (at <00000000000000000000000000000000>:0)
UnityEngine.AndroidJavaObject._CallStatic[ReturnType] (System.String methodName, System.Object[] args) (at <00000000000000000000000000000000>:0)
UniWebViewInterface.Hide (System.String name, System.Boolean fade, System.Int32 edge, System.Single duration, System.String identifier) (at <00000000000000000000000000000000>:0)
UniWebView.Hide (System.Boolean fade, UniWebViewTransitionEdge edge, System.Single duration, System.Action completionHandler) (at <00000000000000000000000000000000>:0)
Game.UI.Popup.PopupWebView.OnDisable () (at <00000000000000000000000000000000>:0)
Game.UI.Popup.PopupSystem+<LoadPopups>d__27.MoveNext () (at <00000000000000000000000000000000>:0)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at <00000000000000000000000000000000>:0)
Game.UI.Popup.<LoadPopups>d__27:MoveNext()
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
AndroidJavaException: java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/jvm/internal/Intrinsics;
java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/jvm/internal/Intrinsics;
com.onevcat.uniwebview.UniWebViewInterface$Companion.setFrame(Unknown Source:2)
com.onevcat.uniwebview.UniWebViewInterface.setFrame(Unknown Source:7)
com.unity3d.player.UnityPlayer.nativeRender(Native Method)
com.unity3d.player.UnityPlayer.access$300(Unknown Source:0)
com.unity3d.player.UnityPlayer$e$1.handleMessage(Unknown Source:95)
android.os.Handler.dispatchMessage(Handler.java:102)
android.os.Looper.loop(Looper.java:246)
com.unity3d.player.UnityPlayer$e.run(Unknown Source:20)
Caused by: java.lang.ClassNotFoundException: kotlin.jvm.internal.Intrinsics
... 8 more
UnityEngine.AndroidJNISafe.CheckException () (at <00000000000000000000000000000000>:0)
UnityEngine.AndroidJavaObject._CallStatic (System.String methodName, System.Object[] args) (at <00000000000000000000000000000000>:0)
UniWebViewInterface.SetFrame (System.String name, System.Int32 x, System.Int32 y, System.Int32 width, System.Int32 height) (at <00000000000000000000000000000000>:0)
UniWebView.UpdateFrame () (at <00000000000000000000000000000000>:0)
Game.UI.Popup.PopupWebView.SetUI () (at <00000000000000000000000000000000>:0)
Game.UI.Popup.PopupWebView.Initialize (System.String url, UnityEngine.Vector4 insets, System.Single timeout) (at <00000000000000000000000000000000>:0)
System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <00000000000000000000000000000000>:0)
Game.UI.Popup.PopupSystem+<LoadPopups>d__27.MoveNext () (at <00000000000000000000000000000000>:0)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at <00000000000000000000000000000000>:0)
Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.
System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <00000000000000000000000000000000>:0)
Game.UI.Popup.PopupSystem+<LoadPopups>d__27.MoveNext () (at <00000000000000000000000000000000>:0)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at <00000000000000000000000000000000>:0)

Android SDK API 30과 관련된 문제인지 의심했다. UniWebView 릴리스 노트에는 다음 항목이 있다.

  • An issue that UniWebViewSafeBrowsing.IsSafeBrowsingSupported may always return false wrongly when build against Android API 30 or above.

빌드 옵션에서 웹뷰 동작에 필요한 설정이 빠졌는지도 의심했다.

원인: Kotlin 표준 라이브러리 누락

섹션 제목: “원인: Kotlin 표준 라이브러리 누락”

예외의 Caused by가 원인을 가리킨다. kotlin.jvm.internal.Intrinsics는 Kotlin 표준 라이브러리의 클래스이고, UniWebViewInterface$Companion$prepare$1은 UniWebView의 Kotlin 코드가 만든 클래스다. 최종 APK에 Kotlin 표준 라이브러리가 들어가지 않아 UniWebView의 Android 코드가 클래스를 찾지 못한 것이다. 위 두 의심은 이 예외와 직접 관련이 없다.

UniWebView 문서에 따르면 Android 쪽은 두 패키지에 의존한다.

  • Kotlin 표준 라이브러리(org.jetbrains.kotlin:kotlin-stdlib-jdk7)
  • Android X Browser(androidx.browser:browser)

기본값으로는 UniWebView가 Gradle 파일을 보고 둘을 자동으로 추가한다. Unity Preferences의 UniWebView 항목에서 “Adds Kotlin”이 꺼져 있거나, 다른 패키지가 Kotlin을 넣는다고 보고 끈 경우 이 예외가 난다.

당시 기록에는 최종 조치가 남아 있지 않다. UniWebView 문서 기준으로는 다음 순서로 해결한다.

  1. Unity Preferences의 UniWebView 항목에서 “Adds Kotlin”을 켠다. 직접 관리하는 Gradle 템플릿을 쓴다면 implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:<버전>'을 추가한다.
  2. 설정을 바꾼 뒤 Build Settings의 Clean Build로 다시 빌드한다. UniWebView 문서는 설정을 바꾼 뒤 거의 모든 경우 Clean Build가 필요하다고 안내한다.
  3. 반대로 빌드에서 Duplicate class kotlin... 에러가 나면 다른 패키지가 이미 Kotlin을 넣은 것이므로 “Adds Kotlin”을 끈다.