رسیدگی به پایان یافتن «وب‌نما»

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

چرا رسیدگی به خاتمه فرایند مهم است

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

استفاده از Termination Handling API

می‌توانید این موقعیت‌ها را با لغو کردن onRenderProcessGone در WebViewClient مدیریت کنید.

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

برای اینکه برنامه‌تان پس‌از پایان فرایند پردازنده گرافیکی همچنان اجرا شود، هر «وب‌نما» در برنامه‌تان باید از این قوانین پیروی کند:

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

نمونه پیاده‌سازی

مثال زیر نشان می‌دهد که چگونه این پایان‌ها را در یک «فعالیت» کامل مدیریت کنید. از 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» منعکس نشده است. درحالی‌که به‌روزرسانی‌های آتی ممکن است این شکاف را برطرف کند، باید این مشکلات را اکنون حل کنید تا از پایداری برنامه مطمئن شوید. پیاده‌سازی این گرداننده باعث می‌شود فوراً متوجه شوید چرا برنامه‌تان بسته می‌شود.
  • تشخیص خاتمه پردازنده از مرگ فرایند برنامه: onRenderProcessGone موقعیت‌هایی را که فرایند پردازنده خاتمه می‌یابد درحالی‌که فرایند برنامه شما فعال می‌ماند، مدیریت می‌کند. اگر کل فرایند برنامه توسط سیستم در پس‌زمینه متوقف شد، از onSaveInstanceState() با WebView.saveState() و WebView.restoreState() برای حفظ و بازیابی سابقه پیمایش استفاده کنید. برای مدیریت ایمن وضعیت ذخیره‌شده بدون رسیدن به محدودیت‌های تراکنش، مدیریت کارآمد وضعیت WebView را ببینید.