مدیریت خاتمه WebView

وب ویو محتوای وب خود را در یک فرآیند رندر جداگانه اجرا می‌کند. گاهی اوقات، این فرآیند می‌تواند خاتمه یابد. این اتفاق زمانی می‌افتد که سیستم رندرکننده را برای بازیابی حافظه از بین ببرد یا اگر خود فرآیند از کار بیفتد. اگر این خاتمه‌ها را مدیریت نکنید، برنامه شما نیز خارج خواهد شد.

چرا مدیریت خاتمه فرآیند حیاتی است؟

در نسخه‌های پایین‌تر اندروید، کاربران کمتر متوجه مشکلی می‌شدند که یک برنامه در مدیریت خاتمه فرآیند رندرینگ با شکست مواجه می‌شد. با این حال، نسخه‌های جدیدتر اندروید از مکانیسم‌های پیچیده‌تری برای مدیریت مصرف منابع فرآیندهای پس‌زمینه استفاده می‌کنند. در نتیجه، اگر برنامه شما یک کنترل‌کننده برای onRenderProcessGone پیاده‌سازی نکرده باشد، اکنون احتمال بیشتری وجود دارد که کاربران هنگام بازگشت به برنامه، خاتمه‌های پیش‌زمینه قابل مشاهده را مشاهده کنند. پیاده‌سازی API مدیریت خاتمه برای اطمینان از بازیابی صحیح برنامه و حفظ تجربه کاربری پایدار ضروری است.

از API مدیریت خاتمه استفاده کنید

شما می‌توانید این موقعیت‌ها را با override کردن onRenderProcessGone در WebViewClient خود مدیریت کنید.

الزامات کلیدی برای بازیابی

برای اینکه برنامه شما پس از پایان فرآیند رندر، همچنان در حال اجرا باشد، هر WebView در برنامه شما باید از این قوانین پیروی کند:

  • از نمونه WebView دوباره استفاده نکنید. پس از حذف رندرکننده، آن شیء خاص WebView بی‌فایده است. شما باید شیء WebView را از سلسله مراتب view خود حذف کنید، آن را از بین ببرید و هرگونه ارجاع به آن را در Activity یا Fragment خود پاک کنید تا از ادامه فراخوانی تصادفی متدها در نمونه WebView از بین رفته جلوگیری شود.
  • یک نمونه جدید ایجاد کنید. اگر می‌خواهید به نمایش محتوای وب ادامه دهید، به یک نمونه جدید WebView نیاز دارید.
  • مقدار true را برگردانید. پیاده‌سازی شما باید true را برگرداند تا به کد WebView بگوید که شما وضعیت را مدیریت کرده‌اید. اگر هر WebView که از آن فرآیند رندر استفاده می‌کند true برنگرداند، WebView برنامه شما را مطابق با دلیل خروج فرآیند رندر، از کار می‌اندازد یا از کار می‌اندازد.

مثال پیاده‌سازی

مثال زیر نحوه مدیریت این خاتمه‌ها را در یک Activity کامل نشان می‌دهد. این مثال از detail.didCrash() برای ثبت اینکه آیا فرآیند از کار افتاده یا اینکه سیستم رندرکننده را برای بازیابی حافظه از بین برده است، استفاده می‌کند. صرف نظر از علت، کد با حذف WebView از کار افتاده از طرح‌بندی، از بین بردن آن و پاک کردن ارجاعات Activity برای جلوگیری 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 منعکس نشده است. در حالی که به‌روزرسانی‌های آینده ممکن است این شکاف را برطرف کنند، شما باید اکنون این مشکلات را حل کنید تا ثبات برنامه تضمین شود. پیاده‌سازی این هندلر، امکان مشاهده فوری دلیل خروج برنامه شما را فراهم می‌کند.