WebView স্টার্টআপ অপ্টিমাইজ করুন

যখন আপনার অ্যাপ প্রথমবার একটি WebView ব্যবহার করে, তখন সিস্টেম কিছু নির্দিষ্ট স্টার্টআপ টাস্ক সম্পাদন করে। এই স্টার্টআপ প্রক্রিয়াটি বেশ জটিল। ডিফল্টরূপে, অ্যাপ্লিকেশনটি যখন প্রথমবার android.webkit বা androidx.webkit প্যাকেজের মধ্যে থাকা অনেকগুলো API কল করে, অথবা WebView ট্যাগযুক্ত কোনো লেআউট ইনফ্লেট করে, তখন এই প্রক্রিয়াটি UI থ্রেডে পরোক্ষভাবে ঘটে থাকে।

কেন এটি গুরুত্বপূর্ণ

যেহেতু এই অন্তর্নিহিত স্টার্টআপটি সম্পূর্ণরূপে প্রধান থ্রেডে ঘটে, তাই এটি আপনার অ্যাপকে ব্যবহারকারীর ইনপুট প্রক্রিয়া করতে বাধা দেয় এবং অ্যাপ্লিকেশন নট রেসপন্ডিং (ANR) ত্রুটির ঝুঁকি মারাত্মকভাবে বাড়িয়ে দেয়। অ্যান্ড্রয়েড কীভাবে সিঙ্গেল-থ্রেডেড এক্সিকিউশন মডেল পরিচালনা করে সে সম্পর্কে আরও তথ্যের জন্য, প্রসেস এবং থ্রেড ওভারভিউ দেখুন।

অন্তর্নিহিত স্টার্টআপের জন্য ট্রিগার

নিম্নলিখিত উপায়ে ইমপ্লিসিট স্টার্টআপ চালু করা যেতে পারে:

  • প্রোগ্রামের মাধ্যমে : WebSettings.getUserAgentString() এর মতো API কল করা।
  • লেআউট ব্যবহার করা : এমন কোনো XML রিসোর্সের উপর setContentView() বা layoutInflater.inflate() কল করা, যেটিতে একটি <WebView> অন্তর্ভুক্ত থাকে।

ইমপ্লিসিট স্টার্টআপ আপনার ব্যবসায়িক মেট্রিকস, যেমন অ্যাপ চালু হওয়ার সময় এবং প্রথমবার প্রদর্শনের সময়কেও নেতিবাচকভাবে প্রভাবিত করতে পারে। যদি ইমপ্লিসিট ইনিশিয়ালাইজেশন আপনার অ্যাপের জন্য সর্বোত্তম না হয়, তবে এর পরিবর্তে startUpWebView ) ব্যবহার করুন।

এই পৃষ্ঠায় startUpWebView API ব্যবহার করে WebView-এর স্টার্টআপ পারফরম্যান্স অপ্টিমাইজ করার পদ্ধতি আলোচনা করা হয়েছে।

WebView স্টার্টআপের নিয়ন্ত্রণ নিন

পারফরম্যান্স উন্নত করতে এবং ANR (অ্যাক্টিভ নয়েজ রেসপন্স) কমাতে, Jetpack Webkit লাইব্রেরিতে উপলব্ধ startUpWebView API ব্যবহার করুন। এই API আপনাকে WebView কখন চালু হবে তার উপর সুস্পষ্ট নিয়ন্ত্রণ দেয়। এটি স্টার্টআপের কাজের একটি উল্লেখযোগ্য অংশ একটি ব্যাকগ্রাউন্ড থ্রেডে স্থানান্তর করে এবং UI থ্রেডে যে কাজগুলো অবশ্যই করতে হবে, সেগুলোকে একটি বড় একক ব্লকের পরিবর্তে খণ্ডে খণ্ডে সম্পন্ন করার সুযোগ দেয়। এর ফলে আপনার UI থ্রেড সমান্তরালভাবে অ্যাপের অন্যান্য গুরুত্বপূর্ণ কাজগুলো পরিচালনা করার জন্য মুক্ত থাকে, যা ব্যবহারকারীর অভিজ্ঞতা বাধাগ্রস্ত হওয়ার সম্ভাবনা কমিয়ে দেয়।

