वेबव्यू के स्टार्टअप को ऑप्टिमाइज़ करना

जब आपका ऐप्लिकेशन पहली बार किसी वेबव्यू का इस्तेमाल करता है, तो सिस्टम स्टार्टअप से जुड़े कुछ टास्क पूरे करता है. स्टार्टअप प्रोसेस में ज़्यादा समय लगता है. डिफ़ॉल्ट रूप से, यह यूज़र इंटरफ़ेस (यूआई) थ्रेड पर अपने-आप होता है. ऐसा तब होता है, जब ऐप्लिकेशन पहली बार android.webkit या androidx.webkit पैकेज में मौजूद कई एपीआई को कॉल करता है या WebView टैग वाला लेआउट बढ़ाता है.

यह ज़रूरी क्यों है

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

इंप्लिसिट स्टार्टअप के लिए ट्रिगर

इन्टेंट के आधार पर ऐप्लिकेशन अपने-आप शुरू होने की सुविधा को इन तरीकों से ट्रिगर किया जा सकता है:

  • प्रोग्राम के हिसाब से: WebSettings.getUserAgentString() जैसे एपीआई को कॉल करना.
  • लेआउट का इस्तेमाल करना: <WebView> को शामिल करने वाले एक्सएमएल संसाधन पर setContentView() या layoutInflater.inflate() को कॉल करना.

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

इस पेज पर, startUpWebView API का इस्तेमाल करके, WebView के स्टार्टअप की परफ़ॉर्मेंस को ऑप्टिमाइज़ करने का तरीका बताया गया है.

वेबव्यू के स्टार्टअप को कंट्रोल करना

परफ़ॉर्मेंस को बेहतर बनाने और एएनआर को कम करने के लिए, Jetpack Webkit लाइब्रेरी में उपलब्ध startUpWebView एपीआई का इस्तेमाल करें. इस एपीआई की मदद से, यह तय किया जा सकता है कि WebView कब शुरू हो. यह स्टार्टअप के ज़्यादातर वर्कलोड को बैकग्राउंड थ्रेड पर ले जाता है. साथ ही, यूज़र इंटरफ़ेस (यूआई) थ्रेड पर होने वाले किसी भी काम को एक बड़े मोनोलिथिक ब्लॉक के बजाय, छोटे-छोटे हिस्सों में पूरा करने की सुविधा देता है. इससे, यूज़र इंटरफ़ेस (यूआई) थ्रेड को अन्य ज़रूरी ऐप्लिकेशन टास्क को एक साथ मैनेज करने में मदद मिलती है. साथ ही, उपयोगकर्ता अनुभव को ब्लॉक करने की संभावना कम हो जाती है.

एपीआई, androidx.webkit.WebViewOutcomeReceiver कॉलबैक का इस्तेमाल करता है. इससे आपको शुरुआती प्रोसेस के पूरा होने की जानकारी मिलती है.

इस एपीआई का इस्तेमाल करने के लिए, अपनी build.gradle फ़ाइल में Jetpack Webkit लाइब्रेरी जोड़ें. पक्का करें कि आपने 1.16.0 या इसके बाद वाले वर्शन का इस्तेमाल किया हो:

dependencies {
    implementation("androidx.webkit:webkit:1.16.0")
}

startUpWebView API का इस्तेमाल करना

स्टार्टअप फ़्लो को ऑप्टिमाइज़ करने का तरीका इस बात पर निर्भर करता है कि आपके ऐप्लिकेशन को वेबव्यू कब दिखाना है.

जब WebView, ज़रूरी पाथ पर न हो

अगर आपके ऐप्लिकेशन को वेबव्यू को तुरंत लोड करने की ज़रूरत नहीं है, तो इनिशियलाइज़ेशन की लागत को पूरी तरह से छिपाया जा सकता है. ऐप्लिकेशन के लाइफ़साइकल की शुरुआत में ही startUpWebView को कॉल करें और सफलता से जुड़े कॉलबैक के ट्रिगर होने का इंतज़ार करें.

