פיתוח פשוט יותר של WebView עם Jetpack Webkit

במדריך הזה מוסבר על היתרונות של ספריית Jetpack Webkit, איך היא פועלת ואיך אפשר להטמיע אותה בפרויקטים.

סקירה כללית

רכיבי WebView הם חלק חיוני בפיתוח ל-Android, אבל לפעמים קשה לנהל אותם בגלל חוסר עקביות בתכונות בגרסאות שונות של Android OS. כל גרסה של Android OS מספקת קבוצה קבועה של ממשקי API של WebView. מכיוון ש-Android מופץ בקצב איטי יותר מ-WebView, יכול להיות ש-Android APIs לא יכללו את כל התכונות הזמינות של WebView. התוצאה היא השקה איטית יותר של תכונות ועלויות בדיקה גבוהות יותר.

‫Jetpack Webkit פותר את הבעיות האלה באמצעות פעולה כשכבת תאימות ושימוש ב-APK העדכני של WebView במכשיר של המשתמש. היא כוללת גם ממשקי API חדשים ומודרניים שזמינים באופן בלעדי בספרייה הזו.

למה כדאי להשתמש ב-Jetpack Webkit?

בנוסף לתאימות בין גרסאות, Jetpack Webkit מציע גם ממשקי API חדשים ומודרניים שיכולים לפשט את הפיתוח ולשפר את הפונקציונליות של האפליקציה:

  • הפעלת אימות מודרני: WebView יכול לטפל בצורה חלקה בתקני אימות מודרניים באינטרנט כמו WebAuthn, וכך לאפשר כניסה באמצעות מפתח גישה. ספריית androidx.webkit מאפשרת לכם שליטה מלאה בשילוב הזה באמצעות השיטה WebSettingsCompat.setWebAuthenticationSupport, שבה תוכלו להשתמש כדי להגדיר את רמת התמיכה שהאפליקציה שלכם דורשת.

  • שיפור הביצועים: אפשר לשפר את הביצועים של WebView באמצעות ממשקי API כמו setBackForwardCacheEnabled, או לקצר את זמן האחזור של הניווט באמצעות ממשקי API של טעינה מראש כמו prefetchUrlAsync ו-prerenderUrlAsync. מידע נוסף זמין במאמר בנושא טעינה מראש ב-WebView.

  • שיפור היציבות: שחזור תהליכי עיבוד שנתקעו או לא מגיבים בלי לגרום לקריסה. מידע נוסף זמין במאמר WebViewRenderProcess#terminate.

  • שליטה מפורטת בנתוני הגלישה: כדי למחוק נתוני גלישה שמאוחסנים על ידי WebView למקורות ספציפיים, משתמשים במחלקה WebStorageCompat.

  • מספק ניווט משופר בדפים: מחליפים את WebView.loadUrl ב-WebViewCompat.navigate כדי לקבל שליטה מדויקת בניווט, החלפה של רשומות בהיסטוריה, תמיכה בכותרת של מצב שמור ומעקב מתואם אחר מחזור החיים באמצעות NavigationListener. מידע נוסף זמין במאמר בנושא ניווט משופר בדף באמצעות WebViewCompat.navigate.

  • אופטימיזציה של ניהול המצב: הגדרת מגבלות מקסימליות של בייטים במהלך סריאליזציה של המצב כדי להגן מפני TransactionTooLargeException מידע נוסף מופיע במאמר ניהול יעיל של מצב WebView.

הסבר על הרכיבים

