إدارة حالة WebView بكفاءة

عند إدارة مراحل نشاط تطبيق Android، يُعد الحفاظ على حالة المستخدم أثناء استرداد الموارد في الخلفية عنصرًا أساسيًا لتوفير تجربة مستخدم سلسة. بالنسبة إلى التطبيقات التي تتضمّن إجراءات على الويب، يتيح لك WebView.saveState(Bundle) تحويل سجلّ التنقّل وحالة WebView إلى Bundle. ويمكن استعادة هذه البيانات لاحقًا باستخدام WebView.restoreState(Bundle).

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

الحدّ الأقصى للمعاملات البالغ 1 ميغابايت ومحو الحالة

يفرض نظام التشغيل Android حدًا صارمًا يبلغ 1 ميغابايت على إجمالي حجم البيانات التي يمكن تخزينها في savedInstanceState. تتم مشاركة ميزانية الذاكرة البالغة 1 ميغابايت بين جميع عمليات التطبيق. إذا كان التطبيق يتضمّن عدة مثيلات WebView، يجب أن تتناسب حالة التنقّل وسجلّهما المشتركَين مع هذا التخصيص المشترك الفردي. يؤدي تجاوز هذا الحد إلى ظهور الخطأ TransactionTooLargeException، ما يؤدي إلى تعطُّل التطبيق.

تتضمّن إحدى استراتيجيات التخفيف الشائعة ولكنّها تسبّب مشاكل مراقبة حجم حزمة حالة WebView ومحو سجلّ WebView بالكامل إذا تجاوز حدّ أمان عشوائيًا (مثل 300 كيلوبايت). على الرغم من أنّ هذا الإجراء يمنع حدوث أعطال، إلا أنّه يؤدي إلى حدوث مشاكل خطيرة في تجربة المستخدم، مثل:

  • فقدان إمكانية الرجوع إلى الخلف: غالبًا ما ينهي نظام التشغيل Android عمليات التطبيقات التي تعمل في الخلفية لاستعادة الذاكرة واستخدامها في مهام أخرى. يمكنك استخدام saveState(Bundle) ضمن الاستدعاء في إحدى مراحل النشاط onSaveInstanceState() للحفاظ على سجلّ التنقّل. إذا محوت هذا السجلّ لتجنُّب حد المعاملة البالغ 1 ميغابايت، سيتم فقدان حزمة التنقّل بأكملها. عندما يعود المستخدم إلى التطبيق، يخرج زر الرجوع في النظام على الفور من المكوّن أو التطبيق لأنّه لم يتبقَّ أي سياق سابق يتيح التنقّل للخلف، بغض النظر عمّا إذا تمت إعادة تشغيل العملية.

  • إبطال ميزة BFCache: يؤدي محو السجلّ إلى منع التطبيق من استخدام ميزة "التخزين المؤقت للصفحات" (BFCache)، ما يزيل إمكانية عرض الصفحات التي تمت زيارتها سابقًا على الفور.

  • زيادة وقت الاستجابة: يفقد المستخدمون حالتهم الحالية داخل WebView، ما يتطلّب إعادة التنقّل وإعادة التهيئة بالكامل. تؤدي هذه العملية إلى زيادة كبيرة في الحمل الزائد على الشبكة ووقت استجابة المعاملات.

استراتيجيات تخفيف الأثر المعمارية

لمنع حدوث أعطال TransactionTooLargeException بدون التأثير سلبًا في تجربة المستخدم من خلال حذف السجلّ بالكامل، عليك الحفاظ على توازن دقيق بين الاحتفاظ بالحالة وكفاءة الذاكرة. من خلال تنفيذ استراتيجيات التحسين التالية، يمكنك إدارة ميزانية المعاملات البالغة 1 ميغابايت بأمان مع الحفاظ على سجلّ التنقّل الأساسي وسلامة الجلسة.

فرض حدود الحجم على تسلسل الحالة

بدلاً من محو سجلّ التنقّل بالكامل عندما يصبح كبيرًا جدًا، يكون النمط الأكثر فعالية هو اقتطاع البيانات السابقة:

  • سياسة الإسقاط المستهدَفة: استخدِم WebViewCompat.saveState() لتسلسل الحالة مع فرض حدّ معيّن على عدد البايتات (على سبيل المثال، WebViewCompat.saveState(webView, outState, maxSizeBytes)). تعمل واجهة برمجة التطبيقات هذه على إسقاط إدخالات التنقّل الأقدم تلقائيًا بالتسلسل إلى أن يتناسب إجمالي الحمولة مع التخصيص الذي حدّدته. والأهم من ذلك، أنّ هذا الإجراء يؤدي فقط إلى اقتطاع Bundle المتسلسل بدون تعديل أو محو السجلّ المباشر WebView النشط، ما يضمن بقاء إمكانية الرجوع إلى الخلف مباشرةً بدون أي تغيير.

  • إزالة الإدخالات للأمام: إذا كانت واجهة التطبيق توفّر زر الرجوع ولكنها لا تتضمّن زرّ الانتقال إلى الصفحة التالية، يمكنك تجاهل جميع إدخالات التنقل للأمام من خلال ضبط المَعلمة includeForwardState في واجهة برمجة التطبيقات saveState على القيمة false. يؤدي ذلك إلى تقليل حجم الحمولة بشكل كبير بدون التأثير في مسارات التنقّل المتاحة للمستخدم.

