إدارة كائنات WebView

يوفّر Android العديد من واجهات برمجة التطبيقات لمساعدتك في إدارة عناصر WebView التي تعرض محتوى الويب في تطبيقك.

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

واجهة برمجة تطبيقات الإصدار

بدءًا من الإصدار Android 7.0 (المستوى 24 من واجهة برمجة التطبيقات)، يمكن للمستخدمين الاختيار من بين عدة حِزم مختلفة لعرض محتوى الويب في عنصر WebView. تتضمّن مكتبة AndroidX.webkit طريقة getCurrentWebViewPackage() لجلب المعلومات ذات الصلة بالحزمة التي تعرض محتوى الويب في تطبيقك. وتكون هذه الطريقة مفيدة عند تحليل الأخطاء التي تحدث فقط عندما يحاول تطبيقك عرض محتوى الويب باستخدام تنفيذ WebView لحزمة معيّنة.

لاستخدام هذه الطريقة، أضف المنطق الموضح في مقتطف الرمز التالي:

Kotlin

val webViewPackageInfo = WebViewCompat.getCurrentWebViewPackage(appContext)
Log.d("MY_APP_TAG", "WebView version: ${webViewPackageInfo.versionName}")

Java

PackageInfo webViewPackageInfo = WebViewCompat.getCurrentWebViewPackage(appContext);
Log.d("MY_APP_TAG", "WebView version: " + webViewPackageInfo.versionName);

خدمة التصفح الآمن من Google

لتوفير تجربة تصفُّح أكثر أمانًا للمستخدمين، تتأكّد ميزة WebView من إثبات ملكية عناوين URL باستخدام ميزة التصفّح الآمن من Google، ما يتيح لتطبيقك عرض تحذير للمستخدمين عند محاولة الانتقال إلى موقع إلكتروني يُحتمَل أن يكون غير آمن.

على الرغم من أنّ القيمة التلقائية لـ EnableSafeBrowsing هي true، قد تحتاج في بعض الحالات إلى تفعيل "التصفّح الآمن" بشكل مشروط أو إيقافه. يتيح الإصدار Android 8.0 (المستوى 26 من واجهة برمجة التطبيقات) والإصدارات الأحدث استخدام setSafeBrowsingEnabled() لتفعيل ميزة "التصفّح الآمن" لعنصر WebView فردي.

إذا أردت إيقاف جميع كائنات WebView لعمليات التحقّق من "التصفّح الآمن"، أضِف العنصر <meta-data> التالي إلى ملف بيان تطبيقك:

<manifest>
    <application>
        <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
                   android:value="false" />
        ...
    </application>
</manifest>

تحديد الإجراءات الآلية

عندما يحاول أحد مثيلات العلامة WebView تحميل صفحة يصنّفها محرّك بحث Google على أنّها تهديد معروف، يعرض WebView تلقائيًا إعلانًا بينيًا يحذر المستخدمين من التهديد المعروف. تتيح هذه الشاشة للمستخدمين خيار تحميل عنوان URL على أي حال أو العودة إلى صفحة سابقة آمنة.

إذا كنت تستهدف الإصدار Android 8.1 (مستوى واجهة برمجة التطبيقات 27) أو إصدارًا أحدث، يمكنك تحديد طريقة استجابة تطبيقك لتهديد معروف بشكل آلي من خلال الطرق التالية:

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

توضّح مقتطفات الرمز التالية كيفية توجيه مثيلات تطبيقك WebView إلى وضع الأمان في أي وقت بعد مواجهة تهديد معروف:

MyWebActivity.java

Kotlin

private lateinit var superSafeWebView: WebView
private var safeBrowsingIsInitialized: Boolean = false

// ...

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    superSafeWebView = WebView(this)
    superSafeWebView.webViewClient = MyWebViewClient()
    safeBrowsingIsInitialized = false

    if (WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) {
        WebViewCompat.startSafeBrowsing(this, ValueCallback<Boolean> { success ->
            safeBrowsingIsInitialized = true
            if (!success) {
                Log.e("MY_APP_TAG", "Unable to initialize Safe Browsing!")
            }
        })
    }
}

