WebViewCompat.navigate הוא חלופה משופרת ל-WebView.loadUrl שמאפשרת שליטה מדויקת בטעינת דפי אינטרנט, בניהול ההיסטוריה ובמעקב אחר מחזור החיים של הניווט ב-WebView.
בעבר, לניווטים בדפים באמצעות loadUrl היו מגבלות משמעותיות:
- אי אפשר להחליף רשומה בהיסטוריה: אי אפשר להחליף את הרשומה הנוכחית בהיסטוריה, ולכן אי אפשר לנווט לדף חדש בלי להוסיף רשומה למחסנית הדפים הקודמים.
- קודים להתקשרות חזרה שאינם תלויים זה בזה: לא היה מנגנון ישיר שיכול לקשר בין שיחה ספציפית של
loadUrlלבין אירועים של קודים להתקשרות חזרה שמתרחשים לאחר מכן ב-WebViewClient. - כותרות נוספות לא נשמרו: כותרות מותאמות אישית שהועברו אל
loadUrlלא נשמרו כחלק מהמצב שלWebView, ולכן הן אבדו כששוחזר המצב.
WebViewCompat.navigate API פותר את הבעיות האלה באמצעות התכונות הבאות:
- החלפת רשומה בהיסטוריית הניווט: מאפשרת להחליף את הדף הנוכחי במחסנית ההיסטוריה של
WebView. - מעקב אחר קריאות חוזרות עם קורלציה: מחזיר אובייקט
Navigationשמשמש כמזהה ייחודי בכל השלבים של מחזור החיים של הניווט. - תמיכה בכותרות של מצב שמור: כותרות נוספות נשמרות באופן מהימן בחבילת המצב
WebView, כך שאפשר לעשות בהן שימוש חוזר כשמשחזרים את המצב.
יכולות ומגבלות עיקריות
לפני שמאמצים את WebViewCompat.navigate, כדאי להביא בחשבון את כללי ההפעלה והמגבלות הבאים:
בטיחות ה-thread: צריך להפעיל את
WebViewCompat.navigateב-thread של ממשק המשתמש (הראשי).ביטול ועדיפות: אי אפשר לבטל באופן מפורש ניווטים שמתבצעים. עם זאת, התחלת שיחה חדשה ב-
navigateבאותוWebViewמבטלת כל ניווט פעיל.תמיכה בסכימת URI: יש תמיכה בסכימות URI סטנדרטיות (כמו
https:ו-http:) ובסכימות URI בהתאמה אישית. אין תמיכה בסכימהjavascript:.מגבלת הגודל של כתובת URL: האורך המקסימלי של מחרוזת כתובת URL שנתמך הוא 2MB.
בדיקת תכונות: כדי לשמור על תאימות בין גרסאות שונות של WebView APK, תמיד כדאי לבדוק את הזמינות של התכונות באמצעות
WebViewFeature.isFeatureSupportedלפני שמפעילים את ה-API.
התחלת ניווט ומעקב אחר מחזור החיים
כדי להגדיר ניווט ולעקוב אחרי מחזור החיים שלו:
- כדי לקבל קריאות חוזרות מובנות של מחזור החיים, צריך לרשום הטמעה של
NavigationListenerבאמצעותWebViewCompat.addNavigationListenerבמהלך ההגדרה שלWebView. כדי למנוע דליפות זיכרון והפעלות כפולות של קריאות חוזרות (callback), צריך לרשום את ה-listener פעם אחת (ולא בכל קריאה לניווט). - יוצרים מופע של
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);
}
}
העברת מצב האפליקציה באמצעות כותרות HTTP
לפעמים אפליקציות אינטרנט צריכות הקשר מהאפליקציה המארחת ל-Android כדי לתאם את הלוגיקה של ה-Backend או להתאים אישית את התוכן באינטרנט. הוספה של פרמטרים של שאילתה לכתובת ה-URL כדי להעביר את המידע הזה עלולה להעמיס על כתובות ה-URL, להפריע לשמירה במטמון ולחשוף את המצב הפנימי של האפליקציה.
במקום זאת, מומלץ להעביר את הקשר האפליקציה באמצעות כותרות HTTP מותאמות אישית. באמצעות התגים
WebViewCompat.navigate ו-NavigationParameters, אפשר לשלוח את הנתונים האלה לשרת בצורה מאובטחת. בנוסף, WebView שומר על הכותרות האלה במהלך שחזור המצב, וכך מוודא שהתוכן באינטרנט נשאר עקבי גם אחרי שינויים בהגדרות. חשוב לדעת שההתמדה הזו חלה רק כשמשתמשים ב-WebViewCompat.navigate. אם משתמשים ב-WebView.loadUrl, כותרות מותאמות אישית לא נשמרות בחבילת המצב WebView ואובדות בזמן השחזור.
תרחישים נפוצים לדוגמה
תרחישים נפוצים להעברת ההקשר של האפליקציה המארחת כוללים את הדוגמאות הבאות:
- גרסת האפליקציה (
X-App-Version): העברת גרסת הפרסום של אפליקציית המארח (למשלBuildConfig.VERSION_NAME) עוזרת לשרת הקצה העורפי לאמת את התאימות של גשר JavaScript מקורי, להגביל תכונות או להציג למשתמשים בקשות לעדכון אפליקציות ישנות יותר. - פלטפורמת הלקוח (
X-Client-Platform): זיהוי מפורש של סביבת המארח כ-Android מאפשר לשרת לספק ממשק משתמש מותאם לפלטפורמה או קישורים לחנות ללא הסתמכות על ניתוח מחרוזתUser-Agent.
דוגמה להטמעה
בדוגמה הבאה אפשר לראות איך מעבירים את גרסת האפליקציה ואת פלטפורמת הלקוח לשרת אינטרנט:
Kotlin
// Attach host app metadata so the server can verify compatibility and tailor content
val params = NavigationParameters.Builder()
.addAdditionalHeaders(
mapOf(
"X-App-Version" to BuildConfig.VERSION_NAME,
"X-Client-Platform" to "Android"
)
)
.build()
// Use navigate instead of loadUrl to retain custom headers across state restoration
WebViewCompat.navigate(webView, "https://www.example.com", params)
Java
// Attach host app metadata so the server can verify compatibility and tailor content
Map<String, String> headers = new HashMap<>();
headers.put("X-App-Version", BuildConfig.VERSION_NAME);
headers.put("X-Client-Platform", "Android");
NavigationParameters params = new NavigationParameters.Builder()
.addAdditionalHeaders(headers)
.build();
// Use navigate instead of loadUrl to retain custom headers across state restoration
WebViewCompat.navigate(webView, "https://www.example.com", params);
מצבי כשל וטיפול בשגיאות
WebViewCompat.navigate API מספק מנגנונים נפרדים לטיפול בשגיאות בהגדרות ובכשלים בניווט בזמן ריצה:
חריגים של ארגומנטים לא תקינים
העברת ארגומנטים לא תקינים מפעילה IllegalArgumentException סינכרוני.
הסיבות הנפוצות לכך הן:
- העברת הערך
nullלפרמטרים נדרשים שלא יכולים להיות null (webView, urlאוparams). - הזנת סכמת URL שלא נתמכת, כמו
javascript:. - העברת מפתחות או ערכים של כותרות HTTP שאינם בפורמט תקין או שלא עומדים בדרישות של מפרט RFC 2616.
שגיאות בתהליך הניווט
אם מתרחשת שגיאה במהלך בקשה לאחזור מהרשת או טעינת הדף (למשל קוד סטטוס HTTP 404, שגיאת פענוח DNS או שגיאת SSL), הפונקציה WebViewCompat.navigate עדיין מחזירה אובייקט Navigation תקין.
בסיום הניווט, בודקים את השיטות הבאות בקריאה החוזרת (callback) של NavigationonNavigationCompleted כדי לאבחן את הכשל:
-
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.רישום מאזין פעם אחת במהלך האתחול: מכיוון שהפונקציה
WebViewCompat.addNavigationListenerמוסיפה מאזין במקום להחליף מאזין קיים, צריך לרשום אתNavigationListenerפעם אחת במהלך ההגדרה שלWebViewכדי למנוע דליפות זיכרון והפעלות כפולות של קריאות חוזרות (callback) בניווטים הבאים.מעקב אחרי גודל מצב השמירה: כשמעבירים מטען ייעודי (payload) גדול של כותרות, כדאי להשתמש ב-
WebViewCompat.saveStateעם גבולות גודל מפורשים כדי להימנע משמירה של נתוני מצב עודפים.
מקורות מידע נוספים
מידע נוסף על יכולות מוטמעות של אתרים ועל אופטימיזציה של ביצועים מופיע במדריכים הבאים:
- איך מפשטים את ההטמעה של WebView באמצעות Jetpack Webkit
- טעינה מראש ב-WebView
- אופטימיזציה של הפעלת WebView
- טיפול בסיום של תהליך העיבוד של WebView