Beenden von WebView verarbeiten

WebView führt seine Webinhalte in einem separaten Renderer-Prozess aus. Manchmal wird dieser Prozess beendet. Das passiert, wenn das System den Renderer beendet, um Arbeitsspeicher freizugeben, oder wenn der Prozess selbst abstürzt. Wenn Sie diese Beendigungen nicht verarbeiten, wird Ihre App ebenfalls beendet.

Warum der Umgang mit Prozessbeendigung entscheidend ist

Bei älteren Android-Versionen haben Nutzer ein Problem seltener bemerkt, wenn eine App die Beendigung des Renderer-Prozesses nicht verarbeitet hat. In neueren Android-Versionen werden jedoch komplexere Mechanismen verwendet, um den Ressourcenverbrauch von Hintergrundprozessen zu verwalten. Wenn Ihre App keinen Handler für onRenderProcessGone implementiert, ist es jetzt wahrscheinlicher, dass Nutzer sichtbare Beendigungen im Vordergrund bemerken, wenn sie zur App zurückkehren. Die Implementierung der Termination Handling API ist erforderlich, damit Ihre App sich ordnungsgemäß erholt und eine stabile Nutzererfahrung bietet.

Termination Handling API verwenden

Sie können diese Situationen umgehen, indem Sie onRenderProcessGone in Ihrem WebViewClient überschreiben.

Wichtige Anforderungen für die Wiederherstellung

Damit Ihre App nach dem Beenden eines Renderer-Prozesses weiter ausgeführt wird, muss jede WebView in Ihrer App die folgenden Regeln einhalten:

  • WebView-Instanz nicht wiederverwenden: Sobald der Renderer nicht mehr vorhanden ist, ist das entsprechende WebView-Objekt nutzlos. Sie müssen das WebView-Objekt aus Ihrer Ansichtshierarchie entfernen, es zerstören und alle Verweise darauf in Ihrer Aktivität oder Ihrem Fragment löschen, um zu vermeiden, dass Methoden versehentlich für die zerstörte WebView-Instanz aufgerufen werden.
  • Neue Instanz erstellen Wenn Sie weiterhin Webinhalte anzeigen möchten, benötigen Sie eine neue WebView-Instanz.
  • Gibt „true“ zurück. Ihre Implementierung muss true zurückgeben, um dem WebView-Code mitzuteilen, dass Sie die Situation behandelt haben. Wenn ein WebView, das diesen Renderer-Prozess verwendet, nicht true zurückgibt, beendet WebView Ihre App oder lässt sie abstürzen, je nach dem Grund für das Beenden des Renderer-Prozesses.

Implementierungsbeispiel

Das folgende Beispiel zeigt, wie diese Beendigungen in einer vollständigen Aktivität behandelt werden. Mit detail.didCrash() wird protokolliert, ob der Prozess abgestürzt ist oder ob der Renderer vom System beendet wurde, um Arbeitsspeicher freizugeben. Unabhängig von der Ursache wird der Code bereinigt, indem die inaktive WebView aus dem Layout entfernt, zerstört und die Referenzen der Aktivität gelöscht werden, um NullPointerException zu verhindern. Anschließend wird eine neue Instanz vorbereitet und true zurückgegeben, damit die App nicht geschlossen wird.

Hinweis:Im folgenden Beispiel wird immer true zurückgegeben, damit die App ausgeführt wird. Wenn Ihre App beendet werden soll, sobald der WebView-Renderer beendet wird, können Sie false von diesem Callback zurückgeben. Wir empfehlen jedoch, die Neuerstellung und Rückgabe von true zu verarbeiten, um eine stabile Nutzererfahrung zu gewährleisten.

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
    }
}

Wichtige Aspekte

Beachten Sie bei der Implementierung von onRenderProcessGone die folgenden Hinweise, um die Stabilität der App zu gewährleisten und Einblick in Beendigungsereignisse zu erhalten:

  • Wiederholte Abstürze:Wenn ein Renderer beim Laden einer bestimmten Seite abstürzt, laden Sie die Seite nicht sofort neu. Das kann dazu führen, dass die neue WebView wieder abstürzt.
  • Sichtbarkeit:App-Beendigungen, die durch das Fehlen dieses Rückrufs verursacht werden, sind noch nicht in Play Console-Absturzberichten enthalten. Zukünftige Updates werden diese Lücke möglicherweise schließen, aber Sie müssen diese Probleme jetzt beheben, um die Stabilität der App zu gewährleisten. Durch die Implementierung dieses Handlers wird sofort ersichtlich, warum Ihre App beendet wird.