ניהול אובייקטים של WebView

ב-Android יש כמה ממשקי API שיכולים לעזור לכם לנהל WebView אובייקטים שמציגים תוכן מהאינטרנט באפליקציה.

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

גרסה API

החל מ-Android 7.0 (רמת API 24), המשתמשים יכולים לבחור מבין כמה חבילות שונות להצגת תוכן מהאינטרנט באובייקט WebView. הפונקציה AndroidX.webkit כוללת את getCurrentWebViewPackage() שיטה לאחזור מידע שקשור לחבילת הגלישה תוכן באפליקציה. השיטה הזו שימושית לניתוח שגיאות שמתרחשות רק כשהאפליקציה שלך מנסה להציג תוכן מהאינטרנט באמצעות חבילה מסוימת של WebView.

כדי להשתמש בשיטה הזו, צריך להוסיף את הלוגיקה שמוצגת בקטע הקוד הבא:

Kotlin

val webViewPackageInfo = WebViewCompat.getCurrentWebViewPackage(appContext)
Log.d("MY_APP_TAG", "WebView version: ${webViewPackageInfo.versionName}")

Java

PackageInfo webViewPackageInfo = WebViewCompat.getCurrentWebViewPackage(appContext);
Log.d("MY_APP_TAG", "WebView version: " + webViewPackageInfo.versionName);

שירות הגלישה הבטוחה של Google

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

למרות שערך ברירת המחדל של EnableSafeBrowsing הוא True, יש הם מקרים שבהם תרצו להפעיל רק את הגלישה הבטוחה באופן מותנה, או להשבית אותה. Android 8.0 (רמת API 26) ואילך תומך ב- setSafeBrowsingEnabled() כדי להחליף את המצב של הגלישה הבטוחה לאובייקט WebView ספציפי.

אם רוצים לבטל את ההסכמה לגלישה הבטוחה בכל WebView האובייקטים בדיקות, יש להוסיף את הרכיב <meta-data> הבא קובץ מניפסט:

<manifest>
    <application>
        <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
                   android:value="false" />
        ...
    </application>
</manifest>

להגדיר פעולות פרוגרמטיות

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

אם מטרגטים ל-Android מגרסה 8.1 (רמת API 27) ואילך, אפשר להגדיר האופן שבו האפליקציה מגיבה באופן פרוגרמטי לאיום ידוע במקרים הבאים דרכים:

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

קטעי הקוד הבאים מראים איך להורות למקרים של האפליקציה WebView כדי לחזור למצב הבטוח אחרי שנתקלים איום:

MyWebActivity.Java

Kotlin

private lateinit var superSafeWebView: WebView
private var safeBrowsingIsInitialized: Boolean = false

// ...

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    superSafeWebView = WebView(this)
    superSafeWebView.webViewClient = MyWebViewClient()
    safeBrowsingIsInitialized = false

    if (WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) {
        WebViewCompat.startSafeBrowsing(this, ValueCallback<Boolean> { success ->
            safeBrowsingIsInitialized = true
            if (!success) {
                Log.e("MY_APP_TAG", "Unable to initialize Safe Browsing!")
            }
        })
    }
}

Java

private WebView superSafeWebView;
private boolean safeBrowsingIsInitialized;

// ...

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    superSafeWebView = new WebView(this);
    superSafeWebView.setWebViewClient(new MyWebViewClient());
    safeBrowsingIsInitialized = false;

    if (WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) {
        WebViewCompat.startSafeBrowsing(this, new ValueCallback<Boolean>() {
            @Override
            public void onReceiveValue(Boolean success) {
                safeBrowsingIsInitialized = true;
                if (!success) {
                    Log.e("MY_APP_TAG", "Unable to initialize Safe Browsing!");
                }
            }
        });
    }
}

MyWebViewClient.Java

Kotlin

class MyWebViewClient : WebViewClientCompat() {
    // Automatically go "back to safety" when attempting to load a website that
    // Google identifies as a known threat. An instance of WebView calls this
    // method only after Safe Browsing is initialized, so there's no conditional
    // logic needed here.
    override fun onSafeBrowsingHit(
            view: WebView,
            request: WebResourceRequest,
            threatType: Int,
            callback: SafeBrowsingResponseCompat
    ) {
        // The "true" argument indicates that your app reports incidents like
        // this one to Safe Browsing.
        if (WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY)) {
            callback.backToSafety(true)
            Toast.makeText(view.context, "Unsafe web page blocked.", Toast.LENGTH_LONG).show()
        }
    }
}

Java