आमतौर पर, आपको अन्य WebView API को कॉल करने से पहले, कॉलबैक का इंतज़ार करना चाहिए. अगर आपने startUpWebView को ट्रिगर किया है, लेकिन अन्य WebView कॉम्पोनेंट को छूने से पहले इसके पूरा होने का इंतज़ार नहीं किया है, तो सिस्टम, यूज़र इंटरफ़ेस (यूआई) थ्रेड को तब तक ब्लॉक कर देता है, जब तक कि शुरू होने की प्रोसेस पूरी नहीं हो जाती. बैकग्राउंड में पहले से पूरा हो चुके काम की वजह से, आपके ऐप्लिकेशन की परफ़ॉर्मेंस में कुछ सुधार हो सकता है. हालांकि, इससे आपको ज़्यादा फ़ायदा नहीं मिलेगा.

जब वेबव्यू क्रिटिकल पाथ पर हो

अगर आपके ऐप्लिकेशन में उपयोगकर्ता के मुख्य अनुभव के लिए, वेबव्यू की तुरंत ज़रूरत होती है, तो शायद आपको वेबव्यू के स्टार्टअप के पूरा होने का इंतज़ार नहीं करना चाहिए. इस स्थिति में, आपको ऐप्लिकेशन के लाइफ़साइकल में startUpWebView को जल्द से जल्द कॉल करना चाहिए. जैसे, Application.onCreate में. हालांकि, कॉल बैक के ट्रिगर होने का इंतज़ार न करें. इसके बजाय, जब ज़रूरी हो, तब सीधे तौर पर WebView API का इस्तेमाल करें.

एसिंक्रोनस स्टार्टअप का ज़्यादा से ज़्यादा फ़ायदा पाने के लिए, WebView को इंस्टैंटिएट करने या WebView API को तब तक कॉल न करें, जब तक कि यूज़र इंटरफ़ेस (यूआई) थ्रेड के अन्य ज़रूरी पाथ ऑपरेशन पूरे न हो जाएं. जैसे, लेआउट हैरारकी को बड़ा करना, अन्य एसडीके शुरू करना या शुरुआती फ़्रेम बनाना.

अगर आपने startUpWebView को कॉल किया है और इसके तुरंत बाद मुख्य थ्रेड पर WebView API को शुरू किया है, तो यूज़र इंटरफ़ेस (यूआई) थ्रेड, शुरू होने की प्रोसेस पूरी होने तक ब्लॉक हो जाती है. इस उदाहरण में, परफ़ॉर्मेंस में कोई सुधार नहीं हुआ है.

अगर वेबव्यू का इस्तेमाल, मुख्य पाथ पर किया जा सकता है, लेकिन आपको वेबव्यू को पूरी तरह से स्टार्टअप नहीं करना है, तो वेबव्यू के स्टार्टअप टास्क को चुनिंदा तौर पर चलाने का विकल्प चुना जा सकता है. ये टास्क, बैकग्राउंड थ्रेड पर चल सकते हैं. इससे, यूज़र इंटरफ़ेस (यूआई) थ्रेड को ऐप्लिकेशन के अन्य ज़रूरी टास्क के लिए खाली किया जा सकता है. इसके लिए, shouldRunUiThreadStartUpTasks(false) का इस्तेमाल किया जा सकता है.

ऐप्लिकेशन के लाइफ़साइकल में बाद में, यूज़र इंटरफ़ेस (यूआई) थ्रेड पर स्टार्टअप से जुड़े बाकी टास्क पूरे करने के लिए, shouldRunUiThreadStartUpTasks(true) के साथ startUpWebView को फिर से कॉल किया जा सकता है. उस समय कॉलबैक का इंतज़ार करना है या नहीं, यह इस बात पर निर्भर करता है कि वेबव्यू का इस्तेमाल क्रिटिकल पाथ पर है या नहीं.

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

एपीआई, androidx.webkit.WebViewOutcomeReceiver कॉलबैक का इस्तेमाल करता है. इससे आपको शुरुआती प्रोसेस के पूरा होने को ट्रैक करने या गड़बड़ी की जानकारी को मैनेज करने में मदद मिलती है.

अपने ऐप्लिकेशन के अलग-अलग हिस्सों से startUpWebView को कई बार कॉल करना सुरक्षित है. हमारा सुझाव है कि आप फिर से कोशिश करने वाले लूप को लागू न करें.

नीचे दिए गए कोड सैंपल में, एसिंक्रोनस तरीके से शुरू करने के लिए WebViewCompat.startUpWebView API का इस्तेमाल करने का तरीका बताया गया है.

Kotlin

import android.content.Context
import android.util.Log
import androidx.webkit.WebViewCompat
import androidx.webkit.WebViewOutcomeReceiver
import androidx.webkit.WebViewStartUpConfig
import androidx.webkit.WebViewStartUpResult
import androidx.webkit.WebViewStartupException
import java.util.concurrent.Executors

