توفّر منصة Android عدة واجهات برمجة تطبيقات لمساعدتك في إدارة عناصر WebView التي تعرض محتوى الويب في تطبيقك.
توضّح هذه الصفحة كيفية استخدام واجهات برمجة التطبيقات هذه للتعامل مع عناصر WebView بشكل أكثر فعالية، ما يحسّن من ثبات تطبيقك وأمانه.
واجهة برمجة تطبيقات الإصدار
اعتبارًا من Android 7.0 (مستوى واجهة برمجة التطبيقات 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 هي "صحيح"، هناك حالات قد تريد فيها تفعيل ميزة "التصفّح الآمن" بشكل مشروط فقط أو إيقافها. يتيح 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 من واجهة برمجة التطبيقات) والإصدارات الأحدث، لا تتوافق واجهة برمجة تطبيقات تحديد الموقع الجغرافي إلا مع المصادر الآمنة، مثل HTTPS. يتم تلقائيًا رفض أي طلب إلى واجهة برمجة تطبيقات تحديد الموقع الجغرافي على مصادر غير آمنة بدون استدعاء الطريقة المقابلة onGeolocationPermissionsShowPrompt().
إيقاف جمع المقاييس
يمكن لـ WebView تحميل بيانات التشخيص المجهولة إلى Google عندما يمنح المستخدم موافقته. يتم جمع البيانات على أساس كل تطبيق على حدة لكل تطبيق ينشئ مثيلاً لـ WebView. يمكنك إيقاف هذه
الميزة من خلال إنشاء العلامة التالية في عنصر
<application> في ملف البيان:
<manifest> <application> ... <meta-data android:name="android.webkit.WebView.MetricsOptOut" android:value="true" /> </application> </manifest>
لا يتم تحميل البيانات من أحد التطبيقات إلا إذا وافق المستخدم ولم يوقف التطبيق هذه الميزة. لمزيد من المعلومات حول إيقاف ميزة إعداد تقارير بيانات التشخيص ، يُرجى الاطّلاع على مقالة خصوصية المستخدم في تقارير WebView.
واجهة برمجة تطبيقات معالجة الإنهاء
تعالج واجهة برمجة تطبيقات معالجة الإنهاء الحالات التي تختفي فيها عملية العارض لعنصر 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; } }
واجهة برمجة تطبيقات أهمية العارض
عندما تعمل عناصر WebView
في
وضع العمليات المتعددة، يكون لديك بعض المرونة في كيفية تعامل تطبيقك مع
حالات نقص الذاكرة. يمكنك استخدام واجهة برمجة تطبيقات أهمية العارض، التي تم طرحها في 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 إلى أنّ تطبيقك لا يهتم بما إذا كان النظام يحافظ على عملية العارض نشطة. في الواقع، من المرجّح أن يتم إنهاء عملية العارض في حالات نقص الذاكرة بسبب مستوى الأولوية المنخفض هذا.
لمزيد من المعلومات حول تشخيص وتحسين استهلاك الذاكرة في تطبيقك عند استخدام محتوى الويب، يُرجى الاطّلاع على مقالة إدارة ذاكرة WebView وتشخيصها. لمزيد من المعلومات حول كيفية تعامل النظام مع حالات نقص الذاكرة في جميع العمليات، يُرجى الاطّلاع على مقالة العمليات ودورة حياة التطبيق.
الاحتفاظ بالحالة وسجلّ التنقّل
عندما يستعيد النظام موارد الخلفية أو يخضع الجهاز لتغيير في الإعدادات، قد يتم تدمير نشاط التطبيق وعناصر WebView الخاصة به. للاحتفاظ بسياق تنقّل المستخدم، استخدِم الطريقة saveState(Bundle)
ضِمن onSaveInstanceState() لتسلسل سجلّ التنقّل في
Bundle، ثم استعِده باستخدام restoreState(Bundle).
بما أنّ Android يفرض حدًا صارمًا يبلغ 1 ميغابايت على العملية بأكملها لـ savedInstanceState، يمكن أن يؤدي نشر جلسات التصفّح الكبيرة على نحو متسلسِل باستخدام واجهة برمجة التطبيقات القياسية للإطار إلى TransactionTooLargeException.
لفرض حدود الحجم أو إزالة إدخالات سجلّ التنقّل للأمام بأمان، استخدِم طريقة Jetpack Webkit WebViewCompat.saveState().
للاطّلاع على استراتيجيات مفصّلة وأفضل الممارسات بشأن إدارة الحالة بكفاءة، يُرجى الاطّلاع على مقالة إدارة حالة WebView بكفاءة.