التعامل مع إنهاء WebView

يشغّل WebView محتوى الويب في عملية عرض منفصلة. في بعض الأحيان، يمكن إنهاء هذه العملية. يحدث ذلك إذا أوقف النظام عملية العرض لاستعادة الذاكرة أو إذا تعطلت العملية نفسها. وإذا لم تتعامل مع عمليات الإنهاء هذه، سيتم إغلاق تطبيقك أيضًا.

أهمية التعامل مع إنهاء العملية

في الإصدارات القديمة من Android، كان من غير المرجّح أن يلاحظ المستخدمون حدوث مشكلة عند تعذُّر معالجة التطبيق لعملية إنهاء عملية العرض. ومع ذلك، تستخدم الإصدارات الأحدث من نظام التشغيل Android آليات أكثر تعقيدًا لإدارة استهلاك الموارد في العمليات التي تعمل في الخلفية. وبالتالي، إذا لم يكن تطبيقك يتضمّن معالجًا للخطأ onRenderProcessGone، من المرجّح الآن أن يلاحظ المستخدمون عمليات إنهاء مرئية للعملية في المقدّمة عند الرجوع إلى التطبيق. ويجب تنفيذ واجهة برمجة التطبيقات لمعالجة الإنهاء لضمان استعادة تطبيقك بشكل سليم والحفاظ على تجربة مستخدم مستقرة.

استخدام Termination Handling API

يمكنك التعامل مع هذه الحالات من خلال تجاهل onRenderProcessGone في WebViewClient.

المتطلبات الأساسية للاسترداد

لإبقاء تطبيقك قيد التشغيل بعد انتهاء عملية العرض، يجب أن يلتزم كل WebView في تطبيقك بالقواعد التالية:

  • لا تعِد استخدام مثيل WebView. وبعد إزالة محرّك العرض، يصبح عنصر WebView المحدّد بلا فائدة. يجب إزالة عنصر WebView من بنية العرض الخاصة بك وإتلافه ومحو أي مراجع إليه في النشاط أو الجزء لتجنُّب مواصلة استدعاء الطرق في مثيل WebView الذي تم إتلافه عن طريق الخطأ.
  • إنشاء مثيل جديد إذا أردت مواصلة عرض محتوى الويب، عليك استخدام مثيل WebView جديد.
  • إرجاع القيمة "صحيح" يجب أن يعرض التنفيذ true لإعلام رمز WebView بأنّك تعاملت مع الموقف. إذا لم تعرض أي علامة WebView تستخدم عملية العرض هذه الرمز true، سيوقف WebView تطبيقك أو سيتعطّل وفقًا لسبب إنهاء عملية العرض.

مثال على التنفيذ

يوضّح المثال التالي كيفية التعامل مع عمليات الإنهاء هذه ضمن نشاط كامل. يستخدم هذا الحقل 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 Console حتى الآن. على الرغم من أنّ التحديثات المستقبلية قد تعالج هذه المشكلة، عليك حلّها الآن لضمان ثبات التطبيق. يوفّر تنفيذ معالج الأخطاء هذا إمكانية الاطّلاع الفوري على سبب خروج تطبيقك.