Java

private WebView superSafeWebView;
private boolean safeBrowsingIsInitialized;

// ...

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    superSafeWebView = new WebView(this);
    superSafeWebView.setWebViewClient(new MyWebViewClient());
    safeBrowsingIsInitialized = false;

    if (WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) {
        WebViewCompat.startSafeBrowsing(this, new ValueCallback<Boolean>() {
            @Override
            public void onReceiveValue(Boolean success) {
                safeBrowsingIsInitialized = true;
                if (!success) {
                    Log.e("MY_APP_TAG", "Unable to initialize Safe Browsing!");
                }
            }
        });
    }
}

MyWebViewClient.java

Kotlin

class MyWebViewClient : WebViewClientCompat() {
    // Automatically go "back to safety" when attempting to load a website that
    // Google identifies as a known threat. An instance of WebView calls this
    // method only after Safe Browsing is initialized, so there's no conditional
    // logic needed here.
    override fun onSafeBrowsingHit(
            view: WebView,
            request: WebResourceRequest,
            threatType: Int,
            callback: SafeBrowsingResponseCompat
    ) {
        // The "true" argument indicates that your app reports incidents like
        // this one to Safe Browsing.
        if (WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY)) {
            callback.backToSafety(true)
            Toast.makeText(view.context, "Unsafe web page blocked.", Toast.LENGTH_LONG).show()
        }
    }
}

Java

public class MyWebViewClient extends WebViewClientCompat {
    // Automatically go "back to safety" when attempting to load a website that
    // Google identifies as a known threat. An instance of WebView calls this
    // method only after Safe Browsing is initialized, so there's no conditional
    // logic needed here.
    @Override
    public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
            int threatType, SafeBrowsingResponseCompat callback) {
        // The "true" argument indicates that your app reports incidents like
        // this one to Safe Browsing.
        if (WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY)) {
            callback.backToSafety(true);
            Toast.makeText(view.getContext(), "Unsafe web page blocked.",
                    Toast.LENGTH_LONG).show();
        }
    }
}

واجهة برمجة تطبيقات الموقع الجغرافي في HTML5

بالنسبة إلى التطبيقات التي تستهدف الإصدار Android 6.0 (مستوى واجهة برمجة التطبيقات 23) والإصدارات الأحدث، لا تتوافق واجهة برمجة التطبيقات Geolocation API إلا مع المصادر الآمنة مثل HTTPS. يتم تلقائيًا رفض أي طلب يتم إرساله إلى واجهة برمجة التطبيقات Geolocation API على الأصول غير الآمنة بدون استدعاء طريقة onGeolocationPermissionsShowPrompt() المقابلة.

إيقاف جمع بيانات المقاييس

بإمكان "WebView" تحميل بيانات التشخيص المخفية الهوية إلى Google عندما يمنح المستخدم موافقته. يتم جمع البيانات على أساس كل تطبيق لكل تطبيق ينشئ مثيلاً لـ WebView. ويمكنك إيقاف هذه الميزة من خلال إنشاء العلامة التالية في عنصر <application> في البيان:

<manifest>
    <application>
    ...
    <meta-data android:name="android.webkit.WebView.MetricsOptOut"
               android:value="true" />
    </application>
</manifest>

لا يتمّ تحميل البيانات من أحد التطبيقات إلّا إذا وافق المستخدم و لم يوقف هذا التطبيق الإجراء. لمزيد من المعلومات حول إيقاف الإبلاغ عن بيانات التشخيص، راجع خصوصية المستخدم في إعداد تقارير WebView.

واجهة برمجة تطبيقات معالجة الإنهاء

تتعامل واجهة برمجة تطبيقات Finish Handling API مع الحالات التي تتوقف فيها عملية العرض لكائن WebView، إما لأنّ النظام يوقف العارض لاستعادة الذاكرة الضرورية أو بسبب تعطُّل عملية العارض. باستخدام واجهة برمجة التطبيقات هذه، يمكنك السماح لتطبيقك بمواصلة التنفيذ، حتى في حال إنهاء عملية العارض.

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

