WebViewCompat.navigate की मदद से, पेज नेविगेशन की बेहतर सुविधा

WebViewCompat.navigate, WebView.loadUrl का बेहतर विकल्प है. यह WebView में वेब पेज लोड होने, इतिहास को मैनेज करने, और नेविगेशन लाइफ़साइकल को ट्रैक करने की सुविधा देता है.

पहले, loadUrl का इस्तेमाल करके पेज नेविगेशन शुरू करने की सुविधा में ये समस्याएं थीं:

  • इतिहास की एंट्री को बदला नहीं जा सकता: इतिहास की मौजूदा एंट्री को बदला नहीं जा सकता. इसलिए, पिछली गतिविधियों में कोई एंट्री जोड़े बिना किसी नए पेज पर नेविगेट करना मुमकिन नहीं है.
  • डिकपल किए गए कॉलबैक: WebViewClient में, किसी खास loadUrl कॉल को बाद के कॉलबैक इवेंट से जोड़ने का कोई सीधा तरीका नहीं था.
  • ज़्यादा हेडर सेव नहीं किए गए: loadUrl को पास किए गए कस्टम हेडर, WebView की स्थिति के हिस्से के तौर पर सेव नहीं किए गए थे. इसलिए, स्थिति को वापस लाने पर वे मिट गए.

WebViewCompat.navigate एपीआई, इन समस्याओं को हल करता है. इसके लिए, यह इन सुविधाओं को उपलब्ध कराता है:

  • नेविगेशन के इतिहास की एंट्री बदलना: इससे WebView इतिहास स्टैक में मौजूद मौजूदा पेज को बदला जा सकता है.
  • कोरिलेटेड कॉलबैक ट्रैकिंग: यह एक Navigation ऑब्जेक्ट दिखाता है. यह नेविगेशन लाइफ़साइकल के सभी चरणों में एक यूनीक आइडेंटिफ़ायर के तौर पर काम करता है.
  • सेव किए गए स्टेट हेडर के लिए सहायता: अतिरिक्त हेडर को WebView स्टेट बंडल में सेव किया जाता है, ताकि स्टेट को वापस लाने पर उनका फिर से इस्तेमाल किया जा सके.

मुख्य सुविधाएं और सीमाएं

WebViewCompat.navigate को अपनाने से पहले, संचालन से जुड़े इन नियमों और पाबंदियों का ध्यान रखें:

  • थ्रेड सुरक्षा: आपको यूज़र इंटरफ़ेस (यूआई) (मुख्य) थ्रेड पर WebViewCompat.navigate को लागू करना होगा.

  • रद्द करना और प्राथमिकता: फ़्लाइट के दौरान नेविगेशन को साफ़ तौर पर रद्द नहीं किया जा सकता. हालांकि, उसी WebView पर नई navigate कॉल शुरू करने से, नेविगेशन की सुविधा बंद हो जाती है.

  • यूआरआई स्कीम के साथ काम करता है: स्टैंडर्ड (जैसे कि https: और http:) और कस्टम यूआरआई स्कीम के साथ काम करता है. javascript: स्कीम काम नहीं करती है.

  • यूआरएल के साइज़ की सीमा: यूआरएल स्ट्रिंग की लंबाई ज़्यादा से ज़्यादा 2 एमबी हो सकती है.

  • सुविधा की उपलब्धता की जांच करना: एपीआई को शुरू करने से पहले, हमेशा WebViewFeature.isFeatureSupported का इस्तेमाल करके, सुविधा की उपलब्धता की जांच करें. इससे अलग-अलग WebView APK वर्शन के साथ काम करने की सुविधा बनी रहती है.

नेविगेशन शुरू करना और लाइफ़साइकल को ट्रैक करना

