WebView'nin sonlandırılmasını işleme

WebView, web içeriğini ayrı bir oluşturucu işleminde çalıştırır. Bu işlem bazen sonlandırılabilir. Bu durum, sistemin belleği geri kazanmak için oluşturucuyu sonlandırması veya sürecin kendisinin kilitlenmesi durumunda meydana gelir. Bu sonlandırmaları işlemezseniz uygulamanız da sonlandırılır.

İşlem sonlandırmayı yönetmek neden önemlidir?

Android'in eski sürümlerinde, bir uygulama oluşturma işlemi sonlandırmayı işleyemediğinde kullanıcıların sorunu fark etme olasılığı daha düşüktü. Ancak Android'in daha yeni sürümlerinde, arka plan işlemlerinin kaynak tüketimini yönetmek için daha karmaşık mekanizmalar kullanılır. Dolayısıyla, uygulamanız onRenderProcessGone için bir işleyici uygulamıyorsa kullanıcıların uygulamaya döndüklerinde görünür ön plan sonlandırmaları görme olasılığı artık daha yüksek. Uygulamanızın sorunsuz bir şekilde kurtarılmasını ve kararlı bir kullanıcı deneyimi sunmasını sağlamak için Termination Handling API'nin uygulanması gerekir.

Termination Handling API'yi kullanma

Bu durumları, onRenderProcessGone değerini WebViewClient içinde geçersiz kılarak ele alabilirsiniz.

Kurtarma için temel şartlar

Renderer işlemi sonlandırıldıktan sonra uygulamanızın çalışmaya devam etmesi için uygulamanızdaki her WebView'ın şu kurallara uyması gerekir:

  • WebView örneğini yeniden kullanmayın. Oluşturucu kaldırıldığında söz konusu WebView nesnesi işe yaramaz. Yok edilen WebView örneğinde yanlışlıkla yöntem çağırmaya devam etmemek için WebView nesnesini görünüm hiyerarşinizden kaldırmanız, yok etmeniz ve Etkinliğinizdeki veya Parçanızdaki tüm referanslarını temizlemeniz gerekir.
  • Yeni bir örnek oluşturun. Web içeriğini göstermeye devam etmek istiyorsanız yeni bir WebView örneğine ihtiyacınız vardır.
  • Doğru değerini döndürür. Uygulamanız, durumu ele aldığınızı WebView koduna bildirmek için true döndürmelidir. Bu oluşturma işlemi kullanan herhangi bir WebView, true döndürmezse WebView, oluşturma işlemi çıkış nedenine uygun olarak uygulamanızı sonlandırır veya kilitler.

Uygulama örneği

Aşağıdaki örnekte, bu sonlandırmaların tam bir etkinlik içinde nasıl ele alınacağı gösterilmektedir. İşlemin kilitlenip kilitlenmediğini veya sistemin belleği geri kazanmak için oluşturucuyu sonlandırıp sonlandırmadığını kaydetmek için detail.didCrash() kullanılır. Nedeni ne olursa olsun, kod, düzeni temizlemek için ölü WebView'u kaldırır, yok eder ve NullPointerException'yi önlemek için Etkinlik'in referanslarını temizler. Ardından yeni bir örnek hazırlar ve uygulamanın kapanmaması için true değerini döndürür.

Not: Aşağıdaki örnek, uygulamanın çalışmaya devam etmesi için her zaman true değerini döndürür. Uygulamanızın, WebView oluşturucu sonlandırıldığında sonlandırılmasını istiyorsanız bu geri çağırmadan false değerini döndürebilirsiniz. Ancak, kararlı bir kullanıcı deneyimi için yeniden oluşturma ve true döndürme işlemlerini yapmanızı öneririz.

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

Dikkat edilmesi gerekenler

onRenderProcessGone özelliğini uygularken uygulama kararlılığını sağlamak ve sonlandırma etkinliklerinin görünürlüğünü korumak için aşağıdaki hususları göz önünde bulundurun:

  • Tekrarlanan kilitlenmeler: Belirli bir sayfa yüklenirken oluşturucu kilitlenirse aynı sayfayı hemen yeniden yüklemeyin. Bu durum, yeni WebView'un tekrar kilitlenmesine neden olabilir.
  • Görünürlük: Bu geri çağırma eksikliğinden kaynaklanan uygulama sonlandırmaları henüz Play Console kilitlenme raporlarına yansıtılmamaktadır. Gelecekteki güncellemelerde bu eksiklik giderilebilir ancak uygulama kararlılığını sağlamak için bu sorunları şimdi çözmeniz gerekir. Bu işleyiciyi uygulamak, uygulamanızın neden çıktığı konusunda anında görünürlük sağlar.