public class MyWebViewClient extends WebViewClientCompat {
    // Automatically go "back to safety" when attempting to load a website that
    // Google identifies as a known threat. An instance of WebView calls this
    // method only after Safe Browsing is initialized, so there's no conditional
    // logic needed here.
    @Override
    public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
            int threatType, SafeBrowsingResponseCompat callback) {
        // The "true" argument indicates that your app reports incidents like
        // this one to Safe Browsing.
        if (WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY)) {
            callback.backToSafety(true);
            Toast.makeText(view.getContext(), "Unsafe web page blocked.",
                    Toast.LENGTH_LONG).show();
        }
    }
}

ממשק API של HTML5 Geolocation

לאפליקציות שמטרגטות ל-Android 6.0 (רמת API 23) ואילך, Geolocation API נתמכת רק במקורות מאובטחים, כמו HTTPS. כל בקשה Geolocation API במקורות לא מאובטחים נדחה באופן אוטומטי בלי להפעיל רכיב ה-method המתאים של onGeolocationPermissionsShowPrompt().

ביטול ההסכמה לאיסוף מדדים

ל-WebView יש אפשרות להעלות נתוני אבחון אנונימיים אל Google כשהמשתמש מביע הסכמה. הנתונים נאספים על בסיס כל אפליקציה בנפרד לכל אפליקציה שמייצרת WebView. יש לך אפשרות לבטל את ההסכמה על ידי יצירת התג הבא בקובץ המניפסט רכיב <application>:

<manifest>
    <application>
    ...
    <meta-data android:name="android.webkit.WebView.MetricsOptOut"
               android:value="true" />
    </application>
</manifest>

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

API לטיפול בסיום

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

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

קטע הקוד הבא מדגים איך להשתמש ב-API הזה בתוך Activity:

Kotlin

    
inner class MyRendererTrackingWebViewClient : WebViewClient() {
    private var mWebView: WebView? = null

    override fun onRenderProcessGone(view: WebView, detail: RenderProcessGoneDetail): Boolean {
        if (!detail.didCrash()) {
            // Renderer is killed because the system ran out of memory. The app
            // can recover gracefully by creating a new WebView instance in the
            // foreground.
            Log.e("MY_APP_TAG", ("System killed the WebView rendering process " +
                "to reclaim memory. Recreating..."))

            mWebView?.also { webView ->
                val webViewContainer: ViewGroup = findViewById(R.id.my_web_view_container)
                webViewContainer.removeView(webView)
                webView.destroy()
                mWebView = null
            }

            // By this point, the instance variable "mWebView" is guaranteed to
            // be null, so it's safe to reinitialize it.

            return true // The app continues executing.
        }

        // Renderer crashes because of an internal error, such as a memory
        // access violation.
        Log.e("MY_APP_TAG", "The WebView rendering process crashed!")

        // In this example, the app itself crashes after detecting that the
        // renderer crashed. If you handle the crash more gracefully and let
        // your app continue executing, you must destroy the current WebView
        // instance, specify logic for how the app continues executing, and
        // return "true" instead.
        return false
    }
}

Java

public class MyRendererTrackingWebViewClient extends WebViewClient {
    private WebView mWebView;

    @Override
    public boolean onRenderProcessGone(WebView view,
            RenderProcessGoneDetail detail) {
        if (!detail.didCrash()) {
            // Renderer is killed because the system ran out of memory. The app
            // can recover gracefully by creating a new WebView instance in the
            // foreground.
            Log.e("MY_APP_TAG", "System killed the WebView rendering process " +
                    "to reclaim memory. Recreating...");

            if (mWebView != null) {
                ViewGroup webViewContainer =
                        (ViewGroup) findViewById(R.id.my_web_view_container);
                webViewContainer.removeView(mWebView);
                mWebView.destroy();
                mWebView = null;
            }

            // By this point, the instance variable "mWebView" is guaranteed to
            // be null, so it's safe to reinitialize it.

            return true; // The app continues executing.
        }

        // Renderer crashes because of an internal error, such as a memory
        // access violation.
        Log.e("MY_APP_TAG", "The WebView rendering process crashed!");

        // In this example, the app itself crashes after detecting that the
        // renderer crashed. If you handle the crash more gracefully and let
        // your app continue executing, you must destroy the current WebView
        // instance, specify logic for how the app continues executing, and
        // return "true" instead.
        return false;
    }
}

ממשק API של רינדור Renderer

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

קטע הקוד הבא מראה איך להקצות עדיפות לכלי לרינדור תהליך שמשויך לאובייקטים של WebView באפליקציה:

Kotlin

val myWebView: WebView = ...
myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true)

Java

WebView myWebView;
myWebView.setRendererPriorityPolicy(RENDERER_PRIORITY_BOUND, true);

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

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