WebView 在单独的渲染器进程中运行其网页内容。有时,此进程可能会被终止。如果系统终止渲染器以回收内存,或者进程本身崩溃,就会发生这种情况。如果您不处理这些终止情况,您的应用也会退出。
为何处理进程终止至关重要
在较低版本的 Android 上,如果应用未能处理渲染器进程终止,用户不太可能注意到问题。不过,较新版本的 Android 使用更复杂的机制来管理后台进程的资源消耗。因此,如果您的应用未针对 onRenderProcessGone 实现处理程序,那么用户在返回应用时现在更有可能看到明显的前台终止。您必须实现 Termination Handling API,才能确保应用能够顺利恢复并保持稳定的用户体验。
使用 Termination Handling API
您可以通过替换 WebViewClient 中的 onRenderProcessGone 来处理这些情况。
恢复账号的主要要求
为了在渲染器进程终止后保持应用运行,应用中的每个 WebView 都必须遵循以下规则:
- 请勿重复使用 WebView 实例。一旦渲染器消失,该特定 WebView 对象就毫无用处。您必须从视图层次结构中移除 WebView 对象,销毁该对象,并清除 Activity 或 Fragment 中对该对象的任何引用,以避免意外地继续调用已销毁的 WebView 实例上的方法。
- 创建新实例。如果您想继续显示网页内容,则需要新的 WebView 实例。
- 返回 true。您的实现必须返回
true,以告知 WebView 代码您已处理相应情况。如果使用该渲染器进程的任何WebView未返回true,WebView 将根据渲染器进程退出原因终止或崩溃您的应用。
实现示例
以下示例展示了如何在完整的 Activity 中处理这些终止情况。它使用 detail.didCrash() 记录进程是否崩溃,或者系统是否终止了渲染器以回收内存。无论原因是什么,代码都会通过以下方式进行清理:从布局中移除已失效的 WebView、销毁该 WebView,并清除 activity 对该 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 管理中心崩溃报告中。虽然未来的更新可能会弥补这一差距,但您必须立即解决这些问题,以确保应用稳定性。实现此处理程序可立即了解应用退出的原因。