כדי להשתמש ב-Jetpack Webkit בצורה יעילה, צריך להבין את הקשר בין הרכיבים הבאים:

  • Android System WebView: זהו מנוע העיבוד שמבוסס על Chromium ו-Google מעדכנת אותו באופן קבוע דרך חנות Google Play, באותו קצב כמו Chrome. היא מכילה את התכונות העדכניות ביותר ומספקת את קוד ההטמעה הבסיסי לכל ממשקי ה-WebView API.

  • Framework APIs (android.webkit): אלה ממשקי ה-API שקבועים לגרסה ספציפית של מערכת ההפעלה Android. לדוגמה, אפליקציה ב-Android 10 יכולה לגשת רק לממשקי ה-API שהיו זמינים כשהגרסה הזו הושקה. לכן, הוא לא יכול להשתמש בתכונות חדשות שנוספו ל-APK של WebView בעדכונים חדשים יותר. לדוגמה, כדי להשתמש ב-WebView#getWebViewRenderProcess() כדי לטפל ב-renderer שלא מגיב, אפשר להפעיל את הפונקציה הזו רק ב-Android מגרסה 10 ואילך.

  • Jetpack Webkit Library (androidx.webkit): זוהי ספרייה קטנה שכלולה באפליקציה. הספרייה הזו משמשת כגשר שמבצע קריאה ל-APK של WebView, במקום לבצע קריאה לממשקי ה-API שמוגדרים בפלטפורמת Android, שיש לה גרסת מערכת הפעלה קבועה. כך, גם אם אפליקציה מותקנת במכשיר עם גרסה ישנה יותר של מערכת הפעלה כמו Android 10, האפליקציה יכולה להשתמש בתכונות האחרונות של WebView. לדוגמה, הפונקציה WebViewCompat.getWebViewRenderProcess() פועלת באופן דומה ל-Framework API, אבל אפשר להפעיל אותה גם בכל גרסאות מערכת ההפעלה לפני Android 10.

אם API זמין גם ב-framework וגם ב-Jetpack Webkit, מומלץ לבחור בגרסת Jetpack Webkit. כך אפשר להבטיח התנהגות עקבית ותאימות במגוון רחב של מכשירים.

אינטראקציה בין Jetpack Webkit לבין APK

ממשקי ה-API ב-Jetpack Webkit מיושמים בשני חלקים:

  • Static Jetpack Webkit: ספריית ה-Jetpack Webkit הסטטית מכילה חלק קטן מהקוד שאחראי להטמעה של ה-API.

  • WebView APK: קובץ ה-APK של WebView מכיל את רוב הקוד.

האפליקציה שלך שולחת קריאה ל-Jetpack Webkit API, ששולח קריאה ל-WebView APK.

אתם יכולים לשלוט בגרסת Jetpack Webkit באפליקציה, אבל לא יכולים לשלוט בעדכוני ה-APK של WebView במכשירים של המשתמשים. בדרך כלל, לרוב המשתמשים יש גרסאות עדכניות של קובץ ה-APK של WebView, אבל האפליקציה עדיין צריכה להיזהר ולא לקרוא לממשקי API שגרסה מסוימת של קובץ ה-APK של WebView לא תומכת בהם.

בנוסף, Jetpack Webkit מפשט את התהליך ומונע את הצורך לבדוק את גרסאות WebView באופן ידני. כדי לדעת אם תכונה מסוימת זמינה, בודקים את הקבוע שלה. לדוגמה, WebViewFeature.WEB_AUTHENTICATION.

איך שיטות הבידינג פועלות יחד

‫Jetpack Webkit מגשר על הפער בין Framework API סטטי לבין WebView APK שמתעדכן לעיתים קרובות. כשמשתמשים ב-Jetpack Webkit API עם התבנית feature-detection, הספרייה מבצעת בדיקה כדי לראות אם התכונה נתמכת על ידי ה-APK של WebView שמותקן במכשיר של המשתמש. היתרון הוא שלא צריך לבדוק את הגרסה של Android OS (המסגרת).

אם קובץ ה-APK של WebView הוא גרסה עדכנית מספיק, הספרייה מפעילה את התכונה. אם לא, הוא מדווח שהתכונה לא זמינה, וכך מונע קריסה של האפליקציה ומאפשר לכם לטפל במצב בצורה חלקה.

השוואה בין Jetpack Webkit לבין ממשקי API של Framework

בקטע הזה מוצגות השוואות בין שיטות הטמעה עם ספריית Jetpack Webkit ובלעדיה:

הפעלת אימות מודרני (WebAuthn)