يوضّح مقتطف الرمز التالي كيفية استخدام واجهة برمجة التطبيقات هذه في Activity:

Kotlin

    
inner class MyRendererTrackingWebViewClient : WebViewClient() {
    private var mWebView: WebView? = null

    override fun onRenderProcessGone(view: WebView, detail: RenderProcessGoneDetail): Boolean {
        if (!detail.didCrash()) {
            // Renderer is killed because the system ran out of memory. The app
            // can recover gracefully by creating a new WebView instance in the
            // foreground.
            Log.e("MY_APP_TAG", ("System killed the WebView rendering process " +
                "to reclaim memory. Recreating..."))

            mWebView?.also { webView ->
                val webViewContainer: ViewGroup = findViewById(R.id.my_web_view_container)
                webViewContainer.removeView(webView)
                webView.destroy()
                mWebView = null
            }

            // By this point, the instance variable "mWebView" is guaranteed to
            // be null, so it's safe to reinitialize it.

            return true // The app continues executing.
        }

        // Renderer crashes because of an internal error, such as a memory
        // access violation.
        Log.e("MY_APP_TAG", "The WebView rendering process crashed!")

        // In this example, the app itself crashes after detecting that the
        // renderer crashed. If you handle the crash more gracefully and let
        // your app continue executing, you must destroy the current WebView
        // instance, specify logic for how the app continues executing, and
        // return "true" instead.
        return false
    }
}

Java

public class MyRendererTrackingWebViewClient extends WebViewClient {
    private WebView mWebView;

    @Override
    public boolean onRenderProcessGone(WebView view,
            RenderProcessGoneDetail detail) {
        if (!detail.didCrash()) {
            // Renderer is killed because the system ran out of memory. The app
            // can recover gracefully by creating a new WebView instance in the
            // foreground.
            Log.e("MY_APP_TAG", "System killed the WebView rendering process " +
                    "to reclaim memory. Recreating...");

            if (mWebView != null) {
                ViewGroup webViewContainer =
                        (ViewGroup) findViewById(R.id.my_web_view_container);
                webViewContainer.removeView(mWebView);
                mWebView.destroy();
                mWebView = null;
            }

            // By this point, the instance variable "mWebView" is guaranteed to
            // be null, so it's safe to reinitialize it.

            return true; // The app continues executing.
        }

        // Renderer crashes because of an internal error, such as a memory
        // access violation.
        Log.e("MY_APP_TAG", "The WebView rendering process crashed!");

        // In this example, the app itself crashes after detecting that the
        // renderer crashed. If you handle the crash more gracefully and let
        // your app continue executing, you must destroy the current WebView
        // instance, specify logic for how the app continues executing, and
        // return "true" instead.
        return false;
    }
}

واجهة برمجة تطبيقات أهمية Renderer

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

يوضّح مقتطف الرمز التالي كيفية منح الأولوية لعملية العارض المرتبطة بكائنات WebView في تطبيقك:

Kotlin

val myWebView: WebView = ...
myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true)

Java

WebView myWebView;
myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true);

في هذا المقتطف بالتحديد، تكون أولوية العارض هي نفسها الأولوية التلقائية للتطبيق أو مرتبطة بها. تقلّل الوسيطة true من أولوية العارض إلى RENDERER_PRIORITY_WAIVED عندما لا يكون كائن WebView المرتبط مرئيًا. بعبارة أخرى، تشير الوسيطة true إلى أنّ تطبيقك لا يهتم بما إذا كان النظام يُبقي عملية العارض أم لا. في الواقع، يؤدي مستوى الأولوية الأدنى هذا إلى خسارة عملية العرض في حالات خارج الذاكرة.

لمعرفة المزيد من المعلومات حول كيفية تعامل النظام مع حالات انخفاض الذاكرة، راجِع العمليات ومراحل نشاط التطبيقات.