fun initializeWebView(context: Context) {
    // 1. Create a startup configuration specifying the background thread
    // that WebView will use to run its initialization tasks.
    val startUpConfig = WebViewStartUpConfig.Builder(
        Executors.newSingleThreadExecutor()
    ).build()

    // 2. Trigger WebView startup asynchronously
    WebViewCompat.startUpWebView(
        context,
        startUpConfig,
        object : WebViewOutcomeReceiver<WebViewStartUpResult, WebViewStartupException> {

            override fun onResult(result: WebViewStartUpResult) {
                // Success: The WebView has finished its background initialization.
                // This callback is guaranteed to be invoked on the UI thread.
                setupWebView()
            }

            override fun onError(error: WebViewStartupException) {
                // Failure: The initialization encountered a startup exception.
                Log.e("WebViewStartup", "Failed to initialize WebView", error)
            }
        }
    )
}

Java

import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewOutcomeReceiver;
import androidx.webkit.WebViewStartUpConfig;
import androidx.webkit.WebViewStartUpResult;
import androidx.webkit.WebViewStartupException;
import java.util.concurrent.Executors;

public void initializeWebView(Context context) {
    // 1. Create the startup configuration specifying the background thread pool
    // to handle internal non-UI initialization processes.
    WebViewStartUpConfig startUpConfig = new WebViewStartUpConfig.Builder(
            Executors.newSingleThreadExecutor()
    ).build();

    // 2. Trigger WebView startup asynchronously
    WebViewCompat.startUpWebView(
            context,
            startUpConfig,
            new WebViewOutcomeReceiver<WebViewStartUpResult, WebViewStartupException>() {

                @Override
                public void onResult(@NonNull WebViewStartUpResult result) {
                    // Success: The WebView has finished its background initialization.
                    // This callback is invoked directly on the UI thread.
                    setupWebView();
                }

                @Override
                public void onError(@NonNull WebViewStartupException error) {
                    // Failure: Handled using the concrete WebViewStartupException
                    Log.e("WebViewStartup", "Failed to initialize WebView", error);
                }
            }
    );
}

एसिंक्रोनस स्टार्टअप से जुड़ी समस्याओं को डीबग करना

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

  • तीसरे पक्ष की लाइब्रेरी या SDK टूल, ऐप्लिकेशन के लाइफ़साइकल की शुरुआत में ही शुरू हो जाते हैं.

  • ContentProviders को आपके APK में इंजेक्ट किया जाता है. इससे ऐप्लिकेशन के शुरू होने पर, WebView API ट्रिगर होते हैं.

  • लेआउट इन्फ़्लेशन या प्रोग्राम के हिसाब से कॉल (जैसे कि उपयोगकर्ता एजेंट स्ट्रिंग फ़ेच करना) जो उम्मीद से पहले हो जाते हैं.

इन अनचाहे इनिशियलाइज़ेशन के बारे में पता लगाने के लिए, WebViewStartUpResult ऑब्जेक्ट में ऑडिटिंग की सुविधाएं पहले से मौजूद होती हैं. इनकी मदद से यह पता लगाया जा सकता है कि ये कहां और क्यों हो रहे हैं:

  • getUiThreadBlockingStartUpLocations(): यह StartUpLocation ऑब्जेक्ट की एक सूची दिखाता है. ये ऑब्जेक्ट उन जगहों को दिखाते हैं जहां WebView स्टार्टअप टास्क ने मुख्य यूज़र इंटरफ़ेस (यूआई) थ्रेड को ब्लॉक किया था.

  • getNonUiThreadBlockingStartUpLocations(): यह उन साइटों की जानकारी देता है जहां स्टार्टअप टास्क चलाने की वजह से बैकग्राउंड थ्रेड ब्लॉक हो गई हैं.

हर StartUpLocation में एक स्टैक ट्रेस होता है. इसे लॉग किया जा सकता है या इसकी जांच की जा सकती है. इससे आपको उस क्लास और तरीके के बारे में सटीक जानकारी मिलती है जिसने इनिशियलाइज़ेशन को ट्रिगर किया है.

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

अपने स्टार्टअप पाथ की ऑडिट करने के लिए, onResult कॉलबैक में इन जगहों की जांच की जा सकती है:

override fun onResult(result: WebViewStartUpResult) {
    // Check if WebView startup was blocked on the UI thread prior to or during initialization
    val uiBlockingLocations = result.getUiThreadBlockingStartUpLocations()
    if (!uiBlockingLocations.isNullOrEmpty()) {
        for (location in uiBlockingLocations) {
            // Log the stack trace of the call site that triggered the UI-blocking startup
            Log.w("WebViewDebug", "WebView startup blocked the UI thread here:", location.getStack())
        }
    } else {
        Log.i("WebViewDebug", "Excellent! No UI-blocking WebView startup detected.")
    }

    // Check where background initialization tasks were executed
    val backgroundLocations = result.getNonUiThreadBlockingStartUpLocations()
    backgroundLocations?.forEach { location ->
        Log.d("WebViewDebug", "WebView background startup occurred at: ${location.getStack()}")
    }

    setupWebView()
}

ऑडिट के दौरान इस डेटा का इस्तेमाल कैसे करें

अपने ऐप्लिकेशन के WebView स्टार्टअप की ऑडिट करते समय, गड़बड़ी की जानकारी वाले डेटा का विश्लेषण करने और परफ़ॉर्मेंस से जुड़ी समस्याओं को ठीक करने के लिए, इन रणनीतियों का इस्तेमाल करें:

  • अचानक दिखने वाले स्टैक ट्रेस देखें: अगर getUiThreadBlockingStartUpLocations() खाली नहीं है, तो प्रिंट किए गए स्टैक ट्रेस देखें. अगर आपको तीसरे पक्ष के एसडीके या अनचाहे कॉम्पोनेंट से जुड़ी क्लास दिखती हैं, तो इसका मतलब है कि आपको इंप्लिसिट इनिशियलाइज़ेशन की समस्या मिली है.

  • कॉल के क्रम की पुष्टि करें: अगर आपके लॉग आउटपुट से पता चलता है कि मैन्युअल startUpWebView कॉल से पहले, इंप्लिसिट इनिशियलाइज़ेशन हुआ था, तो आपको अपने ऐप्लिकेशन में startUpWebView इनिशियलाइज़ेशन को पहले ले जाना चाहिए. इसके अलावा, समस्या पैदा करने वाले SDK को कॉन्फ़िगर करके, WebView पर निर्भर टास्क को कुछ समय के लिए टाला जा सकता है.

पिछले समाधानों से माइग्रेट करना

ऐसा हो सकता है कि आपने पहले बैकग्राउंड थ्रेड पर WebView को चालू करने के लिए, कुछ खास तरीकों का इस्तेमाल किया हो. जैसे, उपयोगकर्ता एजेंट स्ट्रिंग को फ़ेच करना.

इन तरीकों को ऐसे तरीके माना जाता है जिनका इस्तेमाल नहीं किया जा सकता. साथ ही, आने वाली रिलीज़ में इनके काम करने के तरीके में बदलाव हो सकता है. अगर आपका ऐप्लिकेशन, WebView स्टार्टअप को ट्रिगर या मैनेज करने के लिए किसी ऐसे तरीके पर निर्भर करता है जिसके बारे में साफ़ तौर पर नहीं बताया गया है, तो हमारा सुझाव है कि आप startUpWebView API का इस्तेमाल करें. startUpWebView एपीआई, Android और WebView के उन सभी वर्शन पर काम करता है जो Jetpack Webkit लाइब्रेरी के साथ काम करते हैं.

Jetpack Webkit को लागू करने से, यह पक्का करने में मदद मिलती है कि पूरे Android नेटवर्क में एक जैसा व्यवहार हो. इस एपीआई का मुख्य फ़ायदा यह है कि यह भरोसेमंद है. पुराने डिवाइसों पर, जहां नए ऑप्टिमाइज़ेशन उपलब्ध नहीं हैं वहां यह एपीआई, मैन्युअल तरीके से किए जाने वाले काम के मुकाबले बेहतर परफ़ॉर्म करता है. इससे आपको नए डिवाइसों पर, स्टार्टअप के आधुनिक फ़ायदे मिलते हैं. साथ ही, पुराने डिवाइसों पर परफ़ॉर्मेंस से जुड़ी कोई समस्या नहीं होती.

अगर आपको startUpWebView API से जुड़ी कोई समस्या आती है या आपको इसके बारे में कोई सुझाव/राय देनी है या शिकायत करनी है, तो सार्वजनिक समस्या ट्रैकर पर गड़बड़ी की शिकायत दर्ज करें.