אפשר להשתמש ב-WebView כדי להציג אפליקציית אינטרנט או דף אינטרנט כחלק מאפליקציית לקוח. המחלקה WebView היא תוסף של המחלקה View של Android, שמאפשרת להציג דפי אינטרנט כחלק מהפריסה של הפעילות. הוא לא כולל את התכונות של דפדפן אינטרנט מלא, כמו פקדי ניווט או סרגל כתובות. כברירת מחדל, כל מה ש-WebView עושה זה להציג דף אינטרנט.
WebView יכול לעזור לכם לספק מידע באפליקציה שאולי תצטרכו לעדכן, כמו הסכם משתמש קצה או מדריך למשתמש. באפליקציה ל-Android, אתם יכולים ליצור Activity שמכיל WebView, ואז להשתמש בו כדי להציג את המסמך שמתארח אונליין.
WebView יכול גם לעזור כשהאפליקציה מספקת למשתמש נתונים שצריך חיבור לאינטרנט כדי לאחזר אותם, כמו אימייל. במקרה כזה, יכול להיות שיהיה קל יותר ליצור WebView באפליקציה ל-Android שמציג דף אינטרנט עם כל נתוני המשתמש, במקום לבצע בקשה לאחזור מהרשת, לנתח את הנתונים ולהציג אותם בפריסה של Android. במקום זאת, אתם יכולים לעצב דף אינטרנט שמותאם למכשירים עם Android, ואז להטמיע WebView באפליקציה ל-Android שטוענת את דף האינטרנט.
במסמך הזה מוסבר איך להתחיל להשתמש ב-WebView, איך לקשר JavaScript מדף האינטרנט לקוד בצד הלקוח באפליקציה ל-Android, איך לטפל בניווט בדפים ואיך לנהל חלונות כשמשתמשים ב-WebView.
איך עובדים עם WebView בגרסאות קודמות של Android
כדי להשתמש בבטחה ביכולות עדכניות יותר של WebView במכשיר שבו האפליקציה פועלת, צריך להוסיף את הספרייה Jetpack 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 לאפליקציה ב-method 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 שהוספתם ל-
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 יכול לקרוא ל-method בקוד Android כדי להציג Dialog, במקום להשתמש בפונקציה alert() של JavaScript.
כדי לקשר ממשק חדש בין קוד JavaScript לקוד Android, קוראים ל-addJavascriptInterface ומעבירים לו מופע של מחלקה לקישור ל-JavaScript ושם ממשק שה-JavaScript יכול לקרוא לו כדי לגשת למחלקה.
במאמר גישה לממשקי API מקוריים באמצעות JSBridge מוסבר איך לתקשר בין JavaScript לקוד Native, כולל ממשקי API מודרניים ומאובטחים יותר.
לדוגמה, אפשר לכלול את המחלקה הבאה באפליקציה ל-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 כדי לשלוח קריאה למתודה 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());
עכשיו, כשהמשתמש ילחץ על קישור, המערכת תפעיל את ה-method 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>
כתובות URL לא תקינות, כמו זו שמוצגת בדוגמה הקודמת, מטופלות באופן לא עקבי ב-WebView, ולכן מומלץ להשתמש בכתובת URL תקינה.
אפשר להשתמש בסכימה מותאמת אישית או בכתובת URL מסוג HTTPS לדומיין שבשליטת הארגון.
במקום להשתמש במחרוזת פשוטה בקישור, כמו בדוגמה הקודמת, אתם יכולים להשתמש בסכימה מותאמת אישית כמו זו:
<a href="example-app:showProfile">Show Profile</a>
אחר כך אפשר לטפל בכתובת ה-URL הזו ב-method 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; }
shouldOverrideUrlLoading API מיועד בעיקר להפעלת כוונות (intent) לכתובות 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); }
אם האפליקציה משתמשת ב-Jetpack 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(); } }
ה-method canGoBack מחזיר את הערך true אם יש היסטוריית דפי אינטרנט שהמשתמש יכול לבקר בהם. באופן דומה, אפשר להשתמש ב-canGoForward כדי לבדוק אם יש היסטוריית דפים קדימה. אם לא מבצעים את הבדיקה הזו, אחרי שהמשתמש מגיע לסוף ההיסטוריה, goBack ו-goForward לא עושים כלום.
כדי לשמור את היסטוריית הניווט בין יצירה מחדש של פעילות או החזרה של תהליך ברקע, משתמשים ב-saveState כדי להפוך את היסטוריית הניווט של WebView לסדרה ב-Bundle, וב-restoreState כדי לשחזר אותה.
במאמר ניהול יעיל של מצב WebView מוסבר איך לנהל בצורה בטוחה את מגבלות הגודל של המצב השמור ולהימנע משגיאות TransactionTooLargeException.
טיפול בשינויים בהגדרות המכשיר
בזמן הריצה, שינויים במצב הפעילות מתרחשים כשמשתנה ההגדרה של המכשיר, למשל כשמשתמשים מסובבים את המכשיר או סוגרים עורך שיטות קלט (IME). השינויים האלה גורמים להשמדת הפעילות של אובייקט WebView וליצירת פעילות חדשה, שגם יוצרת אובייקט WebView חדש שטוען את כתובת ה-URL של האובייקט שהושמד. אפשר להשתמש ב-saveState בתוך onSaveInstanceState() וב-restoreState אחרי היצירה מחדש כדי לשחזר את הקשר הניווט של המשתמש. במאמר ניהול יעיל של מצב WebView מפורטות אסטרטגיות לניהול בטוח של גודל המצב.
כדי לשנות את פעולת ברירת המחדל של הפעילות, אתם יכולים לשנות את האופן שבו היא מטפלת בשינויים ב-orientation במניפסט. במאמר טיפול בשינויים בהגדרות האישיות מוסבר איך לטפל בשינויים בהגדרות האישיות בזמן הריצה.
ניהול החלונות
כברירת מחדל, המערכת מתעלמת מבקשות לפתיחת חלונות חדשים. זה נכון גם אם הם נפתחים באמצעות JavaScript או באמצעות מאפיין היעד בקישור. אפשר להתאים אישית את WebChromeClient כדי להגדיר התנהגות משלכם לפתיחת כמה חלונות.
כדי לשמור על אבטחת האפליקציה, מומלץ למנוע פתיחה של חלונות קופצים וחלונות חדשים. הדרך הבטוחה ביותר להטמיע את ההתנהגות הזו היא להעביר את "true" אל setSupportMultipleWindows, אבל לא לשנות את ה-method onCreateWindow, ש-setSupportMultipleWindows() תלוי בה.
הלוגיקה הזו מונעת טעינה של כל דף שמשתמש ב-target="_blank" בקישורים שלו.
ניהול הזיכרון והביצועים
מכיוון ש-WebView פועל במנוע שמבוסס על Chromium עם תהליכים נפרדים וקוד Native, השימוש הבסיסי שלו בזיכרון גבוה יותר מאשר תצוגות Android רגילות.
כדי לשמור על יציבות האפליקציה, חשוב לעשות שימוש חוזר במופעים של WebView ולהשמיד אותם כמו שצריך.
במאמר ניהול ואבחון של הזיכרון של WebView מוסבר איך לאבחן ולבצע אופטימיזציה של הזיכרון שבשימוש של האפליקציה כשמשתמשים בתוכן מהאינטרנט.