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、將其刪除,並清除活動的參照,以防止 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 管理中心的當機報告目前不會反映這類情況。日後的更新或許會解決這個問題,但您現在必須解決這些問題,才能確保應用程式穩定性。實作這個處理常式可立即瞭解應用程式的退出原因。