משתמשים ב-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. זוהי ספרייה סטטית שאפשר להוסיף לאפליקציה כדי להשתמש בממשקי android.webkit API שלא זמינים בגרסאות קודמות של הפלטפורמה.
מוסיפים אותו לקובץ 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 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 יכול לקרוא לו כדי לגשת למחלקה.
מידע נוסף על תקשורת בין JavaScript וקוד Native, כולל ממשקי API מודרניים ומאובטחים יותר, זמין במאמר גישה לממשקי API מקוריים באמצעות JSBridge.
לדוגמה, אפשר לכלול את המחלקה הבאה באפליקציית 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());
עכשיו, כשהמשתמש מקיש על קישור, המערכת קוראת לשיטה shouldOverrideUrlLoading()
שבודקת אם המארח של כתובת ה-URL תואם לדומיין ספציפי, כפי שמוגדר
בדוגמה הקודמת. אם יש התאמה, השיטה מחזירה false ולא מבטלת את טעינת כתובת ה-URL. היא מאפשרת ל-WebView לטעון את כתובת ה-URL כרגיל.
אם המארח של כתובת ה-URL לא תואם, נוצר Intent כדי להפעיל את Activity שמוגדר כברירת מחדל לטיפול בכתובות URL, והוא מומר לדפדפן האינטרנט שמוגדר כברירת מחדל אצל המשתמש.
ניהול כתובות URL מותאמות אישית
WebView מטיל הגבלות כשמבקשים משאבים ומפענחים קישורים שמשתמשים בסכמת כתובות URL מותאמת אישית. לדוגמה, אם מטמיעים קריאות חוזרות כמו shouldOverrideUrlLoading() או shouldInterceptRequest(), אז WebView מפעיל אותן רק לכתובות URL תקינות.
לדוגמה, יכול להיות שהשיטה shouldOverrideUrlLoading() לא תופעל עבור קישורים כמו WebView:
<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 מטפל בהן. עם זאת, אתם לא מוגבלים להפעלת כוונות. אתם יכולים להחליף את הפעלת הכוונות בכל התנהגות מותאמת אישית בדוגמאות הקוד שלמעלה.
ניווט בהיסטוריה של דפי אינטרנט
כש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(); } }
השיטה canGoBack() מחזירה true אם יש היסטוריה של דפי אינטרנט שהמשתמש יכול לבקר בהם. באופן דומה, אפשר להשתמש ב-canGoForward() כדי לבדוק אם יש היסטוריה של העברה. אם לא תבצעו את הבדיקה הזו, אחרי שהמשתמש יגיע לסוף ההיסטוריה, goBack() ו-goForward() לא יעשו כלום.
טיפול בשינויים בהגדרות המכשיר
במהלך זמן הריצה, שינויים במצב הפעילות מתרחשים כשמתבצע שינוי בהגדרות של המכשיר, למשל כשמשתמשים מסובבים את המכשיר או סוגרים את עורך שיטות הקלט (IME). השינויים האלה גורמים להרס של הפעילות של אובייקט WebView וליצירה של פעילות חדשה, שיוצרת גם אובייקט WebView חדש שבו נטען ה-URL של האובייקט שנהרס. כדי לשנות את התנהגות ברירת המחדל של הפעילות, אפשר לשנות את אופן הטיפול בשינויים בorientation במניפסט. מידע נוסף על טיפול בשינויים בהגדרות במהלך זמן הריצה זמין במאמר טיפול בשינויים בהגדרות.
ניהול החלונות
כברירת מחדל, המערכת מתעלמת מבקשות לפתוח חלונות חדשים. הדבר נכון גם אם הם נפתחים על ידי JavaScript או על ידי מאפיין היעד בקישור. אתם יכולים להתאים אישית את WebChromeClient כדי להגדיר התנהגות משלכם לפתיחת חלונות מרובים.
כדי לשפר את האבטחה של האפליקציה, מומלץ למנוע את הפתיחה של חלונות קופצים וחלונות חדשים. הדרך הבטוחה ביותר להטמיע את ההתנהגות הזו היא להעביר את "true" אל setSupportMultipleWindows() בלי לבטל את השיטה onCreateWindow(), ש-setSupportMultipleWindows() תלויה בה. הלוגיקה הזו מונעת טעינה של כל דף שמשתמש ב-target="_blank" בקישורים שלו.