ללא Jetpack Webkit

אי אפשר לעשות זאת באמצעות ממשקי API של מסגרת.

עם Jetpack Webkit

הכלי משתמש ב-WebViewFeature.WEB_AUTHENTICATION לבדיקות תאימות.

if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_AUTHENTICATION)) {
  WebSettingsCompat.setWebAuthenticationSupport(
      webView.settings,
      WebSettingsCompat.WEB_AUTHENTICATION_SUPPORT_FOR_APP
  )
}

מחיקת נתונים של מקור (אחסון ספציפי לאתר)

ללא Jetpack WebKit

אין API ישיר לניקוי נתונים של מקור ספציפי. לרוב צריך למחוק את כל הנתונים.

עם Jetpack WebKit

משתמש בממשקי API של תאימות למחיקה מדויקת של נתונים. אפשר להשתמש באחת מהאפשרויות הבאות:

WebStorageCompat.getInstance().deleteBrowsingData()

או

WebStorageCompat.getInstance().deleteBrowsingDataForSite()

קבלת גרסת WebView

ללא Jetpack WebKit

נעשה שימוש במחלקת המסגרת הרגילה.

val webViewPackage = WebView.getCurrentWebViewPackage()

עם Jetpack WebKit

השליפה מתבצעת באמצעות שכבת התאימות כדי להגביר את האבטחה.

val webViewPackage = WebViewCompat.getCurrentWebViewPackage()

טיפול במנוע רינדור שלא מגיב (לקוח מנוע הרינדור)

ללא Jetpack WebKit

השיטה הזו מבוססת על מסגרת סטנדרטית.

webView.setWebViewRenderProcessClient(myClient)

עם Jetpack WebKit

משתמש ב-WebViewCompat ובבדיקת תכונות כדי להגדיר את הלקוח.

if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE)) {
  WebViewCompat.setWebViewRenderProcessClient(webView, myClient)
}

הנחיות להטמעה של אסטרטגיות לשחזור אחרי קריסה זמינות במאמר טיפול בסגירה של WebView. פרטים על ה-API מופיעים בandroidx.webkitמאמרי העזרה.

ניהול של מצב שמור וגודל העסקה

ממשק ה-API‏ WebViewCompat.saveState מאפשר לאכוף מגבלות על מספר הבייטים ולצמצם את ההיסטוריה קדימה במהלך הסריאליזציה, וכך למנוע קריסות של TransactionTooLargeException תוך שמירה על היסטוריית הניווט החיונית.

ללא Jetpack WebKit

השיטה הזו משתמשת בשיטת המסגרת הרגילה, שמבצעת סריאליזציה של כל ערימת הניווט ללא מגבלות גודל, ויכולה להפעיל את TransactionTooLargeException אם מטען הייעודי חורג ממגבלת העסקאות של Android, שהיא 1MB.

webView.saveState(outState)

עם Jetpack WebKit

השימוש ב-WebViewCompat מאפשר לאכוף מגבלת בייט מקסימלית או להסיר רשומות של ניווט קדימה, וכך להגן מפני חריגה מקיבולת העסקאות.

WebViewCompat.saveState(webView, outState, maxSizeBytes)

מידע נוסף מופיע במאמר ניהול יעיל של מצב WebView.

כדי לנווט בדפי אינטרנט עם תמיכה בהחלפת רשומות היסטוריה, תמיכה בכותרת של מצב שמור וקריאות חוזרות (callback) של מחזור חיים מתואם, צריך להשתמש ב-WebViewCompat.navigate במקום ב-WebView.loadUrl.

ללא Jetpack WebKit

השיטה משתמשת ב-WebView.loadUrl, שלא תומכת בהחלפה של רשומת היסטוריה או במעקב אחרי קריאות חוזרות במחזור חיים עם קורלציה.

webView.loadUrl("https://www.example.com")

עם Jetpack WebKit

משתמשים ב-WebViewCompat.navigate עם NavigationParameters כדי להחליף רשומות בהיסטוריה, לשמור כותרות מותאמות אישית במצב השמור ולעקוב אחרי מצבי ניווט.

