안녕하세요.
오늘은 WebView를 구현한 앱에서 발생하는 이상한 오류에 대해 알아보고 어떻게 해결해야 하는지 짧게 알아보려고 합니다.
WebView Crash?
최근 앱에서 PlayStore에 알 수 없는 Crash 제보가 수십 건 올라오기 시작했습니다. 위 사진이 제보되던 Crash의 스택 트레이스입니다. 처음에는 생소한 단어들과 처음 보는 클래스 이름 때문에 원인을 특정하기 어려웠으나 곧 WebView 내부에서 발생하는 Crash라는 것을 알게 되었습니다.
WebView Crash 재현하기
위 오류는 WebView Render Process 과정에서 발생한 Crash입니다. 오류를 하기 위해서는 재현 경로를 알아야 하는데요. 다행히도 Rendenr Process에서 Crash가 발생하도록 하는 재현 경로를 제공해 줍니다 :)
loadUrl("chrome://crash")
E [ERROR:gl_surface_egl.cc(335)] eglChooseConfig failed with error EGL_SUCCESS
E [ERROR:render_frame_impl.cc(1022)] Intentionally crashing (with null pointer dereference) because user navigated to chrome://crash/
WebView Render Process Crash 처리하기
웹뷰 내부에서 발생한 오류이기 때문에 Android 개발자는 근본적인 원인을 해결할 수 없는 것이 현실인데요. 대신 Android 26부터 해당 Crash를 개발자가 직접 처리할 수 있는 콜백 메서드를 제공해 줍니다.
onRenderProcessGone
onRenderProcessGone은 WebView의 Render Process가 종료되었을 때 시스템으로 부터 호출되는 콜백 함수입니다.
반환 값의 자료형은 Boolean 타입인데요. 기본 값은 false로 구현되어 있습니다. 기본 값인 false를 반환할 경우 Render Process에서 Crash가 발생하면서 동시에 앱이 같이 죽게 되고, true 값을 반환하면 앱은 죽지 않는 것을 확인할 수 있습니다.
override fun onRenderProcessGone(view: WebView?, detail: RenderProcessGoneDetail?): Boolean {
return true
}
앱은 죽지 않기 때문에 목적은 달성했지만 사용자에게 좋은 UX인지 고민할 필요성은 있습니다. 사용자에게 어떠한 정보를 알려주지 않고 빈 화면만 보인다면 오류를 완전히 해결했다고 할 수는 없을 것 같습니다. 그래서 몇 가지 추가 속성을 소개하면서 구글에서 소개하는 좋은 사례를 간단히 알아봅시다.
Render Process 종료 이유 유추하기
WebView Render Process가 종료되는 원인은 크게 2가지입니다.
1. 시스템 메모리 부족으로 인해 시스템에서 WebView를 종료시킬 때
2. 알 수 없는 Render Process 충돌로 종료될 때
두 가지를 구분하는 기준은 두 번째 매개변수 RenderProcessGoneDetail 클래스의 didCrash 함수를 통해 확인할 수 있습니다.
override fun onRenderProcessGone(view: WebView, detail: RenderProcessGoneDetail): Boolean {
if (!detail.didCrash()) {
// 메모리 부족으로 WebView를 시스템에서 Kill 했을 때
}
// Render Process에서 Crash 발생
...
}
WebView 인스턴스 제거하기
매개변수로 넘어오는 WebView는 종료된 Render Process를 사용하던 주체이기 때문에 그대로 재사용될 수 없습니다. 때문에 구글은 뷰 계층에서 올바르게 제거해야 한다고 합니다. 그런데 이 부분에는 WebView를 구현하고 있는 UI Tree 구조에 따라 달라질 수 있습니다. 예를 들어, 몇 가지만 짧게 소개해보도록 하겠습니다.
mWebView?.also { webView ->
val webViewContainer: ViewGroup = findViewById(R.id.my_web_view_container)
webViewContainer.removeView(webView)
webView.destroy()
}
WebView를 포함하고 있던 ViewGroup을 찾고 해당 ViewGroup에서 WebView를 올바르게 제거하는 코드입니다. 이후 WebView를 새로 생성하는 로직을 구현하거나 간단한 오류 표시등을 사용자에게 보여줄 수도 있을 것 같습니다.
recreate()
또는 Activity를 재시작하는 recreate 함수를 이용하는 것인데요. configuration Changed가 되었을 때와 비슷하게 UI가 다시 그려지기 때문에 쉽게 새로운 WebView를 생성할 수 있습니다. 하지만 Activity를 이루는 화면이 복잡한 경우 이로 인해 모든 뷰를 다시 생성해야 하는 비용을 고려해 보세요.
이 주제에 관련된 Compose 코드는 아래 Github 링크에서 확인할 수 있습니다!
https://github.com/jaeryo2357/posting_android_sample_code/pull/3
참고
- https://developer.android.com/develop/ui/views/layout/webapps/managing-webview#java
'Android > WebView' 카테고리의 다른 글
[Android] Webview Alert 표시하기 (0) | 2023.10.25 |
---|---|
[Android] WebView로 파일을 업로드하는 방법 (0) | 2023.09.14 |
[Android] WebView에서 유투브 전체 화면을 올바르게 표시하는 방법 (0) | 2023.06.10 |