WebViewCompat.navigate הוא חלופה משופרת ל-WebView.loadUrl שמאפשרת שליטה מדויקת בטעינת דפי אינטרנט, בניהול ההיסטוריה ובמעקב אחר מחזור החיים של הניווט ב-WebView.
בעבר, לניווטים בדפים באמצעות loadUrl היו מגבלות משמעותיות:
- אי אפשר להחליף רשומה בהיסטוריה: אי אפשר להחליף את הרשומה הנוכחית בהיסטוריה, ולכן אי אפשר לנווט לדף חדש בלי להוסיף רשומה למחסנית הדפים הקודמים.
- קודים להתקשרות חזרה שאינם תלויים זה בזה: לא היה מנגנון ישיר שיכול לקשר בין שיחה ספציפית של
loadUrlלבין אירועים של קודים להתקשרות חזרה שמתרחשים לאחר מכן ב-WebViewClient. - כותרות נוספות לא נשמרו: כותרות מותאמות אישית שהועברו אל
loadUrlלא נשמרו כחלק מהמצב שלWebView, ולכן הן אבדו כששוחזר המצב.
WebViewCompat.navigate API פותר את הבעיות האלה באמצעות התכונות הבאות:
- החלפת רשומה בהיסטוריית הניווט: מאפשרת להחליף את הדף הנוכחי במחסנית ההיסטוריה
WebView. - מעקב אחר קריאות חוזרות עם קורלציה: מחזיר אובייקט
Navigationשמשמש כמזהה ייחודי בכל השלבים של מחזור החיים של הניווט. - תמיכה בכותרות של מצב שמור: כותרות נוספות נשמרות באופן מהימן בחבילת המצב
WebView, כך שאפשר לעשות בהן שימוש חוזר כשמשחזרים את המצב.
יכולות ומגבלות עיקריות
לפני שמאמצים את WebViewCompat.navigate, כדאי להביא בחשבון את כללי ההפעלה והמגבלות הבאים:
בטיחות שרשור: צריך להפעיל את
WebViewCompat.navigateבשרשור ה-UI (הראשי).ביטול ועדיפות: אי אפשר לבטל באופן מפורש ניווטים בתהליך. עם זאת, התחלת שיחה חדשה ב-
navigateבאותוWebViewמבטלת כל ניווט פעיל.תמיכה בסכימת URI: יש תמיכה בסכימות URI סטנדרטיות (כמו
https:ו-http:) ובסכימות URI בהתאמה אישית. אין תמיכה בסכימהjavascript:.מגבלת הגודל של כתובת URL: האורך המקסימלי של מחרוזת כתובת URL שנתמך הוא 2MB.
בדיקת תכונות: כדי לשמור על תאימות בין גרסאות שונות של WebView APK, תמיד כדאי לבדוק את הזמינות של התכונות באמצעות
WebViewFeature.isFeatureSupportedלפני שמפעילים את ה-API.
התחלת ניווט ומעקב אחר מחזור החיים
כדי להגדיר ניווט ולעקוב אחרי מחזור החיים שלו:
- כדי לקבל קריאות חוזרות מובנות של מחזור החיים, צריך לרשום הטמעה של
NavigationListenerבאמצעותWebViewCompat.addNavigationListenerבמהלך ההגדרה שלWebView. כדי למנוע דליפות זיכרון והפעלות כפולות של קריאות חוזרות (callback), צריך לרשום את מאזין האירועים פעם אחת (ולא בכל קריאה לניווט). - יוצרים מופע של
NavigationParametersבאמצעותNavigationParameters.Builderכדי לציין התנהגויות אופציונליות, כמו החלפה של היסטוריה או כותרות HTTP בהתאמה אישית. - מתקשרים אל
WebViewCompat.navigateומעבירים את המופעWebView, את כתובת היעד ואת הפרמטרים.
WebViewCompat.navigate מחזירה אובייקט Navigation שמזהה באופן ייחודי את הבקשה. בפונקציות הקריאה החוזרות (callback) של 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 API מספק מנגנונים נפרדים לטיפול בשגיאות בהגדרות ובכשלים בניווט בזמן ריצה:
חריגים של ארגומנטים לא תקינים
העברת ארגומנטים לא תקינים מפעילה IllegalArgumentException סינכרוני.
הסיבות הנפוצות לכך הן:
- העברת הערך
nullלפרמטרים נדרשים שלא יכולים להיות null (webView, urlאוparams). - הזנת סכמת URL שלא נתמכת, כמו
javascript:. - העברת מפתחות או ערכים של כותרות HTTP שאינם בפורמט תקין או שלא עומדים בדרישות של מפרט RFC 2616.
שגיאות בתהליך הניווט
אם מתרחשת שגיאה במהלך בקשה לאחזור מהרשת או טעינת הדף (למשל קוד סטטוס HTTP 404, שגיאת פענוח DNS או שגיאת SSL), הפונקציה WebViewCompat.navigate עדיין מחזירה אובייקט Navigation תקין.
בסיום הניווט, בודקים את השיטות הבאות בקריאה החוזרת (callback) של 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. כך אפשר לוודא שניהול ההיסטוריה אחיד ושהכותרות נשמרות תמיד כחלק מהמצב השמור.תמיד בודקים את התמיכה בתכונה: לפני שמפעילים את ה-API, צריך לוודא שיש תמיכה בזמן ריצה באמצעות
WebViewFeature.isFeatureSupportedכדי להגן על עצמכם מפני גרסאות ישנות יותר של WebView.התאמה בין מופעים של ניווט: אפשר להשתמש באובייקט
Navigationשמוחזר כדי להבחין בין ניווטים מקבילים או לסנן קריאות חוזרות (callback) כשמנהלים כמה מופעים שלWebView.רישום של listener פעם אחת במהלך האתחול: מכיוון ש-
WebViewCompat.addNavigationListenerמוסיף listener במקום להחליף listener קיים, צריך לרשום אתNavigationListenerפעם אחת במהלך ההגדרה שלWebViewכדי למנוע דליפות זיכרון והפעלות כפולות של callback בניווטים הבאים.מעקב אחרי גודל מצב השמירה: כשמעבירים מטען ייעודי (payload) גדול של כותרות, כדאי להשתמש ב-
WebViewCompat.saveStateעם גבולות גודל מפורשים כדי להימנע משמירה של נתוני מצב עודפים.
מקורות מידע נוספים
מידע נוסף על יכולות מוטמעות של אתרים ועל אופטימיזציה של ביצועים מופיע במדריכים הבאים:
- איך מפשטים את ההטמעה של WebView באמצעות Jetpack Webkit
- טעינה מראש ב-WebView
- אופטימיזציה של הפעלת WebView
- טיפול בסיום של תהליך העיבוד של WebView