WebView অবজেক্ট ম্যানেজ করা

Android আপনাকে WebView আপনার অ্যাপে ওয়েব কন্টেন্ট দেখানোর অবজেক্ট ম্যানেজ করতে সাহায্য করার জন্য একাধিক API প্রদান করে।

এই পৃষ্ঠায় বর্ণনা করা হয়েছে যে কীভাবে এইসব API ব্যবহার করে WebView অবজেক্টের সাথে আরও কার্যকরভাবে কাজ করা যায়, আপনার অ্যাপের স্থায়িত্ব ও নিরাপত্তা উন্নত করা যায়।

Version API

Android 7.0 (API লেভেল ২৪) থেকে শুরু করে, ব্যবহারকারীরা WebView অবজেক্টে ওয়েব কন্টেন্ট দেখানোর জন্য বিভিন্ন প্যাকেজ থেকে বেছে নিতে পারবেন। Jetpack Webkit লাইব্রেরিতে getCurrentWebViewPackage() পদ্ধতি অন্তর্ভুক্ত আছে যা আপনার অ্যাপে ওয়েব কন্টেন্ট দেখানো প্যাকেজ সম্পর্কিত তথ্য নিয়ে আসে। এই পদ্ধতিটি তখনই ত্রুটি বিশ্লেষণ করার জন্য উপযোগী হয় যখন আপনার অ্যাপ WebView-এর নির্দিষ্ট প্যাকেজ প্রয়োগ করে ওয়েব কন্টেন্ট দেখানোর চেষ্টা করে।

এই পদ্ধতি ব্যবহার করতে, নিম্নলিখিত কোড স্নিপেটে দেখানো লজিক যোগ করুন:

Kotlin

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

জাভা

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

Google Safe Browsing পরিষেবা

আপনার ব্যবহারকারীদের আরও নিরাপদ ব্রাউজিং অভিজ্ঞতা দিতে, WebView অবজেক্ট URL যাচাই করার জন্য Google Safe Browsing ব্যবহার করে, এর ফলে আপনার অ্যাপ ব্যবহারকারীদের সম্ভাব্য অসুরক্ষিত ওয়েবসাইটে নেভিগেট করার চেষ্টা করলে তাদের সতর্কতা দেখাতে পারে।

EnableSafeBrowsing-এর ডিফল্ট ভ্যালু true হলেও, এমন কিছু পরিস্থিতি তৈরি হতে পারে যখন আপনি শর্তসাপেক্ষে Safe Browsing চালু করতে বা বন্ধ করতে চাইতে পারেন। Android 8.0 (API লেভেল ২৬) এবং পরবর্তী ভার্সনে setSafeBrowsingEnabled() ব্যবহার করে কোনও নির্দিষ্ট WebView অবজেক্টের জন্য 'নিরাপদ ব্রাউজিং' টগল করা যায়।

আপনি যদি চান যে সব WebView অবজেক্ট নিরাপদ ব্রাউজিং চেক থেকে অপ্ট-আউট করুক, তাহলে আপনার অ্যাপের ম্যানিফেস্ট ফাইলে নিম্নলিখিত <meta-data> এলিমেন্ট যোগ করুন:

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

প্রোগ্রাম্যাটিক অ্যাকশন সংজ্ঞায়িত করুন

WebView-এর কোনও ইনস্ট্যান্স যখন এমন কোনও পৃষ্ঠা লোড করার চেষ্টা করে যা Google-এর দ্বারা পরিচিত হুমকি হিসেবে শ্রেণীবদ্ধ করা হয়েছে, তখন WebView ডিফল্ট হিসেবে একটি ইন্টারস্টিশিয়াল দেখায় যা পরিচিত হুমকির ব্যাপারে ব্যবহারকারীদের সতর্ক করে। এই স্ক্রিন ব্যবহারকারীদের যেকোনও উপায়ে URL লোড করার বিকল্প দেয় অথবা নিরাপদ কোনও আগের পৃষ্ঠায় ফিরে যেতে দেয়।

আপনি Android 8.1 (API লেভেল 27) বা তার পরের কোনও ভার্সনকে টার্গেট করলে, আপনার অ্যাপ কীভাবে কোনও পরিচিত বিপদের ক্ষেত্রে প্রতিক্রিয়া জানাবে তা নিম্নলিখিত উপায়ে প্রোগ্রাম্যাটিক্যালি নির্ধারণ করতে পারবেন:

  • আপনার অ্যাপ, Safe Browsing-এ পরিচিত হুমকি সম্পর্কে রিপোর্ট করবে কিনা তা আপনি নিয়ন্ত্রণ করতে পারবেন।
  • আপনি নিজের অ্যাপকে কোনও নির্দিষ্ট অ্যাকশন অটোমেটিক পারফর্ম করার জন্য সেট করতে পারেন—যেমন পরিচিত থ্রেট হিসেবে ক্লাসিফাই করা কোনও URL-এর সম্মুখীন হলে নিরাপদ অবস্থায় ফিরে যাওয়া।

নিম্নলিখিত কোড স্নিপেট থেকে জানা যায় যে, কোনও পরিচিত বিপদ দেখলে আপনার অ্যাপের ইনস্ট্যান্সকে 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!")
            }
        })
    }
}

জাভা

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

জাভা

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

HTML5 Geolocation API

Android 6.0 (API লেভেল 23) ও তার পরবর্তী যেকোনও ভার্সনের জন্য তৈরি অ্যাপের ক্ষেত্রে, জিওলোকেশন API শুধুমাত্র HTTPS-এর মতো সুরক্ষিত অরিজিনে কাজ করে। অসুরক্ষিত অরিজিনে Geolocation API-তে করা যেকোনও অনুরোধ অটোমেটিক প্রত্যাখ্যান করা হয় এবং সংশ্লিষ্ট onGeolocationPermissionsShowPrompt() পদ্ধতি প্রয়োগ করা হয় না।

মেট্রিক সংগ্রহ করার সুবিধা থেকে বেরিয়ে আসা

ব্যবহারকারী সম্মতি দিলে, WebView-এর কাছে Google-এ পরিচয় গোপন রেখে ডায়াগনস্টিক ডেটা আপলোড করার ক্ষমতা থাকে। WebView ইনস্ট্যানশিয়েট করে এমন প্রতিটি অ্যাপের জন্য অ্যাপ-পিছু ডেটা সংগ্রহ করা হয়। আপনি ম্যানিফেস্টের <application> এলিমেন্টে নিম্নলিখিত ট্যাগ তৈরি করে এই ফিচার থেকে বেরিয়ে আসতে পারবেন:

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

ব্যবহারকারী সম্মতি দিলে এবং অ্যাপ অপ্ট-আউট না করলে তবেই অ্যাপ থেকে ডেটা আপলোড করা হয়। ডায়াগনস্টিক ডেটা রিপোর্টিং থেকে অপ্ট-আউট করা সম্পর্কে আরও জানতে, WebView রিপোর্টিংয়ে ব্যবহারকারীর গোপনীয়তা দেখুন।

Termination Handling API

টার্মিনেশন হ্যান্ডলিং API সেইসব কেস ম্যানেজ করে যেখানে কোনও WebView অবজেক্টের রেন্ডারার প্রসেস চলে যায়, এর কারণ হল সিস্টেম প্রয়োজনীয় মেমরি রিক্লেইম করার জন্য রেন্ডারারকে কিল করে অথবা রেন্ডারার প্রসেস ক্র্যাশ করে। এই API ব্যবহার করে, আপনি রেন্ডারার প্রসেস বন্ধ হয়ে গেলেও আপনার অ্যাপ এক্সিকিউট করা চালিয়ে যেতে পারবেন।

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

নিচে দেওয়া কোড স্নিপেট থেকে বোঝা যায় যে কীভাবে Activity-এর মধ্যে এই API ব্যবহার করতে হয়:

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

জাভা

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

Renderer Importance API

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

নিম্নলিখিত কোড স্নিপেট থেকে জানা যায় যে আপনার অ্যাপের WebView অবজেক্টের সাথে যুক্ত রেন্ডারার প্রসেসকে কীভাবে অগ্রাধিকার দিতে হয়:

Kotlin

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

জাভা

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

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

ওয়েব কন্টেন্ট ব্যবহার করার সময় আপনার অ্যাপের মেমরি ফুটপ্রিন্ট ডায়াগনসিস ও অপ্টিমাইজ করার ব্যাপারে আরও জানতে, WebView মেমরি ম্যানেজ ও ডায়াগনসিস করা দেখুন। প্রসেস জুড়ে সিস্টেম কীভাবে কম-মেমরি পরিস্থিতি ম্যানেজ করে সেই সম্পর্কে আরও জানতে, প্রসেস এবং অ্যাপ লাইফসাইকেল দেখুন।

স্টেট ও নেভিগেশন ইতিহাস সংরক্ষণ করুন

সিস্টেম ব্যাকগ্রাউন্ড রিসোর্স রিক্লেম করলে বা ডিভাইসের কনফিগারেশন পরিবর্তন হলে, অ্যাপের অ্যাক্টিভিটি এবং এর WebView অবজেক্ট নষ্ট হয়ে যেতে পারে। ব্যবহারকারীর নেভিগেশন কনটেক্সট সেভ করতে, saveState(Bundle) মেথড ব্যবহার করে onSaveInstanceState()-এর মধ্যে নেভিগেশন ইতিহাসকে Bundle-এ সিরিয়ালাইজ করুন এবং তারপর restoreState(Bundle) ব্যবহার করে সেটি ফিরিয়ে আনুন।

যেহেতু Android, savedInstanceState-এর জন্য পুরো প্রসেস জুড়ে ১ এমবি-র কঠোর সীমা এনফোর্স করে, তাই স্ট্যান্ডার্ড ফ্রেমওয়ার্ক API-এর মাধ্যমে বড় ব্রাউজিং সেশন সিরিয়ালাইজ করলে TransactionTooLargeException হতে পারে। সাইজের সীমা এনফোর্স করতে বা ফরওয়ার্ড ইতিহাস এন্ট্রি নিরাপদে ড্রপ করতে, Jetpack Webkit পদ্ধতি ব্যবহার করুন WebViewCompat.saveState()।

স্টেট দক্ষতার সাথে ম্যানেজ করার ব্যাপারে বিস্তারিত কৌশল ও পেশাদার পদ্ধতি জানতে, WebView স্টেট দক্ষতার সাথে ম্যানেজ করুন দেখুন।