if (WebViewFeature.isFeatureSupported(WebViewFeature.WEBVIEW_NAVIGATE_EXPERIMENTAL_V1)) {
  val params = NavigationParameters.Builder()
      .setShouldReplaceCurrentEntry(true)
      .build()
  val navigation = WebViewCompat.navigate(webView, "https://www.example.com", params)
} else {
  webView.loadUrl("https://www.example.com")
}

מידע נוסף על מעקב אחר ניווט והגדרת פרמטרים זמין במאמר ניווט משופר בדפים באמצעות WebViewCompat.navigate.

שילוב Jetpack Webkit בקוד

השימוש ב-Jetpack Webkit משפר את היכולות של מחלקת WebView הרגילה, אבל הוא לא מחליף לגמרי את מחלקת WebView המקורית.

אפשר להמשיך להשתמש בכיתה ב-android.webkit.WebView. אפשר להוסיף אותו לפריסות ה-XML ולקבל הפניה למופע בקוד. כדי לגשת לתכונות של מסגרת רגילה, עדיין אפשר לקרוא לשיטות ישירות במופע WebView או באובייקט ההגדרות שלו.

כדי לגשת לתכונות מתקדמות, משתמשים בשיטות העזר הסטטיות שסופקו על ידי Jetpack Webkit, כמו WebViewCompat ו-WebSettingsCompat. מעבירים את מופע ה-WebView הקיים לשיטות האלה.

Kotlin

import android.webkit.WebView
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeature

// You still get your WebView instance the standard way.
val webView: WebView = findViewById(R.id.my_webview)

// To enable a modern feature, you pass that instance to a Jetpack Webkit helper.
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
    WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON)
}

Java

import android.webkit.WebView;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;

// You still get your WebView instance the standard way.
WebView webView = findViewById(R.id.my_webview);

// To enable a modern feature, you pass that instance to a Jetpack Webkit helper.
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
    WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON);
}

הטמעה של Jetpack Webkit

כדי להטמיע את Jetpack Webkit, פועלים לפי השלבים הבאים.

שלב 1: מוסיפים את התלות

בקובץ build.gradle.kts או build.gradle של המודול, כוללים את יחסי התלות הבאים כדי להוסיף את Jetpack Webkit:

מגניב

dependencies {
    implementation "androidx.webkit:webkit:1.16.0"
}

Kotlin

dependencies {
    implementation("androidx.webkit:webkit:1.16.0")
}

ספריית Jetpack Webkit מכילה עטיפות דקות, כך שההשפעה על גודל האפליקציה היא מינימלית.

שלב 2: הטמעת התבנית של זיהוי התכונות

כדי למנוע קריסות כשמפעילים ממשקי API לא זמינים, צריך להשתמש בבדיקות תכונות. מומלץ להקיף כל קריאה ל-API בבדיקת תכונה, ואולי לשקול לוגיקה של חזרה למצב קודם למקרה שה-API לא זמין.

מומלץ להשתמש ב-WebView API מודרני לפי הדפוס הבא:

Kotlin

import android.webkit.WebView
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeature

val webView: WebView = findViewById(R.id.my_webview)

// Before you use a modern API, first check if it is supported.
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
    // If the check passes, it is safe to call the API.
    WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON)
} else {
    // Optionally, provide a fallback for older WebView versions.
}

Java

import android.webkit.WebView;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.WebViewFeature;

WebView webView = findViewById(R.id.my_webview);

// Before you use a modern API, first check if it is supported.
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
    // If the check passes, it is safe to call the API.
    WebSettingsCompat.setForceDark(webView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
} else {
    // Optionally, provide a fallback for older WebView versions.
}

הדפוס הזה עוזר לוודא שהאפליקציה חזקה. בגלל שבדיקת התכונה מופעלת קודם, האפליקציה לא קורסת אם התכונה לא זמינה. התקורה של בדיקת WebViewFeature#isFeatureSupported זניחה.