פיתוח אפליקציות אינטרנט ב-WebView

שימוש ב-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 אל משתמש. למידע נוסף על ניפוי באגים בהקשר הזה, אפשר לקרוא את המאמר ניפוי באגים באינטרנט של Google.
  • טיפול באירועים שמשפיעים על רינדור התוכן, כמו שגיאות בטופס. שליחות או ניווט באמצעות WebViewClient אפשר גם להשתמש המחלקה המשנית הזו כדי ליירט את הטעינה של כתובות ה-URL.
  • הפעלת JavaScript על-ידי שינוי WebSettings
  • שימוש ב-JavaScript כדי לגשת לאובייקטים של framework ב-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 יכול לקרוא לו כדי לגשת לכיתה.

לדוגמה, אפשר לכלול את הכיתה הבאה באפליקציה ל-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 כרגיל. אם המארח של כתובת האתר לא תואם, אז הדומיין Intent נוצר כדי להפעיל את ברירת המחדל Activity לטיפול בכתובות URL, שמפנה לדפדפן האינטרנט שמוגדר כברירת מחדל של המשתמש.

טיפול בכתובות URL מותאמות אישית

WebView מחילה הגבלות כאשר מבקשים משאבים ופותרים קישורים שמשתמשים בסכימה של כתובת URL מותאמת אישית. לדוגמה, אם אתם מטמיעים קריאות חוזרות (callback), כמו shouldOverrideUrlLoading() או shouldInterceptRequest(), לאחר מכן, WebView מפעיל אותן רק עבור כתובות URL חוקיות.

לדוגמה, יכול להיות ש-WebView לא יקרא ל-method shouldOverrideUrlLoading() לקישורים כמו זה:

<a href="showProfile">Show Profile</a>

כתובות URL לא חוקיות, כמו הכתובת שמוצגת בדוגמה הקודמת, מטופלות באופן לא עקבי ב-WebView, לכן מומלץ להשתמש במקום זאת בכתובת 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() מיועד בעיקר להשקה של Intents לכתובות URL ספציפיות. כשמטמיעים את המזהה, צריך להקפיד להחזיר את הערך false לכתובות URL הכינויים של WebView. עם זאת, אתם לא מוגבלים להשקת כוונות. אפשר החלפת ההפעלה של Intents בכל התנהגות מותאמת אישית שמופיעה בקוד הקודם דוגמאות.

כשהערך של 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() אבל לא לשנות את onCreateWindow() ש-setSupportMultipleWindows() תלויה בה. הלוגיקה הזאת מונעת דף שהקישורים בו מופיעים ב-target="_blank".