नेविगेशन को कॉन्फ़िगर करने और उसके लाइफ़साइकल को ट्रैक करने के लिए, यह तरीका अपनाएं:

  1. लाइफ़साइकल के स्ट्रक्चर्ड कॉलबैक पाने के लिए, WebView सेटअप के दौरान WebViewCompat.addNavigationListener का इस्तेमाल करके, NavigationListener लागू होने की जानकारी रजिस्टर करें. मेमोरी लीक और डुप्लीकेट कॉलबैक को रोकने के लिए, हर नेविगेशन कॉल पर रजिस्टर करने के बजाय, लिसनर को एक बार रजिस्टर करें.
  2. NavigationParameters इंस्टेंस बनाने के लिए, NavigationParameters.Builder का इस्तेमाल करें. इससे, इतिहास बदलने या कस्टम एचटीटीपी हेडर जैसे वैकल्पिक व्यवहार तय किए जा सकते हैं.
  3. अपने WebView इंस्टेंस, डेस्टिनेशन यूआरएल, और पैरामीटर पास करके, WebViewCompat.navigate को कॉल करें.

WebViewCompat.navigate, एक Navigation ऑब्जेक्ट दिखाता है. यह ऑब्जेक्ट, अनुरोध की यूनीक तरीके से पहचान करता है. अपने NavigationListener कॉलबैक में, इस ऑब्जेक्ट की तुलना आने वाले Navigation पैरामीटर से करें, ताकि उस नेविगेशन को ट्रैक किया जा सके.

लागू करने का उदाहरण

यहां दिए गए उदाहरण में, नेविगेशन पैरामीटर कॉन्फ़िगर करने, WebViewCompat.navigate को लागू करने, और नेविगेशन के लाइफ़साइकल इवेंट सुनने का तरीका बताया गया है:

Kotlin

class WebNavigationManager(private val webView: WebView) {
    // Track the navigation instance returned by the API
    private var currentNavigation: Navigation? = null

    init {
        // 1. Define listener to observe navigation lifecycle events
        val listener = object : NavigationListener {
            override fun onNavigationStarted(navigation: Navigation) {
                if (navigation == currentNavigation) {
                    // Navigation started
                }
            }

            override fun onNavigationRedirected(navigation: Navigation) {
                if (navigation == currentNavigation) {
                    // Navigation encountered a redirect
                }
            }

            override fun onNavigationCompleted(navigation: Navigation) {
                if (navigation == currentNavigation) {
                    if (navigation.didCommit()) {
                        // Navigation committed successfully
                    } else if (navigation.didCommitErrorPage()) {
                        // Navigation committed an error page
                        val statusCode = navigation.statusCode
                        val error = navigation.webResourceError
                    }
                }
            }

            override fun onFirstContentfulPaintMillis(page: Page, durationMillis: Long) {
                // Match page with current navigation
                if (page == currentNavigation?.page) {
                    // Page rendering started (First Contentful Paint achieved)
                }
            }
        }

        // 2. Register listener on the main thread
        WebViewCompat.addNavigationListener(webView, listener)
    }

    @UiThread
    fun navigateToPage(url: String) {
        // Check feature availability
        if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEBVIEW_NAVIGATE_EXPERIMENTAL_V1)) {
            // Fall back to standard loadUrl if navigate API is unavailable
            webView.loadUrl(url)
            return
        }

        // 3. Configure navigation parameters
        val params = NavigationParameters.Builder()
            .setShouldReplaceCurrentEntry(true)
            .addAdditionalHeaders(
                mapOf("X-Test-Navigate-Header" to "TestValue")
            )
            .build()

        // 4. Initiate navigation on the UI thread
        currentNavigation = WebViewCompat.navigate(webView, url, params)
    }
}

Java

public class WebNavigationManager {

    private Navigation mCurrentNavigation;
    private final WebView mWebView;

    public WebNavigationManager(@NonNull WebView webView) {
        mWebView = webView;
        setupListener();
    }