এপিআইটি androidx.webkit.WebViewOutcomeReceiver কলব্যাক ব্যবহার করে, যা আপনাকে সফল ইনিশিয়ালাইজেশন ট্র্যাক করতে দেয়।

এই API ব্যবহার করতে, আপনার build.gradle ফাইলে Jetpack Webkit লাইব্রেরিটি যোগ করুন। নিশ্চিত করুন যে আপনি সংস্করণ 1.16.0 বা তার উচ্চতর সংস্করণ ব্যবহার করছেন:

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

startUpWebView API ব্যবহার করুন

আপনার অ্যাপে ঠিক কখন WebView প্রদর্শন করার প্রয়োজন হবে, তার উপরই নির্ভর করে আপনি আপনার স্টার্টআপ ফ্লো কীভাবে অপ্টিমাইজ করবেন।

যখন WebView ক্রিটিক্যাল পাথে থাকে না

আপনার অ্যাপে যদি তাৎক্ষণিকভাবে WebView লোড করার প্রয়োজন না হয়, তবে আপনি ইনিশিয়ালাইজেশনের খরচ পুরোপুরি আড়াল করতে পারেন। আপনার অ্যাপের লাইফসাইকেলের শুরুতেই startUpWebView কল করুন এবং সফলতার কলব্যাকটি ফায়ার হওয়ার জন্য অপেক্ষা করুন।

আদর্শগতভাবে, অন্যান্য WebView API কল করার আগে আপনার কলব্যাকের জন্য অপেক্ষা করা উচিত। আপনি যদি startUpWebView ট্রিগার করেন কিন্তু অন্যান্য WebView কম্পোনেন্ট স্পর্শ করার আগে এটি শেষ হওয়ার জন্য অপেক্ষা না করেন, তাহলে সিস্টেম ইনিশিয়ালাইজেশন সম্পূর্ণ হওয়ার অপেক্ষায় UI থ্রেডকে ব্লক করে রাখে। ইতিমধ্যে সম্পন্ন হওয়া ব্যাকগ্রাউন্ড কাজ থেকে আপনার অ্যাপ কিছুটা পারফরম্যান্স সুবিধা পেতে পারে, কিন্তু সর্বোচ্চ সুবিধা নয়।

যখন WebView ক্রিটিক্যাল পাথে থাকে

যদি আপনার অ্যাপের মূল ইউজার জার্নির জন্য অবিলম্বে একটি WebView-এর প্রয়োজন হয়, তাহলে সম্ভবত WebView চালু হওয়া সম্পূর্ণ হওয়ার জন্য অপেক্ষা করা আপনার পক্ষে সম্ভব নয়। এই পরিস্থিতিতে, আপনার অ্যাপ লাইফসাইকেলে যত তাড়াতাড়ি সম্ভব (যেমন Application.onCreate এ) startUpWebView কল করা উচিত, কিন্তু কলব্যাক ট্রিগার হওয়ার জন্য অপেক্ষা করবেন না। এর পরিবর্তে, যখন প্রয়োজন হবে তখন সরাসরি WebView API ব্যবহার করুন।

অ্যাসিঙ্ক্রোনাস স্টার্টআপ থেকে সর্বাধিক সুবিধা পেতে, একটি WebView ইনস্ট্যানশিয়েট করা বা WebView API কল করাকে অত্যন্ত গুরুত্বপূর্ণভাবে ততক্ষণ পর্যন্ত স্থগিত রাখুন যতক্ষণ না চালানোর জন্য অন্য কোনো ক্রিটিক্যাল-পাথ UI থ্রেড অপারেশন বাকি থাকে (যেমন লেআউট হায়ারার্কি ইনফ্লেট করা, অন্যান্য SDK ইনিশিয়ালাইজ করা, বা প্রাথমিক ফ্রেম আঁকা)।

