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
truedöndürmelidir. Bu oluşturma işlemi kullanan herhangi birWebView,truedö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.