    private void setupListener() {
        // 1. Define listener to observe navigation lifecycle events
        NavigationListener listener = new NavigationListener() {
            @Override
            public void onNavigationStarted(@NonNull Navigation navigation) {
                if (navigation.equals(mCurrentNavigation)) {
                    // Navigation started
                }
            }

            @Override
            public void onNavigationRedirected(@NonNull Navigation navigation) {
                if (navigation.equals(mCurrentNavigation)) {
                    // Navigation encountered a redirect
                }
            }

            @Override
            public void onNavigationCompleted(@NonNull Navigation navigation) {
                if (navigation.equals(mCurrentNavigation)) {
                    if (navigation.didCommit()) {
                        // Navigation committed successfully
                    } else if (navigation.didCommitErrorPage()) {
                        // Navigation committed an error page
                        int statusCode = navigation.getStatusCode();
                        WebResourceErrorCompat error = navigation.getWebResourceError();
                    }
                }
            }

            @Override
            public void onFirstContentfulPaintMillis(@NonNull Page page, long durationMillis) {
                if (mCurrentNavigation != null && page.equals(mCurrentNavigation.getPage())) {
                    // Page rendering started (First Contentful Paint achieved)
                }
            }
        };

        // 2. Register listener on the main thread
        WebViewCompat.addNavigationListener(mWebView, listener);
    }

    @UiThread
    public void navigateToPage(@NonNull String url) {
        // Check feature availability
        if (!WebViewFeature.isFeatureSupported(WebViewFeature.WEBVIEW_NAVIGATE_EXPERIMENTAL_V1)) {
            // Fall back to standard loadUrl if navigate API is unavailable
            mWebView.loadUrl(url);
            return;
        }

        // 3. Configure navigation parameters
        NavigationParameters params = new NavigationParameters.Builder()
            .setShouldReplaceCurrentEntry(true)
            .addAdditionalHeaders(Collections.singletonMap(
                "X-Test-Navigate-Header", "TestValue"
            ))
            .build();

        // 4. Initiate navigation on the UI thread
        mCurrentNavigation = WebViewCompat.navigate(mWebView, url, params);
    }
}

एचटीटीपी हेडर का इस्तेमाल करके ऐप्लिकेशन की स्थिति को आगे बढ़ाना

वेब ऐप्लिकेशन को अक्सर होस्ट करने वाले Android ऐप्लिकेशन से कॉन्टेक्स्ट की ज़रूरत होती है, ताकि बैकएंड लॉजिक को मैनेज किया जा सके या वेब कॉन्टेंट को पसंद के मुताबिक बनाया जा सके. इस जानकारी को भेजने के लिए, यूआरएल में क्वेरी पैरामीटर जोड़ने से यूआरएल में काफ़ी डेटा इकट्ठा हो सकता है. साथ ही, इससे कैश मेमोरी में सेव करने की प्रोसेस में रुकावट आ सकती है और ऐप्लिकेशन की इंटरनल स्थिति का पता चल सकता है.

इसके बजाय, हमारा सुझाव है कि कस्टम एचटीटीपी हेडर का इस्तेमाल करके, ऐप्लिकेशन का कॉन्टेक्स्ट पास करें. WebViewCompat.navigate और NavigationParameters का इस्तेमाल करके, इस डेटा को अपने सर्वर पर सुरक्षित तरीके से भेजा जा सकता है. इसके अलावा, WebView इन हेडर को स्टेट रीस्टोर करने के दौरान सेव रखता है. इससे यह पक्का होता है कि कॉन्फ़िगरेशन में बदलाव होने पर भी, वेब कॉन्टेंट एक जैसा बना रहे. ध्यान दें कि यह सुविधा सिर्फ़ WebViewCompat.navigate का इस्तेमाल करते समय लागू होती है. WebView.loadUrl का इस्तेमाल करने पर, कस्टम हेडर WebView स्टेट बंडल में सेव नहीं होते हैं. साथ ही, इन्हें वापस लाने पर ये मिट जाते हैं.

इस्तेमाल के सामान्य उदाहरण

होस्ट ऐप्लिकेशन का कॉन्टेक्स्ट पास करने के सामान्य इस्तेमाल के उदाहरणों में ये शामिल हैं:

  • ऐप्लिकेशन का वर्शन (X-App-Version): होस्ट ऐप्लिकेशन का रिलीज़ वर्शन (जैसे कि BuildConfig.VERSION_NAME) पास करने से, आपके बैकएंड सर्वर को नेटिव JavaScript ब्रिज की कंपैटिबिलिटी की पुष्टि करने, गेट की गई सुविधाओं को चालू करने या उपयोगकर्ताओं को पुराने ऐप्लिकेशन अपडेट करने के लिए सूचना देने में मदद मिलती है.
  • क्लाइंट प्लैटफ़ॉर्म (X-Client-Platform): होस्ट एनवायरमेंट को Android के तौर पर साफ़ तौर पर पहचानना. इससे सर्वर, User-Agent स्ट्रिंग पार्सिंग पर भरोसा किए बिना, प्लैटफ़ॉर्म के हिसाब से यूज़र इंटरफ़ेस (यूआई) या रूट स्टोर के लिंक डिलीवर कर पाता है.

