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 moreUnityEngine.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 moreUnityEngine.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 moreUnityEngine.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)의심 1: UniWebView 4의 버그
섹션 제목: “의심 1: UniWebView 4의 버그”Android SDK API 30과 관련된 문제인지 의심했다. UniWebView 릴리스 노트에는 다음 항목이 있다.
Fix
섹션 제목: “Fix”- An issue that
UniWebViewSafeBrowsing.IsSafeBrowsingSupportedmay always returnfalsewrongly when build against Android API 30 or above.
의심 2: 빌드 옵션 설정 누락
섹션 제목: “의심 2: 빌드 옵션 설정 누락”빌드 옵션에서 웹뷰 동작에 필요한 설정이 빠졌는지도 의심했다.
원인: 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 문서 기준으로는 다음 순서로 해결한다.
- Unity Preferences의 UniWebView 항목에서 “Adds Kotlin”을 켠다. 직접 관리하는 Gradle 템플릿을 쓴다면
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:<버전>'을 추가한다. - 설정을 바꾼 뒤 Build Settings의 Clean Build로 다시 빌드한다. UniWebView 문서는 설정을 바꾼 뒤 거의 모든 경우 Clean Build가 필요하다고 안내한다.
- 반대로 빌드에서
Duplicate class kotlin...에러가 나면 다른 패키지가 이미 Kotlin을 넣은 것이므로 “Adds Kotlin”을 끈다.