WebView는 별도의 렌더기 프로세스에서 웹 콘텐츠를 실행합니다. 이 프로세스는 종료될 수 있습니다. 이러한 상황은 시스템이 메모리를 회수하기 위해 렌더러를 종료하거나 프로세스 자체가 비정상 종료되는 경우에 발생합니다. 이러한 종료를 처리하지 않으면 앱도 종료됩니다.
프로세스 종료 처리가 중요한 이유
이전 버전의 Android에서는 앱이 렌더기 프로세스 종료를 처리하지 못할 때 사용자가 문제를 알아차릴 가능성이 낮았습니다. 그러나 최신 버전의 Android는 더 복잡한 메커니즘을 사용하여 백그라운드 프로세스의 리소스 소비를 관리합니다. 따라서 앱에서 onRenderProcessGone 핸들러를 구현하지 않으면 사용자가 앱으로 돌아갈 때 눈에 보이는 포그라운드 종료를 관찰할 가능성이 더 높습니다. 앱이 원활하게 복구되고 안정적인 사용자 환경을 유지하려면 종료 처리 API를 구현해야 합니다.
Termination Handling API 사용
WebViewClient에서 onRenderProcessGone를 재정의하여 이러한 상황을 처리할 수 있습니다.
복구의 주요 요구사항
렌더기 프로세스가 종료된 후에도 앱을 계속 실행하려면 앱의 각 WebView가 다음 규칙을 따라야 합니다.
- WebView 인스턴스를 재사용하지 마세요. 렌더기가 사라지면 특정 WebView 객체는 쓸모가 없습니다. 뷰 계층 구조에서 WebView 객체를 삭제하고, 이를 소멸하고, Activity 또는 Fragment에서 이에 대한 참조를 삭제하여 소멸된 WebView 인스턴스에서 실수로 메서드를 계속 호출하지 않도록 해야 합니다.
- 새 인스턴스를 만듭니다. 웹 콘텐츠를 계속 표시하려면 새 WebView 인스턴스가 필요합니다.
- I can do that. 구현은
true를 반환하여 상황을 처리한 WebView 코드에 알려야 합니다. 해당 렌더기 프로세스를 사용하는WebView가true를 반환하지 않으면 WebView는 렌더기 프로세스 종료 이유에 따라 앱을 종료하거나 비정상 종료합니다.
구현 예시
다음 예는 전체 Activity 내에서 이러한 종료를 처리하는 방법을 보여줍니다. detail.didCrash()를 사용하여 프로세스가 비정상 종료되었는지 또는 시스템이 메모리를 회수하기 위해 렌더러를 종료했는지 기록합니다. 원인과 관계없이 코드는 레이아웃에서 작동 중지된 WebView를 삭제하고 소멸시키며 활동의 참조를 삭제하여 NullPointerException를 방지함으로써 정리합니다.
그런 다음 새 인스턴스를 준비하고 true를 반환하여 앱이 종료되지 않도록 합니다.
참고: 다음 예에서는 앱을 계속 실행하기 위해 항상 true를 반환합니다.
WebView 렌더기가 종료될 때 앱이 종료되도록 하려면 이 콜백에서 false를 반환하면 됩니다. 하지만 안정적인 사용자 환경을 위해 재생성을 처리하고 true를 반환하는 것이 좋습니다.
class MyWebViewActivity : AppCompatActivity() {
private var webViewContainer: ViewGroup? = null
private var myWebView: WebView? = null
private var crashCount = 0
private val MAX_CRASHES = 3
// NEW FLAG: Tracks if the WebView crashed while the activity was hidden
private var isWebViewPendingRecovery = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my_web_view)
webViewContainer = findViewById(R.id.my_web_view_container)
initWebView()
}
// NEW LIFECYCLE HOOK: Triggers when the user brings the app back to the foreground
override fun onResume() {
super.onResume()
// If a crash happened in the background, rebuild it now that the user can see it
if (isWebViewPendingRecovery) {
isWebViewPendingRecovery = false
Log.i("MY_APP_TAG", "Activity resumed. Recovering pending WebView now.")
recoverWebView()
}
}
private fun initWebView() {
val webView = WebView(this)
myWebView = webView
webViewContainer?.addView(webView)
webView.webViewClient = object : WebViewClient() {
override fun onRenderProcessGone(view: WebView, detail: RenderProcessGoneDetail): Boolean {
if (detail.didCrash()) {
Log.e("MY_APP_TAG", "The WebView rendering process crashed!")
} else {
Log.e("MY_APP_TAG", "System killed the WebView rendering process to reclaim memory.")
}
// Safely clean up the dead WebView
webViewContainer?.removeView(webView)
webView.destroy()
myWebView = null
// MODIFIED LOGIC: Check the lifecycle state of the Activity
// lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED) means the activity is visible and active
if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
Log.i("MY_APP_TAG", "Activity is in foreground. Recovering immediately.")
recoverWebView()
} else {
// The app is in the background. Mark it for later!
Log.w("MY_APP_TAG", "Activity is in background. Postponing recovery until user returns.")
isWebViewPendingRecovery = true
}
return true
}
}
webView.loadUrl("https://www.example.com")
}
// HELPER FUNCTION: Extracted the recovery logic to reuse it
private fun recoverWebView() {
if (crashCount < MAX_CRASHES) {
crashCount++
Log.w("MY_APP_TAG", "Recovering WebView... Attempt $crashCount")
initWebView()
} else {
Log.e("MY_APP_TAG", "WebView crashed too many times. Showing error UI.")
showErrorUI()
}
}
private fun showErrorUI() {
// Example: Inflate an error view
}
override fun onDestroy() {
super.onDestroy()
myWebView?.let {
webViewContainer?.removeView(it)
it.destroy()
}
myWebView = null
}
}
중요한 고려사항
onRenderProcessGone를 구현할 때는 앱 안정성을 보장하고 종료 이벤트에 대한 가시성을 유지하기 위해 다음 사항을 고려하세요.
- 반복적인 비정상 종료: 특정 페이지를 로드하는 동안 렌더러가 비정상 종료되면 동일한 페이지를 즉시 새로고침하지 마세요. 이로 인해 새 WebView가 다시 다운될 수 있습니다.
- 공개 상태: 이 콜백 누락으로 인한 앱 종료는 아직 Play Console 비정상 종료 보고서에 반영되지 않습니다. 향후 업데이트에서 이 격차를 해결할 수 있지만, 앱 안정성을 보장하려면 지금 이러한 문제를 해결해야 합니다. 이 핸들러를 구현하면 앱이 종료되는 이유를 즉시 파악할 수 있습니다.
- 앱 프로세스 종료와 렌더기 종료 구분:
onRenderProcessGone는 앱 프로세스가 활성 상태로 유지되는 동안 렌더기 프로세스가 종료되는 상황을 처리합니다. 백그라운드에서 시스템에 의해 전체 앱 프로세스가 종료되면WebView.saveState()및WebView.restoreState()와 함께onSaveInstanceState()를 사용하여 탐색 기록을 유지하고 복구합니다. 트랜잭션 한도에 도달하지 않고 저장된 상태를 안전하게 관리하려면 WebView 상태를 효율적으로 관리를 참고하세요.