लागू करने का उदाहरण

यहां दिए गए उदाहरण में, वेब सर्वर को ऐप्लिकेशन वर्शन और क्लाइंट प्लैटफ़ॉर्म पास करने का तरीका बताया गया है:

Kotlin

// Attach host app metadata so the server can verify compatibility and tailor content
val params = NavigationParameters.Builder()
    .addAdditionalHeaders(
        mapOf(
            "X-App-Version" to BuildConfig.VERSION_NAME,
            "X-Client-Platform" to "Android"
        )
    )
    .build()

// Use navigate instead of loadUrl to retain custom headers across state restoration
WebViewCompat.navigate(webView, "https://www.example.com", params)

Java

// Attach host app metadata so the server can verify compatibility and tailor content
Map<String, String> headers = new HashMap<>();
headers.put("X-App-Version", BuildConfig.VERSION_NAME);
headers.put("X-Client-Platform", "Android");

NavigationParameters params = new NavigationParameters.Builder()
    .addAdditionalHeaders(headers)
    .build();

// Use navigate instead of loadUrl to retain custom headers across state restoration
WebViewCompat.navigate(webView, "https://www.example.com", params);

गड़बड़ी के मोड और गड़बड़ी ठीक करना

WebViewCompat.navigate API, कॉन्फ़िगरेशन से जुड़ी गड़बड़ियों और रनटाइम नेविगेशन की समस्याओं को ठीक करने के लिए अलग-अलग तरीके उपलब्ध कराता है:

अमान्य आर्ग्युमेंट से जुड़ी अपवाद स्थितियां

अमान्य आर्ग्युमेंट पास करने पर, सिंक्रोनस IllegalArgumentException ट्रिगर होता है. इसकी सामान्य वजहें ये हैं:

  • ज़रूरी नॉन-नल पैरामीटर (webView, url या params) के लिए null पास करना.
  • ऐसी यूआरएल स्कीम देना जो काम नहीं करती, जैसे कि javascript:.
  • एचटीटीपी हेडर की ऐसी कुंजियां या वैल्यू पास करना जो RFC 2616 की खास बातों के मुताबिक नहीं हैं.

अगर नेटवर्क अनुरोध या पेज लोड होने के दौरान कोई गड़बड़ी होती है (जैसे कि एचटीटीपी 404 स्टेटस कोड, डीएनएस रिज़ॉल्यूशन में गड़बड़ी या एसएसएल गड़बड़ी), तो WebViewCompat.navigate अब भी एक मान्य Navigation ऑब्जेक्ट दिखाता है.

नेविगेशन पूरा होने के बाद, onNavigationCompleted कॉलबैक के अंदर मौजूद Navigation इंस्टेंस पर इन तरीकों की जांच करें, ताकि समस्या का पता लगाया जा सके:

  • getStatusCode: एचटीटीपी रिस्पॉन्स का स्टेटस कोड दिखाता है. उदाहरण के लिए, 404 या 500.
  • getWebResourceError: यह WebResourceErrorCompat ऑब्जेक्ट दिखाता है. इसमें नेटवर्क से जुड़ी गड़बड़ियों के बारे में जानकारी होती है. जैसे, कनेक्शन टाइम आउट या होस्ट लुकअप में गड़बड़ियां.
  • didCommitErrorPage: इससे पता चलता है कि WebView ने गड़बड़ी की है और उपयोगकर्ता को गड़बड़ी वाला पेज दिखाया है.
  • didCommit: इससे पता चलता है कि नेविगेशन को बिना रोके, टारगेट पेज पर ले जाया गया या नहीं.

