משתמשים ב-WebView
כדי לספק אפליקציית אינטרנט או דף אינטרנט כחלק מאפליקציית לקוח. הכיתה WebView
היא תוסף לכיתה View
של Android, שמאפשרת להציג דפי אינטרנט כחלק מתצוגת הפעילות. הוא לא כולל את התכונות של דפדפן אינטרנט מפותח, כמו אמצעי ניווט או שורת כתובות. כברירת מחדל, האפשרות WebView
מציגה דף אינטרנט.
WebView
יכול לעזור לכם לספק באפליקציה מידע שעשוי להיות צורך לעדכן, כמו הסכם משתמש קצה או מדריך למשתמש. באפליקציה ל-Android, אפשר ליצור Activity
שמכיל WebView
, ואז להשתמש בו כדי להציג את המסמך שמתארח באינטרנט.
WebView
יכולה לעזור גם כשהאפליקציה מספקת למשתמש נתונים שדורשים חיבור לאינטרנט כדי לאחזר אותם, כמו אימייל. במקרה כזה, יכול להיות שיהיה קל יותר ליצור WebView
באפליקציה ל-Android שמוצג בו דף אינטרנט עם כל נתוני המשתמש, במקום לבצע בקשת רשת, לנתח את הנתונים ולבצע להם רינדור בפריסה של Android. במקום זאת, תוכלו לעצב דף אינטרנט שמותאם למכשירי Android, ולאחר מכן להטמיע WebView
באפליקציה ל-Android שיטמיע את דף האינטרנט.
במסמך הזה נסביר איך להתחיל להשתמש ב-WebView
, איך לקשר את JavaScript מדף האינטרנט לקוד בצד הלקוח באפליקציה ל-Android, איך לטפל בניווט בדף ואיך לנהל חלונות כשמשתמשים ב-WebView
.
עבודה עם WebView בגרסאות קודמות של Android
כדי להשתמש בבטחה ביכולות WebView
עדכניות יותר במכשיר שבו האפליקציה פועלת, מוסיפים את הספרייה AndroidX Webkit. זוהי ספרייה סטטית שאפשר להוסיף לאפליקציה כדי להשתמש בממשקי API של android.webkit
שלא זמינים לגרסאות קודמות של הפלטפורמה.
מוסיפים אותו לקובץ build.gradle
באופן הבא:
Kotlin
dependencies { implementation("androidx.webkit:webkit:1.8.0") }
מגניב
dependencies { implementation ("androidx.webkit:webkit:1.8.0") }
לפרטים נוספים, קראו את הדוגמה WebView
ב-GitHub.
הוספת WebView לאפליקציה
כדי להוסיף WebView
לאפליקציה, אפשר לכלול את הרכיב <WebView>
בפריסת הפעילות או להגדיר את כל החלון של Activity
כ-WebView
ב-onCreate()
.
הוספת רכיב WebView בפריסה של הפעילות
כדי להוסיף WebView
לאפליקציה בתצוגה, מוסיפים את הקוד הבא לקובץ ה-XML של הפריסה של הפעילות:
<WebView android:id="@+id/webview" android:layout_width="match_parent" android:layout_height="match_parent" />
כדי לטעון דף אינטרנט ב-WebView
, משתמשים ב-loadUrl()
, כפי שמתואר בדוגמה הבאה:
Kotlin
val myWebView: WebView = findViewById(R.id.webview) myWebView.loadUrl("http://www.example.com")
Java
WebView myWebView = (WebView) findViewById(R.id.webview); myWebView.loadUrl("http://www.example.com");
הוספת WebView ב-onCreate()
כדי להוסיף WebView
לאפליקציה במקום זאת בשיטה onCreate()
של פעילות, צריך להשתמש בלוגיקה דומה לזו:
Kotlin
val myWebView = WebView(activityContext) setContentView(myWebView)
Java
WebView myWebView = new WebView(activityContext); setContentView(myWebView);
לאחר מכן טוענים את הדף:
Kotlin
myWebView.loadUrl("http://www.example.com")
Java
myWebView.loadUrl("https://www.example.com");
לחלופין, אפשר לטעון את כתובת ה-URL ממחרוץ HTML:
Kotlin
// Create an unencoded HTML string, then convert the unencoded HTML string into // bytes. Encode it with base64 and load the data. val unencodedHtml = "<html><body>'%23' is the percent code for ‘#‘ </body></html>"; val encodedHtml = Base64.encodeToString(unencodedHtml.toByteArray(), Base64.NO_PADDING) myWebView.loadData(encodedHtml, "text/html", "base64")
Java
// Create an unencoded HTML string, then convert the unencoded HTML string into // bytes. Encode it with base64 and load the data. String unencodedHtml = "<html><body>'%23' is the percent code for ‘#‘ </body></html>"; String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(), Base64.NO_PADDING); myWebView.loadData(encodedHtml, "text/html", "base64");
לאפליקציה צריכה להיות גישה לאינטרנט. כדי לקבל גישה לאינטרנט, מבקשים את ההרשאה INTERNET
בקובץ המניפסט, כמו בדוגמה הבאה:
<manifest ... > <uses-permission android:name="android.permission.INTERNET" /> ... </manifest>
אפשר להתאים אישית את WebView
באמצעות אחת מהפעולות הבאות:
- הפעלת התמיכה במסך מלא באמצעות
WebChromeClient
. המחלקה הזו מופעלת גם כש-WebView
זקוקה להרשאה כדי לשנות את ממשק המשתמש של האפליקציה המארחת, למשל יצירה או סגירה של חלונות או שליחת תיבות דו-שיח של JavaScript למשתמש. מידע נוסף על ניפוי באגים בהקשר הזה זמין במאמר ניפוי באגים באפליקציות אינטרנט. - טיפול באירועים שמשפיעים על רינדור תוכן, כמו שגיאות בשליחת טפסים או בניווט באמצעות
WebViewClient
. אפשר גם להשתמש בתת-הסוג הזה כדי ליירט את טעינת כתובות ה-URL. - הפעלת JavaScript על ידי שינוי של
WebSettings
. - שימוש ב-JavaScript כדי לגשת לאובייקטים של Android framework שהחדרתם ל-
WebView
.
שימוש ב-JavaScript ב-WebView
אם דף האינטרנט שרוצים לטעון ב-WebView
משתמש ב-JavaScript, צריך להפעיל את JavaScript ב-WebView
. אחרי שמפעילים את JavaScript, אפשר ליצור ממשקים בין קוד האפליקציה לקוד JavaScript.
הפוך JavaScript לפעיל
כברירת מחדל, JavaScript מושבת ב-WebView
. אפשר להפעיל אותו דרך ה-WebSettings
המצורף ל-WebView
. מאחזרים את WebSettings
באמצעות getSettings()
, ואז מפעילים את JavaScript באמצעות setJavaScriptEnabled()
.
דוגמה:
Kotlin
val myWebView: WebView = findViewById(R.id.webview) myWebView.settings.javaScriptEnabled = true
Java
WebView myWebView = (WebView) findViewById(R.id.webview); WebSettings webSettings = myWebView.getSettings(); webSettings.setJavaScriptEnabled(true);
WebSettings
מספק גישה למגוון הגדרות נוספות שעשויות להיות שימושיות. לדוגמה, אם אתם מפתחים אפליקציית אינטרנט שמיועדת ספציפית ל-WebView
באפליקציה שלכם ל-Android, אתם יכולים להגדיר מחרוזת של סוכן משתמש בהתאמה אישית באמצעות setUserAgentString()
, ואז להריץ שאילתה על סוכן המשתמש המותאם אישית בדף האינטרנט כדי לוודא שהלקוח שמבקש את דף האינטרנט הוא האפליקציה שלכם ל-Android.
קישור קוד JavaScript לקוד Android
כשאתם מפתחים אפליקציית אינטרנט שמיועדת במיוחד ל-WebView
באפליקציית Android שלכם, אתם יכולים ליצור ממשקים בין קוד JavaScript לבין קוד Android בצד הלקוח. לדוגמה, קוד JavaScript יכול להפעיל שיטה בקוד Android כדי להציג Dialog
, במקום להשתמש בפונקציה alert()
של JavaScript.
כדי לקשר ממשק חדש בין קוד JavaScript לקוד Android, צריך לבצע קריאה ל-addJavascriptInterface()
, ולהעביר לה מופע של הכיתה שרוצים לקשר ל-JavaScript ושם של ממשק ש-JavaScript יכול לקרוא אליו כדי לגשת לכיתה.
לדוגמה, אפשר לכלול את הכיתה הבאה באפליקציה ל-Android:
Kotlin
/** Instantiate the interface and set the context. */ class WebAppInterface(private val mContext: Context) { /** Show a toast from the web page. */ @JavascriptInterface fun showToast(toast: String) { Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show() } }
Java
public class WebAppInterface { Context mContext; /** Instantiate the interface and set the context. */ WebAppInterface(Context c) { mContext = c; } /** Show a toast from the web page. */ @JavascriptInterface public void showToast(String toast) { Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show(); } }
בדוגמה הזו, הכיתה WebAppInterface
מאפשרת לדף האינטרנט ליצור הודעת Toast
באמצעות השיטה showToast()
.
אפשר לקשר את הכיתה הזו ל-JavaScript שפועל ב-WebView
באמצעות addJavascriptInterface()
, כפי שמוצג בדוגמה הבאה:
Kotlin
val webView: WebView = findViewById(R.id.webview) webView.addJavascriptInterface(WebAppInterface(this), "Android")
Java
WebView webView = (WebView) findViewById(R.id.webview); webView.addJavascriptInterface(new WebAppInterface(this), "Android");
הפעולה הזו יוצרת ממשק שנקרא Android
ל-JavaScript שפועל ב-WebView
. בשלב הזה, לאפליקציית האינטרנט יש גישה לכיתה WebAppInterface
. לדוגמה, הנה קוד HTML ו-JavaScript שיוצרים הודעת הודעה נשלחת באמצעות הממשק החדש כשהמשתמש מקיש על לחצן:
<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" /> <script type="text/javascript"> function showAndroidToast(toast) { Android.showToast(toast); } </script>
אין צורך לאתחל את הממשק של Android
מ-JavaScript. ה-WebView
יגרום לכך שהיא תהיה זמינה באופן אוטומטי בדף האינטרנט שלכם. לכן, כשמשתמש מקשיב על הלחצן, הפונקציה showAndroidToast()
משתמשת בממשק Android
כדי לקרוא ל-method WebAppInterface.showToast()
.
טיפול בניווט בדפים
כשהמשתמש מקיש על קישור מדף אינטרנט ב-WebView
, כברירת מחדל, מערכת Android מפעילה אפליקציה שמטפלת בכתובות URL. בדרך כלל דפדפן האינטרנט שמוגדר כברירת מחדל נפתח וטוען את כתובת היעד. עם זאת, אפשר לשנות את ההתנהגות הזו ב-WebView
כך שהקישורים יפתחו ב-WebView
. לאחר מכן תוכלו לאפשר למשתמש לנווט אחורה וקדימה בהיסטוריית דפי האינטרנט שלו, שמתוחזקת על ידי WebView
.
כדי לפתוח קישורים שהמשתמש לחץ עליהם, צריך לספק WebViewClient
ל-WebView
באמצעות setWebViewClient()
.
כל הקישורים שהמשתמש מקייש עליהם נטענים ב-WebView
. אם אתם רוצים יותר שליטה על המיקום שבו נטען הקישור שנלחץ עליו, תוכלו ליצור WebViewClient
משלכם שיחליף את השיטה shouldOverrideUrlLoading()
. בדוגמה הבאה MyWebViewClient
הוא כיתה פנימית של Activity
.
Kotlin
private class MyWebViewClient : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { if (Uri.parse(url).host == "www.example.com") { // This is your website, so don't override. Let your WebView load // the page. return false } // Otherwise, the link isn't for a page on your site, so launch another // Activity that handles URLs. Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply { startActivity(this) } return true } }
Java
private class MyWebViewClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { if ("www.example.com".equals(request.getUrl().getHost())) { // This is your website, so don't override. Let your WebView load the // page. return false; } // Otherwise, the link isn't for a page on your site, so launch another // Activity that handles URLs. Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl()); startActivity(intent); return true; } }
לאחר מכן יוצרים מופע של WebViewClient
החדש עבור WebView
:
Kotlin
val myWebView: WebView = findViewById(R.id.webview) myWebView.webViewClient = MyWebViewClient()
Java
WebView myWebView = (WebView) findViewById(R.id.webview); myWebView.setWebViewClient(new MyWebViewClient());
עכשיו, כשהמשתמש מקייש על קישור, המערכת קוראת לשיטה shouldOverrideUrlLoading()
, שבה בודקים אם מארח כתובת ה-URL תואם לדומיין ספציפי, כפי שהוגדר בדוגמה הקודמת. אם יש התאמה, השיטה מחזירה את הערך false ולא מבטלת את טעינת כתובת ה-URL. הוא מאפשר ל-WebView
לטעון את כתובת ה-URL כרגיל. אם מארח כתובת ה-URL לא תואם, נוצר אירוע Intent
כדי להפעיל את Activity
שמוגדרת כברירת מחדל לטיפול בכתובות URL, שמפנה לדפדפן האינטרנט שמוגדר כברירת מחדל של המשתמש.
טיפול בכתובות URL מותאמות אישית
WebView
מחילה הגבלות כאשר מבקשים משאבים ופותרים קישורים
שמשתמשים בסכימה של כתובת URL מותאמת אישית. לדוגמה, אם מטמיעים קריאות חזרה (callbacks) כמו shouldOverrideUrlLoading()
או shouldInterceptRequest()
, WebView
מפעיל אותן רק עבור כתובות URL תקינות.
לדוגמה, יכול להיות ש-WebView
לא יפעיל את השיטה shouldOverrideUrlLoading()
עבור קישורים כמו:
<a href="showProfile">Show Profile</a>
המערכת של WebView
מטפלת באופן לא עקבי בכתובות URL לא תקינות, כמו זו שמוצגת בדוגמה הקודמת, לכן מומלץ להשתמש במקום זאת בכתובת URL תקינה.
אפשר להשתמש בסכמה מותאמת אישית או בכתובת URL מסוג HTTPS לדומיין שבשליטת הארגון.
במקום להשתמש במחרוזת פשוטה בקישור, כמו בדוגמה הקודמת, אפשר להשתמש בסכימה מותאמת אישית כמו זו:
<a href="example-app:showProfile">Show Profile</a>
לאחר מכן תוכלו לטפל בכתובת ה-URL הזו בשיטה shouldOverrideUrlLoading()
באופן הבא:
Kotlin
// The URL scheme must be non-hierarchical, meaning no trailing slashes. const val APP_SCHEME = "example-app:" override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { return if (url?.startsWith(APP_SCHEME) == true) { urlData = URLDecoder.decode(url.substring(APP_SCHEME.length), "UTF-8") respondToData(urlData) true } else { false } }
Java
// The URL scheme must be non-hierarchical, meaning no trailing slashes. private static final String APP_SCHEME = "example-app:"; @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith(APP_SCHEME)) { urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8"); respondToData(urlData); return true; } return false; }
ה-API של shouldOverrideUrlLoading()
מיועד בעיקר להפעלת כוונות לכתובות URL ספציפיות. כשמטמיעים אותו, חשוב להחזיר את הערך false
לכתובות URL ש-WebView
מטפל בהן. עם זאת, אתם לא מוגבלים להשקת כוונות. אפשר להחליף את ההפעלה של Intentים בכל התנהגות מותאמת אישית בדוגמאות הקוד הקודמות.
ניווט בהיסטוריית דפי האינטרנט
כשה-WebView
מבטל את טעינת כתובת ה-URL, הוא מצטבר באופן אוטומטי היסטוריה של דפי אינטרנט שנצפו. אפשר לנווט אחורה וקדימה בהיסטוריה באמצעות goBack()
ו-goForward()
.
לדוגמה, כך אפשר לראות איך Activity
יכול להשתמש בלחצן 'הקודם' של המכשיר כדי לנווט אחורה:
Kotlin
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { // Check whether the key event is the Back button and if there's history. if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) { myWebView.goBack() return true } // If it isn't the Back button or there isn't web page history, bubble up to // the default system behavior. Probably exit the activity. return super.onKeyDown(keyCode, event) }
Java
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { // Check whether the key event is the Back button and if there's history. if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) { myWebView.goBack(); return true; } // If it isn't the Back button or there's no web page history, bubble up to // the default system behavior. Probably exit the activity. return super.onKeyDown(keyCode, event); }
אם האפליקציה שלכם משתמשת ב-AndroidX AppCompat
מגרסה 1.6.0 ואילך, תוכלו לפשט עוד יותר את קטע הקוד הקודם:
Kotlin
onBackPressedDispatcher.addCallback { // Check whether there's history. if (myWebView.canGoBack()) { myWebView.goBack() } }
Java
onBackPressedDispatcher.addCallback { // Check whether there's history. if (myWebView.canGoBack()) { myWebView.goBack(); } }
השיטה canGoBack()
מחזירה את הערך true אם יש היסטוריית דפי אינטרנט שהמשתמש יכול לבקר בהם. באופן דומה, אפשר להשתמש ב-canGoForward()
כדי לבדוק אם יש היסטוריה של העברה קדימה. אם לא מבצעים את הבדיקה הזו, אחרי שהמשתמש מגיע לסוף ההיסטוריה, goBack()
ו-goForward()
לא עושים כלום.
טיפול בשינויים בהגדרות המכשיר
במהלך זמן הריצה, שינויים במצב הפעילות מתרחשים כשהגדרות המכשיר משתנות, למשל כשמשתמשים מסובבים את המכשיר או סוגרים עורך שיטת קלט (IME). השינויים האלה גורמים להשמדה של פעילות של אובייקט WebView
וליצירת פעילות חדשה, שבעקבותיה נוצר גם אובייקט WebView
חדש שטוען את כתובת ה-URL של האובייקט שהושמד. כדי לשנות את התנהגות ברירת המחדל של הפעילות, אפשר לשנות את האופן שבו היא מטפלת בשינויים ב-orientation
במניפסט. למידע נוסף על טיפול בשינויים בהגדרות זמן ריצה, קראו את המאמר שינויים בהגדרות של הכינוי.
ניהול החלונות
כברירת מחדל, המערכת מתעלמת מבקשות לפתיחת חלונות חדשים. הדבר נכון בין אם פותחים אותם באמצעות JavaScript או על ידי מאפיין היעד בקישור. אתם יכולים להתאים אישית את WebChromeClient
בהתאם להתנהגות שלכם כשאתם פותחים כמה חלונות.
כדי לשפר את האבטחה של האפליקציה, מומלץ למנוע פתיחה של חלונות קופצים וחלונות חדשים. הדרך הבטוחה ביותר להטמיע את ההתנהגות הזו היא להעביר את "true"
אל setSupportMultipleWindows()
, אבל לא לבטל את ה-method onCreateWindow()
, שהפעולה setSupportMultipleWindows()
תלויה בה. הלוגיקה הזו מונעת טעינה של כל דף שהקישורים שלו מכילים את target="_blank"
.