যদি আপনি startUpWebView কল করার পরপরই মেইন থ্রেডে `WebView` এপিআইগুলো ইনভোক করেন, তাহলে ইনিশিয়ালাইজেশন সম্পন্ন হওয়ার জন্য অপেক্ষা করতে গিয়ে UI থ্রেডটি ব্লক হয়ে যায়। এই পরিস্থিতিতে পারফরম্যান্সে কোনো সুবিধা হয় না।

যদি WebView-এর ব্যবহার ক্রিটিক্যাল পাথে পরিণত হতে পারে কিন্তু আপনি WebView সম্পূর্ণরূপে চালু করতে না চান, তাহলে আপনি ব্যাকগ্রাউন্ড থ্রেডে চলতে সক্ষম WebView স্টার্টআপ টাস্কগুলো বেছে বেছে চালাতে পারেন, যা UI থ্রেডকে অ্যাপের অন্যান্য গুরুত্বপূর্ণ কাজের জন্য মুক্ত করে দেবে। এর জন্য, আপনি shouldRunUiThreadStartUpTasks(false) ব্যবহার করতে পারেন।

আপনার অ্যাপের লাইফসাইকেলের পরবর্তী পর্যায়ে, UI থ্রেডে অবশিষ্ট স্টার্টআপ টাস্কগুলো শেষ করার জন্য আপনি shouldRunUiThreadStartUpTasks(true) সহ startUpWebView আবার কল করতে পারেন। সেই মুহূর্তে আপনি কলব্যাকের জন্য অপেক্ষা করবেন কিনা, তা নির্ভর করে WebView-এর ব্যবহার ক্রিটিক্যাল পাথে আছে কিনা তার উপর।

বাস্তবায়নের উদাহরণ

এই API-টি androidx.webkit.WebViewOutcomeReceiver কলব্যাক ব্যবহার করে, যার মাধ্যমে আপনি সফল ইনিশিয়ালাইজেশন ট্র্যাক করতে অথবা ডায়াগনস্টিক ব্যর্থতা সামলাতে পারেন।

আপনার অ্যাপের বিভিন্ন অংশ থেকে startUpWebView একাধিকবার কল করা নিরাপদ। আমরা একটি সাধারণ রিট্রাই লুপ ব্যবহার করা থেকে বিরত থাকার পরামর্শ দিই।

নিম্নলিখিত কোড নমুনাটি অ্যাসিঙ্ক্রোনাস ইনিশিয়ালাইজেশনের জন্য WebViewCompat.startUpWebView API কীভাবে ব্যবহার করতে হয় তা প্রদর্শন করে।

কোটলিন

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)
            }
        }
    )
}

জাভা

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-টি পরোক্ষভাবে ইনিশিয়ালাইজ হয়ে যাচ্ছে। এর নিম্নলিখিত কারণগুলো থাকতে পারে:

  • অ্যাপের জীবনচক্রের শুরুতেই থার্ড-পার্টি লাইব্রেরি বা এসডিকে চালু করা হয়।

  • আপনার APK-তে ইনজেক্ট করা ContentProviders অ্যাপ চালু হওয়ার সময় WebView API-গুলিকে ট্রিগার করে।

  • লেআউট স্ফীতি অথবা প্রোগ্রাম্যাটিক কল (যেমন ইউজার এজেন্ট স্ট্রিং আনা) যা অপ্রত্যাশিতভাবে সময়ের আগেই ঘটে।