सेव किए गए स्टेट बंडल को मैनेज करने की सुविधा

NavigationParameters के साथ अतिरिक्त हेडर पास करने पर, WebView इन हेडर को सेव किए गए स्टेट बंडल में सेव करता है, ताकि स्टेट को वापस लाने पर इनका फिर से इस्तेमाल किया जा सके. हालांकि, हेडर के बड़े कलेक्शन से सेव की गई स्थिति Bundle का साइज़ काफ़ी बढ़ सकता है.

अगर आपको Android के सेव किए गए स्टेटस के दौरान TransactionTooLargeException को रोकने के लिए, बंडल के साइज़ को सीमित करना है, तो WebViewCompat.saveState का इस्तेमाल करें. इस तरीके से, बंडल के साइज़ की ज़्यादा से ज़्यादा सीमा को बाइट में सेट किया जा सकता है. साथ ही, आपके पास आगे बढ़ने के इतिहास के आइटम को शामिल न करने का विकल्प भी होता है:

Kotlin

// Save state with a maximum bundle size limit (for example, 64 KB)
val maxSizeBytes = 64 * 1024
val includeForwardState = false
val outState = Bundle()

WebViewCompat.saveState(webView, outState, maxSizeBytes, includeForwardState)

Java

// Save state with a maximum bundle size limit (for example, 64 KB)
int maxSizeBytes = 64 * 1024;
boolean includeForwardState = false;
Bundle outState = new Bundle();

WebViewCompat.saveState(webView, outState, maxSizeBytes, includeForwardState);

इसके बाद, जो बंडल बनता है वह स्टैंडर्ड WebView.restoreState तरीके के साथ काम करता है.

माइग्रेशन और लागू करने से जुड़े सुझाव

WebView में नेविगेट करते समय, बेहतर परफ़ॉर्मेंस और स्थिरता बनाए रखने के लिए, इन सुझावों को अपनाएं:

  • loadUrl से navigate पर माइग्रेट करना: लेगसी WebView.loadUrl से किए गए सभी कॉल को WebViewCompat.navigate पर माइग्रेट करें. इससे यह पक्का होता है कि इतिहास को एक जैसा मैनेज किया जाए. साथ ही, यह भी पक्का होता है कि हेडर हमेशा सेव किए गए स्टेटस के हिस्से के तौर पर सेव किए जाएं.

  • हमेशा पुष्टि करें कि सुविधा काम करती है या नहीं: एपीआई को शुरू करने से पहले, WebViewFeature.isFeatureSupported की मदद से पुष्टि करें कि रनटाइम सपोर्ट उपलब्ध है. इससे WebView के पुराने वर्शन से होने वाले नुकसान से बचा जा सकता है.

  • नेविगेशन इंस्टेंस को आपस में जोड़ना: एक साथ कई WebView इंस्टेंस मैनेज करते समय, एक साथ होने वाले नेविगेशन में अंतर करने या फ़िल्टर कॉलबैक के लिए, दिखाए गए Navigation ऑब्जेक्ट का इस्तेमाल करें.

  • शुरुआत के दौरान, लिसनर को एक बार रजिस्टर करें: ऐसा इसलिए, क्योंकि WebViewCompat.addNavigationListener मौजूदा लिसनर को बदलने के बजाय, एक नया लिसनर जोड़ता है. इसलिए, मेमोरी लीक और बाद के नेविगेशन के दौरान डुप्लीकेट कॉलबैक एक्ज़ीक्यूशन से बचने के लिए, WebView सेटअप के दौरान अपने NavigationListener को एक बार रजिस्टर करें.

  • सेव की गई स्थिति के साइज़ पर नज़र रखें: बड़े हेडर पेलोड पास करते समय, साइज़ की सीमाएं तय करने के लिए WebViewCompat.saveState का इस्तेमाल करें, ताकि ज़्यादा स्टेट डेटा सेव न हो.

अन्य संसाधन

एम्बेड की गई वेब क्षमताओं और परफ़ॉर्मेंस ऑप्टिमाइज़ेशन के बारे में ज़्यादा जानने के लिए, यहां दी गई गाइड देखें: