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

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

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

Version API

بدءًا من الإصدار 7.0 من نظام التشغيل Android (مستوى واجهة برمجة التطبيقات 24)، يمكن للمستخدمين الاختيار من بين عدة حِزم مختلفة لعرض محتوى الويب في عنصر WebView. تتضمّن مكتبة Jetpack 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 على أي حال أو الرجوع إلى صفحة سابقة آمنة.

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

  • يمكنك التحكّم في ما إذا كان تطبيقك يبلّغ ميزة &quot;التصفّح الآمن&quot; عن التهديدات المعروفة.
  • يمكنك ضبط تطبيقك على تنفيذ إجراء معيّن تلقائيًا، مثل الرجوع إلى الوضع الآمن، في كل مرة يصادف فيها عنوان 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 Geolocation API

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

إيقاف جمع المقاييس

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

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

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

Termination Handling API

تتعامل واجهة برمجة التطبيقات Termination 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 Importance API

عندما WebView كائنات تعمل في وضع العمليات المتعددة، يتوفّر لك بعض المرونة في طريقة تعامل تطبيقك مع حالات نقص الذاكرة. يمكنك استخدام Renderer Importance API، الذي تم طرحه في Android 8.0، لضبط سياسة أولوية للعارض الذي تم تعيينه لكائن 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 إلى أنّ تطبيقك لا يهمّه ما إذا كان النظام سيُبقي عملية العرض قيد التشغيل. في الواقع، يؤدي مستوى الأولوية المنخفض هذا إلى زيادة احتمالية إنهاء عملية العرض في حالات نقص الذاكرة.

لمزيد من المعلومات عن طريقة تعامل النظام مع حالات نقص الذاكرة، يُرجى الاطّلاع على العمليات ودورة حياة التطبيق.