এই অপ্রত্যাশিত প্রারম্ভিকীকরণগুলি কোথায় এবং কেন ঘটছে তা নির্ণয় করতে আপনাকে সাহায্য করার জন্য, WebViewStartUpResult অবজেক্টটি অন্তর্নির্মিত নিরীক্ষা ক্ষমতা প্রদান করে:

  • getUiThreadBlockingStartUpLocations() : StartUpLocation অবজেক্টের একটি তালিকা ফেরত দেয়, যা সেইসব স্থানকে নির্দেশ করে যেখানে WebView স্টার্টআপ টাস্কগুলো প্রধান UI থ্রেডকে ব্লক করেছিল।

  • 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() খালি না থাকে, তাহলে প্রিন্ট করা স্ট্যাক ট্রেসগুলো দেখুন। যদি আপনি থার্ড-পার্টি SDK-এর অন্তর্গত ক্লাস বা অপ্রত্যাশিত কম্পোনেন্ট দেখতে পান, তাহলে আপনি একটি ইমপ্লিসিট ইনিশিয়ালাইজেশন বটলনেক খুঁজে পেয়েছেন।

  • কলের ক্রম যাচাই করুন: যদি আপনার লগ আউটপুটে দেখা যায় যে আপনার ম্যানুয়াল startUpWebView কলের আগে একটি ইমপ্লিসিট ইনিশিয়ালাইজেশন ঘটেছে, তাহলে আপনার অ্যাপে startUpWebView ইনিশিয়ালাইজেশনটি আগে নিয়ে আসুন অথবা সমস্যা সৃষ্টিকারী SDK-টিকে তার WebView-নির্ভর কাজগুলো বিলম্বিত করার জন্য কনফিগার করুন।

পূর্ববর্তী বিকল্প পদ্ধতি থেকে স্থানান্তরিত করুন

অতীতে, আপনি হয়তো ব্যাকগ্রাউন্ড থ্রেডে WebView ইনিশিয়ালাইজেশন জোর করে করানোর জন্য কিছু সুস্পষ্ট পদ্ধতি ব্যবহার করে থাকতে পারেন, যেমন ইউজার এজেন্ট স্ট্রিং ফেচ করা।

এই বিকল্প পদ্ধতিগুলো এখন আর সমর্থিত নয়, এবং আসন্ন রিলিজগুলোতে এগুলোর অন্তর্নিহিত আচরণ পরিবর্তিত হতে পারে। যদি আপনার অ্যাপ WebView চালু বা পরিচালনা করার জন্য কোনো সুস্পষ্ট, নথিভুক্ত নয় এমন বিকল্প পদ্ধতির উপর নির্ভর করে, তবে আমরা এর পরিবর্তে startUpWebView API ব্যবহার করার পরামর্শ দিই। startUpWebView API-টি Jetpack Webkit লাইব্রেরি দ্বারা সমর্থিত Android এবং WebView-এর সকল সংস্করণে কাজ করে।

জেটপ্যাক ওয়েবকিট ইমপ্লিমেন্টেশন ব্যবহার করলে পুরো অ্যান্ড্রয়েড ইকোসিস্টেম জুড়ে সামঞ্জস্যপূর্ণ আচরণ নিশ্চিত করা যায়। এই এপিআই-এর একটি প্রধান সুবিধা হলো এর স্থিতিস্থাপকতা: পুরোনো ডিভাইসগুলোতে, যেখানে নতুন অপটিমাইজেশন উপলব্ধ নেই, সেখানে এপিআই ম্যানুয়াল ওয়ার্কঅ্যারাউন্ডের সাথে পারফরম্যান্সের সমতা বজায় রাখে। এর ফলে আপনি পুরোনো ডিভাইসগুলোতে পারফরম্যান্সের কোনো ক্ষতি ছাড়াই নতুন ডিভাইসগুলোতে আধুনিক স্টার্টআপের সুবিধাগুলো গ্রহণ করতে পারেন।

startUpWebView API নিয়ে আপনার কোনো সমস্যা হলে বা কোনো মতামত থাকলে, পাবলিক ইস্যু ট্র্যাকারে একটি বাগ রিপোর্ট করুন।