إدارة وقت استجابة الموارد باستخدام واجهة HTTP Cache Quota API

في حين أنّ saveState يدير الحدّ الأقصى البالغ 1 ميغابايت Bundle لسجلّ التصفّح المؤقت، تتيح واجهة برمجة التطبيقات HTTP Cache Quota API التحكّم يدويًا في موارد الويب الدائمة (ذاكرة التخزين المؤقت للقرص) على أساس كل ملف شخصي. يؤدي ذلك إلى إنشاء تمييز واضح بين سياق التنقّل القصير المدى ومواد العرض المخزّنة مؤقتًا على المدى الطويل.

يتضمّن اختيار حصة مناسبة مفاضلة بين الأداء والتكلفة:

  • تؤدي الحصص الأعلى إلى تحسين مدى التوفّر بلا إنترنت ووقت استجابة تحميل الموارد من خلال الاحتفاظ بالمزيد من مواد العرض على القرص.
  • تؤدي الحصص المنخفضة إلى تقليل مساحة التطبيق على القرص ومنع نظام التشغيل من إزالة بيانات التطبيق المهمة الأخرى من ذاكرة التخزين المؤقت.

تظل هذه الإعدادات محفوظة عند إعادة تشغيل التطبيق، ويجب ضبطها من سلسلة التعليمات الرئيسية.

يوضّح التنفيذ التالي كيفية ضبط حصة ذاكرة التخزين المؤقت للقرص للملف الشخصي التلقائي:

Kotlin

if (WebViewFeature.isFeatureSupported(WebViewFeature.MULTI_PROFILE) &&
    WebViewFeature.isFeatureSupported(WebViewFeature.HTTP_CACHE)) {
    val defaultProfile = ProfileStore.getInstance()
        .getOrCreateProfile(Profile.DEFAULT_PROFILE_NAME)
    val httpCache = defaultProfile.httpCache

    // Set explicit cache size to 50MB (50 * 1024 * 1024 bytes)
    httpCache.setQuotaBytes(50L * 1024 * 1024)
}

Java

if (WebViewFeature.isFeatureSupported(WebViewFeature.MULTI_PROFILE) &&
    WebViewFeature.isFeatureSupported(WebViewFeature.HTTP_CACHE)) {
    Profile defaultProfile = ProfileStore.getInstance()
        .getOrCreateProfile(Profile.DEFAULT_PROFILE_NAME);
    HttpCache httpCache = defaultProfile.getHttpCache();

    // Set explicit cache size to 50MB (50 * 1024 * 1024 bytes)
    httpCache.setQuotaBytes(50L * 1024 * 1024);
}

لمزيد من المعلومات حول استراتيجيات تحديد حجم الحصة وإدارة مراحل النشاط وحدود الملف الشخصي، يمكنك الاطّلاع على إدارة حصة ذاكرة التخزين المؤقت لبروتوكول HTTP في WebView.

اعتبارات الأداء الرئيسية

توضّح النقاط التالية القيود الفنية وسلوكيات البيانات الداخلية التي تحكم سلوك حالة WebView:

  • ملفات PageState ثنائية كبيرة مبهمة: يتألف% 70 تقريبًا من البيانات المخزَّنة بواسطة saveState من ملفات PageState ثنائية كبيرة داخلية من محرك العرض. تسجّل هذه البيانات حالات الجلسات الدقيقة، بما في ذلك إدخالات النماذج ومواضع التمرير في إطارات iframe. تجنَّب محاولة تحليل أو إزالة أجزاء فردية من هذه الكائنات الثنائية الكبيرة يدويًا، لأنّ ذلك يشكّل مخاطر أمنية جسيمة ويؤدي إلى فقدان سلامة عملية استعادة الجلسة.

  • إدارة السجلّ الدقيق: لا تتيح واجهة برمجة التطبيقات WebBackForwardList العادية إمكانية إزالة عناصر السجلّ الفردية بشكل عشوائي. لإدارة الحالة بشكل صارم، يجب تنفيذ استراتيجيات الاقتطاع باستخدام المَعلمتَين maxSizeBytes وincludeForwardState ضمن WebViewCompat.saveState() لضمان سلامة التصميم.