WebViewCompat.navigate هو بديل محسّن لـ WebView.loadUrl يوفّر تحكّمًا دقيقًا في تحميل صفحات الويب وإدارة السجلّ وتتبُّع مراحل نشاط التنقّل في WebView.
في السابق، كانت هناك قيود ملحوظة على بدء عمليات التنقّل في الصفحات باستخدام loadUrl:
- عدم إمكانية استبدال إدخال السجلّ: لم يكن بإمكانك استبدال إدخال السجلّ الحالي، ما كان يجعل من المستحيل الانتقال إلى صفحة جديدة بدون إضافة إدخال إلى السجلّ الخلفي.
- عمليات الاستدعاء غير المرتبطة: لم تتوفّر آلية مباشرة لربط طلب
loadUrlمعيّن بأحداث عمليات الاستدعاء اللاحقة فيWebViewClient. - لم يتم حفظ العناوين الإضافية: لم يتم حفظ العناوين المخصّصة التي تم تمريرها إلى
loadUrlكجزء من حالةWebView، وبالتالي تم فقدانها عند استعادة الحالة.
تعمل واجهة برمجة التطبيقات WebViewCompat.navigate على حلّ هذه المشاكل من خلال توفير الميزات التالية:
- استبدال إدخال سجلّ التنقّل: يتيح لك استبدال الصفحة الحالية في حزمة سجلّ
WebView. - تتبُّع عمليات معاودة الاتصال المرتبطة: تعرض هذه الطريقة عنصر
Navigationيعمل كمعرّف فريد في جميع مراحل نشاط التنقّل. - إتاحة عناوين الحالة المحفوظة: يتم حفظ العناوين الإضافية بشكل موثوق في حزمة الحالة
WebViewحتى يمكن إعادة استخدامها عند استعادة الحالة.
الإمكانات والقيود الرئيسية
قبل استخدام WebViewCompat.navigate، يجب مراعاة قواعد التشغيل والقيود التالية:
أمان سلسلة التعليمات: يجب استدعاء
WebViewCompat.navigateفي سلسلة تعليمات واجهة المستخدم (الرئيسية).الإلغاء والأولوية: لا يمكن إلغاء عمليات التنقّل أثناء الرحلة بشكل صريح. ومع ذلك، فإنّ بدء مكالمة
navigateجديدة علىWebViewيحلّ محلّ أي عملية تنقّل نشطة.إتاحة مخطّطات URI: تتوفّر مخطّطات URI القياسية (مثل
https:وhttp:) والمخصّصة. المخططjavascript:غير متاح.الحدّ الأقصى لحجم عنوان URL: الحدّ الأقصى لطول سلسلة عنوان URL المسموح به هو 2 ميغابايت.
التحقّق من توفّر الميزة: تحقَّق دائمًا من توفّر الميزة باستخدام
WebViewFeature.isFeatureSupportedقبل استدعاء واجهة برمجة التطبيقات للحفاظ على التوافق مع إصدارات مختلفة من حِزم APK الخاصة بـ WebView.
بدء التنقّل وتتبُّع مراحل النشاط
لضبط التنقّل وتتبُّع مراحل نشاطه، اتّبِع الخطوات التالية:
- سجِّل عملية تنفيذ
NavigationListenerباستخدامWebViewCompat.addNavigationListenerأثناء عملية إعدادWebViewلتلقّي عمليات ردّ الاتصال المنظَّمة لدورة الحياة. سجِّل أداة معالجة الأحداث مرة واحدة (بدلاً من تسجيلها في كل عملية تنقّل) لمنع تسرُّب الذاكرة وتنفيذ عمليات معاودة الاتصال المكرّرة. - أنشئ مثيلاً من
NavigationParametersباستخدامNavigationParameters.Builderلتحديد السلوكيات الاختيارية، مثل استبدال السجلّ أو عناوين HTTP المخصّصة. - استدعِ الدالة
WebViewCompat.navigate، مع تمرير مثيلWebViewوعنوان URL المقصود والمَعلمات.
تعرض WebViewCompat.navigate عنصر Navigation يحدّد الطلب بشكل فريد. في عمليات الاسترجاع NavigationListener، قارِن هذا العنصر بالمعلمة Navigation الواردة لتتبُّع عملية التنقّل المحدّدة هذه.
مثال على التنفيذ
يوضّح المثال التالي كيفية ضبط مَعلمات التنقّل واستدعاء WebViewCompat.navigate والاستماع إلى أحداث دورة حياة التنقّل:
Kotlin
class WebNavigationManager(private val webView: WebView) {
// Track the navigation instance returned by the API
private var currentNavigation: Navigation? = null
init {
// 1. Define listener to observe navigation lifecycle events
val listener = object : NavigationListener {
override fun onNavigationStarted(navigation: Navigation) {
if (navigation == currentNavigation) {
// Navigation started
}
}
override fun onNavigationRedirected(navigation: Navigation) {
if (navigation == currentNavigation) {
// Navigation encountered a redirect
}
}
override fun onNavigationCompleted(navigation: Navigation) {
if (navigation == currentNavigation) {
if (navigation.didCommit()) {
// Navigation committed successfully
} else if (navigation.didCommitErrorPage()) {
// Navigation committed an error page
val statusCode = navigation.statusCode
val error = navigation.webResourceError
}
}
}
override fun onFirstContentfulPaintMillis(page: Page, durationMillis: Long) {
// Match page with current navigation
if (page == currentNavigation?.page) {
// Page rendering started (First Contentful Paint achieved)
}
}
}
// 2. Register listener on the main thread
WebViewCompat.addNavigationListener(webView, listener)
}
@UiThread
fun navigateToPage(url: String) {
// Check feature availability
if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEBVIEW_NAVIGATE_EXPERIMENTAL_V1)) {
// Fall back to standard loadUrl if navigate API is unavailable
webView.loadUrl(url)
return
}
// 3. Configure navigation parameters
val params = NavigationParameters.Builder()
.setShouldReplaceCurrentEntry(true)
.addAdditionalHeaders(
mapOf("X-Test-Navigate-Header" to "TestValue")
)
.build()
// 4. Initiate navigation on the UI thread
currentNavigation = WebViewCompat.navigate(webView, url, params)
}
}
Java
public class WebNavigationManager {
private Navigation mCurrentNavigation;
private final WebView mWebView;
public WebNavigationManager(@NonNull WebView webView) {
mWebView = webView;
setupListener();
}
private void setupListener() {
// 1. Define listener to observe navigation lifecycle events
NavigationListener listener = new NavigationListener() {
@Override
public void onNavigationStarted(@NonNull Navigation navigation) {
if (navigation.equals(mCurrentNavigation)) {
// Navigation started
}
}
@Override
public void onNavigationRedirected(@NonNull Navigation navigation) {
if (navigation.equals(mCurrentNavigation)) {
// Navigation encountered a redirect
}
}
@Override
public void onNavigationCompleted(@NonNull Navigation navigation) {
if (navigation.equals(mCurrentNavigation)) {
if (navigation.didCommit()) {
// Navigation committed successfully
} else if (navigation.didCommitErrorPage()) {
// Navigation committed an error page
int statusCode = navigation.getStatusCode();
WebResourceErrorCompat error = navigation.getWebResourceError();
}
}
}
@Override
public void onFirstContentfulPaintMillis(@NonNull Page page, long durationMillis) {
if (mCurrentNavigation != null && page.equals(mCurrentNavigation.getPage())) {
// Page rendering started (First Contentful Paint achieved)
}
}
};
// 2. Register listener on the main thread
WebViewCompat.addNavigationListener(mWebView, listener);
}
@UiThread
public void navigateToPage(@NonNull String url) {
// Check feature availability
if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEBVIEW_NAVIGATE_EXPERIMENTAL_V1)) {
// Fall back to standard loadUrl if navigate API is unavailable
mWebView.loadUrl(url);
return;
}
// 3. Configure navigation parameters
NavigationParameters params = new NavigationParameters.Builder()
.setShouldReplaceCurrentEntry(true)
.addAdditionalHeaders(Collections.singletonMap(
"X-Test-Navigate-Header", "TestValue"
))
.build();
// 4. Initiate navigation on the UI thread
mCurrentNavigation = WebViewCompat.navigate(mWebView, url, params);
}
}
طُرق التعذُّر ومعالجة الأخطاء
توفّر واجهة برمجة التطبيقات WebViewCompat.navigate آليات مختلفة للتعامل مع أخطاء الإعداد وحالات تعذُّر التنقّل في وقت التشغيل:
استثناءات الوسيطة غير الصالحة
يؤدي تمرير وسيطات غير صالحة إلى تشغيل IllegalArgumentException متزامن.
تشمل الأسباب الشائعة ما يلي:
- إدخال القيمة
nullللمَعلمات المطلوبة غير الفارغة (webViewأوurlأوparams) - توفير مخطّط URL غير متوافق، مثل
javascript: - تمرير مفاتيح أو قيم عناوين HTTP غير صالحة لا تتوافق مع مواصفات RFC 2616
أخطاء في عملية التنقّل
في حال حدوث خطأ أثناء طلب شبكة أو تحميل صفحة (مثل رمز الحالة HTTP 404 أو تعذُّر التحويل باستخدام نظام أسماء النطاقات (DNS) أو خطأ في طبقة المقابس الآمنة)، يعرض WebViewCompat.navigate
مع ذلك عنصر Navigation صالحًا.
عند انتهاء عملية التنقّل، افحص الطرق التالية في مثيل Navigation داخل معاودة الاتصال onNavigationCompleted لتشخيص الخطأ:
getStatusCode: تعرض رمز حالة استجابة HTTP (مثلاً،404أو500).getWebResourceError: تعرض هذه السمة كائنWebResourceErrorCompatيتضمّن تفاصيل عن أخطاء الشبكة، مثل انتهاء مهلة الاتصال أو تعذُّر البحث عن المضيف.didCommitErrorPage: تشير إلى ما إذا كانWebViewقد نفّذ وعرض صفحة خطأ للمستخدم.-
didCommit: تشير إلى ما إذا تم تنفيذ عملية التنقّل بنجاح إلى صفحة مستهدَفة بدون إيقافها.
إدارة حِزم حفظ الحالة
عند تمرير عناوين إضافية باستخدام NavigationParameters، تحفظ WebView هذه العناوين في حزمة الحالة المحفوظة حتى يمكن إعادة استخدامها عند استعادة الحالة. ومع ذلك، يمكن أن تؤدي المجموعات الكبيرة من العناوين إلى زيادة حجم الحالة المحفوظة Bundle بشكل كبير.
إذا كنت بحاجة إلى تقييد حجم الحزمة لمنع TransactionTooLargeException
أثناء عمليات حفظ الحالة في Android، استخدِم WebViewCompat.saveState. تتيح لك هذه الطريقة ضبط الحد الأقصى لحجم الحزمة بالبايت، ويمكنك اختياريًا استبعاد عناصر السجلّ المتقدّم:
Kotlin
// Save state with a maximum bundle size limit (for example, 64 KB)
val maxSizeBytes = 64 * 1024
val includeForwardState = false
val outState = Bundle()
WebViewCompat.saveState(webView, outState, maxSizeBytes, includeForwardState)
Java
// Save state with a maximum bundle size limit (for example, 64 KB)
int maxSizeBytes = 64 * 1024;
boolean includeForwardState = false;
Bundle outState = new Bundle();
WebViewCompat.saveState(webView, outState, maxSizeBytes, includeForwardState);
تظل الحزمة الناتجة متوافقة مع طريقة WebView.restoreState العادية.
اقتراحات بشأن نقل البيانات والتنفيذ
لضمان الأداء والاستقرار الأمثلَين عند التنقّل في WebView،
اتّبِع الاقتراحات التالية:
النقل من
loadUrlإلىnavigate: ننقل جميع مكالماتWebView.loadUrlالقديمة إلىWebViewCompat.navigate. يضمن ذلك إدارة السجلّ بشكل موحّد ويضمن حفظ العناوين دائمًا كجزء من الحالة المحفوظة.التأكّد دائمًا من توفّر الميزة: قبل استدعاء واجهة برمجة التطبيقات، تأكَّد من توفّرها في وقت التشغيل باستخدام
WebViewFeature.isFeatureSupportedللحماية من إصدارات WebView القديمة.ربط حالات التنقّل: استخدِم العنصر
Navigationالذي تم عرضه للتمييز بين عمليات التنقّل المتزامنة أو فلترة عمليات معاودة الاتصال عند إدارة عدة مثيلاتWebView.تسجيل أداة معالجة الأحداث مرة واحدة أثناء عملية التهيئة: لأنّ
WebViewCompat.addNavigationListenerتضيف أداة معالجة الأحداث بدلاً من استبدال أداة حالية، سجِّلNavigationListenerمرة واحدة أثناء عملية إعدادWebViewلتجنُّب تسرُّب الذاكرة وتنفيذ عمليات معاودة الاتصال المكرّرة في عمليات التنقّل اللاحقة.مراقبة حجم حالة الحفظ: عند تمرير حمولات كبيرة في العناوين، استخدِم
WebViewCompat.saveStateمع حدود حجم واضحة لتجنُّب حفظ بيانات حالة مفرطة.
مراجع إضافية
لمزيد من المعلومات حول إمكانات الويب المضمّنة وتحسين الأداء، يُرجى الاطّلاع على الأدلة التالية:
- تبسيط عملية تنفيذ WebView باستخدام Jetpack Webkit
- التحميل المبني على توقّع في WebView
- تحسين بدء تشغيل WebView
- التعامل مع إنهاء عملية العرض في WebView