এই পৃষ্ঠায় একটি WebView এর ওয়েব কন্টেন্ট এবং একটি হোস্ট অ্যান্ড্রয়েড অ্যাপ্লিকেশনের মধ্যে যোগাযোগ সহজ করার জন্য নেটিভ ব্রিজ (যা জাভাস্ক্রিপ্ট ব্রিজ নামেও পরিচিত) স্থাপনের বিভিন্ন পদ্ধতি এবং সর্বোত্তম অনুশীলন নিয়ে আলোচনা করা হয়েছে।
এর ফলে ওয়েব ডেভেলপাররা জাভাস্ক্রিপ্ট ব্যবহার করে প্ল্যাটফর্মের নেটিভ ফিচারগুলো—যেমন ক্যামেরা, ফাইল সিস্টেম বা উন্নত হার্ডওয়্যার সেন্সর—অ্যাক্সেস করতে পারেন, যা সাধারণত স্ট্যান্ডার্ড ওয়েব এপিআইগুলো প্রদান করে না।
ব্যবহারের ক্ষেত্র
একটি জাভাস্ক্রিপ্ট ব্রিজ ইমপ্লিমেন্টেশন বিভিন্ন ইন্টিগ্রেশন সিনারিও সক্ষম করে, যেখানে ওয়েব কন্টেন্টের জন্য অ্যান্ড্রয়েড অপারেটিং সিস্টেমে আরও গভীর অ্যাক্সেসের প্রয়োজন হয়। নিচে এর কিছু উদাহরণ দেওয়া হলো:
- প্ল্যাটফর্ম ইন্টিগ্রেশন : একটি ওয়েব পেজ থেকে নেটিভ অ্যান্ড্রয়েড ইউআই কম্পোনেন্ট (যেমন, বায়োমেট্রিক প্রম্পট,
BottomSheetDialog) সক্রিয় করা। - পারফরম্যান্স : ভারী গণনার কাজগুলোকে নেটিভ জাভা বা কোটলিন কোডে অফলোড করা।
- ডেটা স্থায়িত্ব : স্থানীয় এনক্রিপ্টেড ডেটাবেস বা শেয়ার্ড প্রেফারেন্স অ্যাক্সেস করা।
- বৃহৎ ডেটা স্থানান্তর : অ্যাপ এবং ওয়েব রেন্ডারারের মধ্যে মিডিয়া ফাইল বা জটিল ডেটা কাঠামো আদান-প্রদান করা।
যোগাযোগ ব্যবস্থা
অ্যান্ড্রয়েড একটি নেটিভ সংযোগ স্থাপনের জন্য তিন প্রজন্মের প্রধান এপিআই (API) প্রদান করে। যদিও সেগুলো সবই এখনও উপলব্ধ, তবে নিরাপত্তা, ব্যবহারযোগ্যতা এবং পারফরম্যান্সের দিক থেকে এগুলোর মধ্যে উল্লেখযোগ্য পার্থক্য রয়েছে।
addWebMessageListener ব্যবহার করুন (সুপারিশকৃত)
ওয়েব কন্টেন্ট এবং নেটিভ অ্যাপ কোডের মধ্যে যোগাযোগের জন্য addWebMessageListener হলো সবচেয়ে আধুনিক এবং প্রস্তাবিত পদ্ধতি। এটি জাভাস্ক্রিপ্ট ইন্টারফেসের ব্যবহারের সহজতার সাথে মেসেজিং সিস্টেমের নিরাপত্তাকে একত্রিত করে।
এটি যেভাবে কাজ করে : অ্যাপটি একটি নির্দিষ্ট নাম এবং কিছু অনুমোদিত অরিজিন নিয়মসহ একটি লিসেনার যুক্ত করে। এরপর ওয়েবভিউ নিশ্চিত করে যে, পেজটি লোড হওয়া শুরু হওয়ার মুহূর্ত থেকেই জাভাস্ক্রিপ্ট অবজেক্টটি গ্লোবাল স্কোপে ( window.objectName ) উপস্থিত থাকে।
প্রারম্ভিকীকরণ : যেকোনো স্ক্রিপ্ট চলার আগে WebView-তে জাভাস্ক্রিপ্ট অবজেক্টটি যুক্ত হওয়া নিশ্চিত করতে, আপনাকে অবশ্যই loadUrl() কল করার আগে addWebMessageListener কল করতে হবে।
প্রধান বৈশিষ্ট্য :
নিরাপত্তা ও বিশ্বাসযোগ্যতা : প্রচলিত এপিআই-এর থেকে ভিন্ন, এই পদ্ধতিতে ইনিশিয়ালাইজেশনের সময়
allowedOriginRulesএর একটিSet<String>প্রয়োজন হয়। বিশ্বাসযোগ্যতা স্থাপনের এটিই প্রধান পদ্ধতি।যখন আপনি
https://example.comমতো একটি বিশ্বস্ত অরিজিন নির্দিষ্ট করেন, তখন WebView নিশ্চিত করে যে এটি ইনজেক্ট করা জাভাস্ক্রিপ্ট অবজেক্টগুলোকে শুধুমাত্র সেই নির্দিষ্ট অরিজিন থেকে লোড হওয়া ওয়েব পেজগুলোতেই প্রকাশ করবে।নেটিভ লিসেনার কলব্যাক প্রতিটি মেসেজের সাথে একটি
sourceOriginপ্যারামিটার গ্রহণ করে। আপনার ব্রিজ যদি একাধিক অনুমোদিত অরিজিন সমর্থন করে, তবে প্রেরকের সঠিক অরিজিন যাচাই করার জন্য আপনি এটি ব্যবহার করতে পারেন।যেহেতু WebView প্ল্যাটফর্ম স্তরে এই উৎস যাচাইকরণ কঠোরভাবে প্রয়োগ করে, তাই আপনার অ্যাপ সাধারণত একটি বিশ্বস্ত
sourceOriginথেকে প্রাপ্ত বার্তাগুলোকে সত্য বলে ধরে নিতে পারে, যা বেশিরভাগ প্রচলিত বাস্তবায়নে কঠোর পেলোড যাচাইকরণের প্রয়োজনীয়তা দূর করে।- WebView স্কিম (HTTP/HTTPS), হোস্ট এবং পোর্টের সাথে নিয়মগুলো মিলিয়ে দেখে।
- WebView পাথ উপেক্ষা করে। উদাহরণস্বরূপ,
https://example.comমাধ্যমেhttps://example.com/loginএবংhttps://example.com/homeগ্রহণ করা হয়। - WebView সাবডোমেনের ক্ষেত্রে ওয়াইল্ডকার্ডের ব্যবহার কঠোরভাবে শুধুমাত্র হোস্টের শুরুতে সীমাবদ্ধ রাখে। উদাহরণস্বরূপ,
https://*.example.comhttps://foo.example.comসাথে মেলে কিন্তুhttps://example.comসাথে মেলে না। যদি আপনারhttps://example.comএবং এর সাবডোমেন উভয়কেই মেলানোর প্রয়োজন হয়, তবে আপনাকে অবশ্যই প্রতিটি অরিজিন রুল আলাদাভাবে allowlist-এ যোগ করতে হবে (উদাহরণস্বরূপ,"https://example.com", "https://*.example.com")। আপনি স্কিমের জন্য বা কোনো ডোমেনের মাঝখানে ওয়াইল্ডকার্ড ব্যবহার করতে পারবেন না।
এটি ব্রিজটিকে যাচাইকৃত ডোমেইনের মধ্যে সীমাবদ্ধ রাখে, ফলে অননুমোদিত তৃতীয় পক্ষের কন্টেন্ট বা ইনজেক্টেড আইফ্রেম নেটিভ কোড এক্সিকিউট করতে পারে না।
মাল্টি-ফ্রেম সাপোর্ট : অরিজিন নিয়মের সাথে মেলে এমন সমস্ত ফ্রেমে কাজ করে।
থ্রেডিং : লিসেনার কলব্যাকটি অ্যাপ্লিকেশনটির প্রধান (UI) থ্রেডে চলে। যদি আপনার ব্রিজকে জটিল ডেটা প্রসেসিং, JSON পার্সিং, বা ডাটাবেস লুকআপ পরিচালনা করতে হয়, তবে "অ্যাপ নট রেসপন্ডিং" (ANR) ত্রুটির কারণে অ্যাপ্লিকেশন UI ফ্রিজ হয়ে যাওয়া রোধ করতে আপনাকে অবশ্যই সেই কাজটি একটি ব্যাকগ্রাউন্ড থ্রেডে অফলোড করতে হবে।
দ্বিমুখী : যখন ওয়েব পেজ কোনো বার্তা পাঠায়, তখন অ্যাপটি একটি
JavaScriptReplyProxyপায় যা ব্যবহার করে এটি সেই নির্দিষ্ট ফ্রেমে বার্তা ফেরত পাঠাতে পারে। আপনি এইreplyProxyঅবজেক্টটি সংরক্ষণ করতে পারেন এবং যেকোনো সময় এটি ব্যবহার করে পেজটিতে যেকোনো সংখ্যক বার্তা পাঠাতে পারেন, শুধু পেজটির পাঠানো প্রতিটি বার্তার উত্তর দেওয়ার জন্যই নয়। যদি বার্তা পাঠানোর মূল ফ্রেমটি অন্য কোথাও চলে যায় বা ধ্বংস হয়ে যায়, তাহলে প্রক্সিতেpostMessage()ব্যবহার করে পাঠানো বার্তাগুলো নীরবে উপেক্ষা করা হয়।অ্যাপ-সাইড সূচনা : যদিও ওয়েব পেজকেই সর্বদা অ্যাপের সাথে যোগাযোগ চ্যানেল শুরু করতে হয়, নেটিভ অ্যাপটি একতরফাভাবে ওয়েব পেজকে এই প্রক্রিয়াটি শুরু করার জন্য অনুরোধ করতে পারে। নেটিভ অ্যাপটি
addDocumentStartJavaScript()(পেজ লোড হওয়ার আগে জাভাস্ক্রিপ্ট মূল্যায়ন করতে) অথবাevaluateJavaScript()(পেজ লোড হওয়ার পরে জাভাস্ক্রিপ্ট মূল্যায়ন করতে) ব্যবহার করে ওয়েব পেজের সাথে যোগাযোগ করতে পারে।
সীমাবদ্ধতা : এই API ডেটা স্ট্রিং অথবা byte[] অ্যারে হিসেবে পাঠায়। JSON অবজেক্টের মতো আরও জটিল ডেটা স্ট্রাকচারের ক্ষেত্রে, আপনাকে প্রথমে এটিকে ওই ফরম্যাটগুলোর কোনো একটিতে সিরিয়ালাইজ করতে হবে এবং তারপর ডেটা স্ট্রাকচারটি পুনর্গঠন করার জন্য অন্য প্রান্তে ডিসিরিয়ালাইজ করতে হবে।
ব্যবহারের উদাহরণ :
একটি দ্বিমুখী বার্তা বিনিময়ের সম্পূর্ণ ক্রম বোঝার জন্য, ঘটনাগুলো এই ক্রমে ঘটে:
- সূচনা (অ্যাপ) : নেটিভ অ্যাপটি
addWebMessageListenerমাধ্যমে লিসেনারটি রেজিস্টার করে এবংloadUrl()এর মাধ্যমে ওয়েব পেজটি লোড করে। - বার্তা প্রেরণ (ওয়েব) : যোগাযোগ শুরু করার জন্য ওয়েব পেজের জাভাস্ক্রিপ্ট
myObject.postMessage(message)কল করে। - বার্তা গ্রহণ এবং উত্তর (অ্যাপ) : অ্যাপটি লিসেনার কলব্যাকে বার্তাটি গ্রহণ করে এবং প্রদত্ত
replyProxy.postMessage()ব্যবহার করে উত্তর দেয়। - উত্তর গ্রহণ (ওয়েব) : ওয়েব পেজটি
myObject.onmessage()কলব্যাক ফাংশনে অ্যাসিঙ্ক্রোনাস উত্তরটি গ্রহণ করে।
কোটলিন
val myListener = WebViewCompat.WebMessageListener { _, _, _, _, replyProxy ->
// Handle the message from JS
replyProxy.postMessage("Acknowledged!")
}
// Check whether the WebView version supports the feature.
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
val allowedOrigins = setOf("https://www.example.com")
WebViewCompat.addWebMessageListener(webView, "myObject", allowedOrigins, myListener)
}
জাভা
WebMessageListener myListener = (view, message, sourceOrigin, isMainFrame, replyProxy) -> {
// Handle the message from JS
replyProxy.postMessage("Acknowledged!");
};
// Check whether the WebView version supports the feature.
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
Set<String> allowedOrigins = Set.of("https://www.example.com");
WebViewCompat.addWebMessageListener(webView, "myObject", allowedOrigins, myListener);
}
নিম্নলিখিত জাভাস্ক্রিপ্টটি addWebMessageListener এর ক্লায়েন্ট-সাইড বাস্তবায়ন প্রদর্শন করে, যা ওয়েব কন্টেন্টকে নেটিভ অ্যাপ থেকে বার্তা গ্রহণ করতে এবং myObject প্রক্সির মাধ্যমে নিজস্ব বার্তা পাঠাতে সক্ষম করে।
myObject.onmessage = function(event) {
console.log("App says: " + event.data);
};
myObject.postMessage("Hello world!");
postWebMessage ব্যবহার করুন (বিকল্প)
ওয়েবের window.postMessage এর অনুরূপ একটি অ্যাসিঙ্ক্রোনাস, মেসেজিং-ভিত্তিক বিকল্প প্রদানের জন্য অ্যান্ড্রয়েড এটি চালু করেছে।
এটি যেভাবে কাজ করে : অ্যাপটি ওয়েব পেজের প্রধান ফ্রেমে একটি পেলোড পাঠাতে WebViewCompat.postWebMessage ব্যবহার করে। একটি দ্বিমুখী যোগাযোগ চ্যানেল স্থাপন করতে, আপনি একটি WebMessageChannel তৈরি করতে পারেন এবং এর একটি পোর্টের মাধ্যমে মেসেজটি ওয়েব কন্টেন্টে পাঠাতে পারেন।
বৈশিষ্ট্য :
- অ্যাসিঙ্ক্রোনাস :
addWebMessageListenerমতো, এই মেথডটিও অ্যাসিঙ্ক্রোনাস মেসেজিং ব্যবহার করে, যা নিশ্চিত করে যে অ্যাপটি ব্যাকগ্রাউন্ডে ডেটা প্রসেস করার সময়েও ওয়েব পেজটি ব্যবহারকারীর ইন্টারঅ্যাকশনের প্রতি রেসপন্সিভ থাকে। - অরিজিন অ্যাওয়্যার : আপনি একটি
targetOriginনির্দিষ্ট করে দিতে পারেন, যাতে ওয়েবভিউ শুধুমাত্র একটি বিশ্বস্ত ওয়েবসাইটে ডেটা সরবরাহ করে।
সীমাবদ্ধতা :
- পরিধি : এই API শুধুমাত্র মেইনফ্রেমের সাথে যোগাযোগ সীমাবদ্ধ করে। এটি সরাসরি আইফ্রেমকে সম্বোধন করা বা তাতে বার্তা পাঠানো সমর্থন করে না।
- URI সীমাবদ্ধতা :
data:URI,file:URI, বাloadData()ব্যবহার করে লোড করা কন্টেন্টের জন্য আপনি এই পদ্ধতিটি ব্যবহার করতে পারবেন না, যদি না আপনি টার্গেট অরিজিন হিসেবে "*" নির্দিষ্ট করেন। এটি করলে যেকোনো পৃষ্ঠা বার্তাটি গ্রহণ করতে পারে। - পরিচয় ঝুঁকি : ওয়েব কন্টেন্টের পক্ষে প্রেরকের পরিচয় যাচাই করার কোনো সুস্পষ্ট উপায় নেই। ওয়েব পেজটি যে বার্তাটি গ্রহণ করে, তা আপনার নেটিভ অ্যাপ বা অন্য কোনো আইফ্রেম থেকে আসতে পারে।
যেসব অ্যান্ড্রয়েড সংস্করণ addWebMessageListener সমর্থন করে না, সেগুলোতে স্ট্রিং-ভিত্তিক ডেটার জন্য একটি সহজ, অ্যাসিঙ্ক চ্যানেলের প্রয়োজন হলে এই পদ্ধতিটি ব্যবহার করুন।
addJavascriptInterface (Legacy) ব্যবহার করুন
সবচেয়ে পুরোনো পদ্ধতি হলো সরাসরি WebView-তে একটি নেটিভ অবজেক্ট ইনস্ট্যান্স ইনজেক্ট করা।
এটি যেভাবে কাজ করে : আপনি একটি Kotlin বা Java ক্লাস তৈরি করবেন, অনুমোদিত মেথডগুলোকে @JavascriptInterface দিয়ে টীকাযুক্ত করবেন, এবং addJavascriptInterface(Object, String) ব্যবহার করে ক্লাসটির একটি ইনস্ট্যান্স WebView-তে যোগ করবেন।
বৈশিষ্ট্য :
- সিনক্রোনাস : আপনার অ্যান্ড্রয়েড কোডের মেথডটি রিটার্ন না করা পর্যন্ত জাভাস্ক্রিপ্ট এক্সিকিউশন এনভায়রনমেন্ট ব্লক হয়ে থাকে।
- থ্রেড সেফটি : সিস্টেমটি একটি ব্যাকগ্রাউন্ড থ্রেডে মেথড কল করে, তাই কোটলিন বা জাভা সাইডে সতর্ক সিনক্রোনাইজেশন প্রয়োজন।
- নিরাপত্তা ঝুঁকি : ডিফল্টরূপে,
addJavascriptInterface`iframe` সহ `WebView`-এর ভেতরের প্রতিটি ফ্রেমে উপলব্ধ থাকে। এতে অরিজিন-ভিত্তিক অ্যাক্সেস কন্ট্রোলের অভাব রয়েছে। `WebView`-এর অ্যাসিঙ্ক্রোনাস আচরণের কারণে, কোন ফ্রেমটি আপনার ইন্টারফেসকে কল করছে তা নিরাপদে নির্ধারণ করা সম্ভব নয়। নিরাপত্তা যাচাইয়ের জন্য আপনারWebView.getUrl()এর মতো পদ্ধতির উপর নির্ভর করা উচিত নয়, কারণ এগুলোর নির্ভুলতার কোনো নিশ্চয়তা নেই এবং কোন নির্দিষ্ট ফ্রেমটি অনুরোধটি করেছে তাও নির্দেশ করে না।
প্রক্রিয়াগুলির সারাংশ
নিম্নলিখিত সারণিতে তিনটি প্রধান নেটিভ ব্রিজ বাস্তবায়ন পদ্ধতির একটি সংক্ষিপ্ত তুলনা দেওয়া হলো:
| পদ্ধতি | addWebMessageListener | postWebMessage | addJavascriptInterface |
|---|---|---|---|
| বাস্তবায়ন | অ্যাসিঙ্ক্রোনাস (প্রধান থ্রেডে লিসেনার) | অ্যাসিঙ্ক্রোনাস | সিঙ্ক্রোনাস |
| নিরাপত্তা | সর্বোচ্চ (অনুমতি তালিকা-ভিত্তিক) | উচ্চ (উৎস সচেতন) | নিম্ন (উৎস যাচাই করা হয়নি) |
| জটিলতা | মাঝারি | মাঝারি | সহজ |
| দিকনির্দেশনা | দ্বিমুখী | দ্বিমুখী | ওয়েব থেকে অ্যাপে |
| সর্বনিম্ন ওয়েবভিউ সংস্করণ | সংস্করণ ৮২ (এবং জেটপ্যাক ওয়েবকিট ১.৩.০) | সংস্করণ ৪৫ (এবং জেটপ্যাক ওয়েবকিট ১.১.০) | সমস্ত সংস্করণ |
| সুপারিশকৃত | হাঁ | না | না |
বৃহৎ ডেটা স্থানান্তর পরিচালনা করুন
মাল্টি-মেগাবাইট স্ট্রিং বা বাইনারি ফাইলের মতো বড় পেলোড স্থানান্তর করার সময়, ৩২-বিট ডিভাইসে অ্যাপ্লিকেশন নট রেসপন্ডিং (ANR) ত্রুটি বা ক্র্যাশ এড়াতে আপনাকে অবশ্যই মেমরি সাবধানে পরিচালনা করতে হবে। এই বিভাগে হোস্ট অ্যাপ্লিকেশন এবং ওয়েব কন্টেন্টের মধ্যে উল্লেখযোগ্য পরিমাণ ডেটা স্থানান্তরের সাথে সম্পর্কিত বিভিন্ন কৌশল এবং সীমাবদ্ধতা নিয়ে আলোচনা করা হয়েছে।
বাইট অ্যারের মাধ্যমে বাইনারি ডেটা স্থানান্তর করুন
WebMessageCompat ক্লাসের সাহায্যে, আপনি বাইনারি ডেটাকে Base64 স্ট্রিং-এ সিরিয়ালাইজ করার পরিবর্তে সরাসরি byte[] অ্যারে পাঠাতে পারেন। যেহেতু Base64 ডেটার আকারে প্রায় ৩৩% অতিরিক্ত জায়গা যোগ করে, তাই এই পদ্ধতিটি উল্লেখযোগ্যভাবে বেশি মেমোরি-সাশ্রয়ী এবং দ্রুততর।
- বাইনারি সুবিধা : আপনার নেটিভ অ্যাপ এবং ওয়েব কন্টেন্টের মধ্যে ইমেজ ফাইল বা অডিওর মতো বাইনারি ডেটা স্থানান্তর করুন।
- সীমাবদ্ধতা : বাইট অ্যারে ব্যবহার করলেও, সিস্টেম অ্যাপ এবং ওয়েবভিউ দ্বারা ওয়েব কন্টেন্ট রেন্ডার করার জন্য ব্যবহৃত বিচ্ছিন্ন প্রসেসের মধ্যেকার আন্তঃপ্রক্রিয়া যোগাযোগ (IPC) সীমানা অতিক্রম করে ডেটা কপি করে। খুব বড় ফাইলের ক্ষেত্রে এটি এখনও উল্লেখযোগ্য পরিমাণে মেমরি খরচ করে।
নিম্নলিখিত কোড উদাহরণগুলি দেখায় কিভাবে নেটিভ অ্যাপ সাইডে addWebMessageListener সেট আপ করতে হয়, যা WebMessageCompat.TYPE_ARRAY_BUFFER দ্বারা চিহ্নিত বার্তাগুলি গ্রহণ করতে পারে এবং ঐচ্ছিকভাবে WebViewFeature.MESSAGE_ARRAY_BUFFER পরীক্ষা করে বাইনারি ডেটা দিয়ে উত্তর দিতে পারে।
কোটলিন
fun setupWebView(webView: WebView) {
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
val listener = WebViewCompat.WebMessageListener { view, message, sourceOrigin, isMainFrame, replyProxy ->
// Check if the received message is an ArrayBuffer
if (message.type == WebMessageCompat.TYPE_ARRAY_BUFFER) {
val binaryData: ByteArray = message.arrayBuffer
// Process your binary data (image, audio, etc.)
println("Received bytes: ${binaryData.size}")
// Optional: Send a binary reply back to JavaScript.
// This example sends a 3-byte array for simplicity.
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER)) {
val replyBytes = byteArrayOf(0x01, 0x02, 0x03)
replyProxy.postMessage(replyBytes)
}
}
}
// "myBridge" matches the window.myBridge in JavaScript
WebViewCompat.addWebMessageListener(
webView,
"myBridge",
setOf("https://example.com"), // Security: restrict origins
listener
)
}
}
জাভা
public void setupWebView(WebView webView) {
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
WebViewCompat.WebMessageListener listener = (view, message, sourceOrigin, isMainFrame, replyProxy) -> {
// Check if the received message is an ArrayBuffer
if (message.getType() == WebMessageCompat.TYPE_ARRAY_BUFFER) {
byte[] binaryData = message.getArrayBuffer();
// Process your binary data (image, audio, etc.)
System.out.println("Received bytes: " + binaryData.length);
// Optional: Send a binary reply back to JavaScript.
// This example sends a 3-byte array for simplicity.
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER)) {
byte[] replyBytes = new byte[]{0x01, 0x02, 0x03};
replyProxy.postMessage(replyBytes);
}
}
};
// "myBridge" matches the window.myBridge in JavaScript
WebViewCompat.addWebMessageListener(
webView,
"myBridge",
Set.of("https://example.com"), // Security: restrict origins
listener
);
}
}
নিম্নলিখিত জাভাস্ক্রিপ্ট কোডটি addWebMessageListener এর ক্লায়েন্ট-সাইড বাস্তবায়ন প্রদর্শন করে, যা পূর্ববর্তী উদাহরণে ইনজেক্ট করা window.myBridge প্রক্সি ব্যবহার করে ওয়েব কন্টেন্টকে নেটিভ অ্যাপের সাথে বাইনারি ডেটা ( ArrayBuffer ) আদান-প্রদান করতে সক্ষম করে।
// Function to send an image or binary buffer to the app
async function sendBinaryToApp() {
const response = await fetch('image.jpg');
const buffer = await response.arrayBuffer();
// Check if the injected bridge object exists
if (window.myBridge) {
// You can send the ArrayBuffer directly
window.myBridge.postMessage(buffer);
}
}
// Receiving binary data from the app
if (window.myBridge) {
window.myBridge.onmessage = function(event) {
if (event.data instanceof ArrayBuffer) {
console.log('Received binary data from App, length:', event.data.byteLength);
// Process the binary data (for example, as a Uint8Array)
const bytes = new Uint8Array(event.data);
console.log('First byte:', bytes[0]);
}
};
}
দক্ষ বৃহৎ-স্কেল ডেটা লোডিং
খুব বড় ফাইলের (>10 MB) ক্ষেত্রে, ডেটা স্ট্রিম করতে shouldInterceptRequest মেথডটি ব্যবহার করুন:
- ওয়েব পেজটি একটি কাস্টম, প্লেসহোল্ডার ইউআরএল-এ
fetch()কল শুরু করে। উদাহরণস্বরূপ,https://app.local/large-file। - অ্যান্ড্রয়েড অ্যাপটি
WebViewClientএর মাধ্যমে এই অনুরোধটি গ্রহণ করে। - অ্যাপটি ডেটা
InputStreamহিসেবে ফেরত দেয়।
এর ফলে সম্পূর্ণ পেলোডকে একবারে মেমরিতে লোড না করে, ডেটাকে খণ্ড খণ্ড করে স্ট্রিম করা যায়।
নিম্নলিখিত জাভাস্ক্রিপ্ট ফাংশনটি একটি কাস্টম, প্লেসহোল্ডার URL-এ স্ট্যান্ডার্ড fetch() কল ব্যবহার করে নেটিভ অ্যাপ্লিকেশন থেকে দক্ষতার সাথে একটি বড় বাইনারি ফাইল লোড করার ক্লায়েন্ট-সাইড কোড প্রদর্শন করে।
async function fetchBinaryFromApp() {
try {
// This URL doesn't need to exist on the internet
const response = await fetch('https://app.local/data/large-file.bin');
if (!response.ok) throw new Error('Network response was not okay');
// For raw binary data:
const arrayBuffer = await response.arrayBuffer();
console.log('Received binary data, size:', arrayBuffer.byteLength);
// Process buffer (for example, new Uint8Array(arrayBuffer))
/*
// OR for an image:
const blob = await response.blob();
const imageUrl = URL.createObjectURL(blob);
document.getElementById('myImage').src = imageUrl;
*/
} catch (error) {
console.error('Fetch error:', error);
}
}
নিম্নলিখিত কোড উদাহরণগুলি নেটিভ অ্যাপ সাইড প্রদর্শন করে, যেখানে কোটলিন এবং জাভা উভয় ক্ষেত্রেই WebViewClient.shouldInterceptRequest পদ্ধতি ব্যবহার করে ওয়েব কন্টেন্ট দ্বারা অনুরোধ করা একটি কাস্টম প্লেসহোল্ডার URL ইন্টারসেপ্ট করার মাধ্যমে একটি বৃহৎ বাইনারি ফাইল স্ট্রিম করা হয়।
কোটলিন
webView.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView?,
request: WebResourceRequest?
): WebResourceResponse? {
val url = request?.url ?: return null
// Check if this is our custom placeholder URL
if (url.host == "app.local" && url.path == "/data/large-file.bin") {
try {
// 1. Get your data as an InputStream
// (from Assets, Files, or a generated byte stream)
val inputStream: InputStream = context.assets.open("my_data.pb")
// 2. Define Response Headers (Crucial for CORS/Fetch)
val headers = mutableMapOf<String, String>()
headers["Access-Control-Allow-Origin"] = "*" // Allow fetch from any origin
// 3. Return the response
return WebResourceResponse(
"application/octet-stream", // MIME type (for example, image/jpeg)
"UTF-8", // Encoding
200, // Status Code
"OK", // Reason Phrase
headers, // Custom Headers
inputStream // The actual data stream
)
} catch (e: Exception) {
// Handle exception
}
}
return super.shouldInterceptRequest(view, request)
}
}
জাভা
webView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
String urlPath = request.getUrl().getPath();
String host = request.getUrl().getHost();
// Check if this is our custom placeholder URL
if ("app.local".equals(host) && "/data/large-file.bin".equals(urlPath)) {
try {
// 1. Get your data as an InputStream
// (from Assets, Files, or a generated byte stream)
InputStream inputStream = getContext().getAssets().open("my_data.pb");
// 2. Define Response Headers (Crucial for CORS/Fetch)
Map<String, String> headers = new HashMap<>();
headers.put("Access-Control-Allow-Origin", "*"); // Allow fetch from any origin
// 3. Return the response
return new WebResourceResponse(
"application/octet-stream", // MIME type (for example, image/jpeg)
"UTF-8", // Encoding
200, // Status Code
"OK", // Reason Phrase
headers, // Custom Headers
inputStream // The actual data stream
);
} catch (Exception e) {
// Handle exception
}
}
return super.shouldInterceptRequest(view, request);
}
});
নিরাপত্তা সংক্রান্ত সুপারিশগুলো অনুসরণ করুন
আপনার অ্যাপ্লিকেশন এবং ব্যবহারকারীর ডেটা সুরক্ষিত রাখতে, একটি ব্রিজ বাস্তবায়ন করার সময় এই নির্দেশিকাগুলো অনুসরণ করুন:
HTTPS প্রয়োগ করুন : ক্ষতিকর তৃতীয় পক্ষের কন্টেন্ট যাতে আপনার অ্যাপ্লিকেশনের নেটিভ লজিক ব্যবহার করতে না পারে, তা নিশ্চিত করতে শুধুমাত্র সুরক্ষিত অরিজিনের সাথেই যোগাযোগের অনুমতি দিন।
অরিজিন রুলসের উপর নির্ভর করুন : বিশ্বাসযোগ্যতা বজায় রাখার সর্বোত্তম উপায় হলো আপনার
allowedOriginRulesকঠোরভাবে সংজ্ঞায়িত করা এবং মেসেজ কলব্যাকে প্রদত্তsourceOriginযাচাই করা। সম্পূর্ণ ওয়াইল্ডকার্ড (*), যা সমস্ত অরিজিনকে ম্যাচ করে, সেটিকে আপনার একমাত্র অরিজিন রুল হিসেবে ব্যবহার করা থেকে বিরত থাকুন, যদি না তা একান্তই প্রয়োজনীয় হয়। সাবডোমেনের জন্য ওয়াইল্ডকার্ড ব্যবহার (উদাহরণস্বরূপ,*.example.com) একাধিক সাবডোমেন (উদাহরণস্বরূপ,foo.example.com,bar.example.com) ম্যাচ করার ক্ষেত্রে বৈধ এবং নিরাপদ থাকে।দ্রষ্টব্য : যদিও অরিজিন রুলস ক্ষতিকর থার্ড-পার্টি ওয়েবসাইট এবং হিডেন আইফ্রেম থেকে সুরক্ষা দেয়, কিন্তু এটি আপনার নিজের ট্রাস্টেড ডোমেইনের মধ্যে থাকা ক্রস-সাইট স্ক্রিপ্টিং (XSS) দুর্বলতা থেকে সুরক্ষা দিতে পারে না। উদাহরণস্বরূপ, যদি আপনার ওয়েব পেজ ব্যবহারকারী-সৃষ্ট কন্টেন্ট প্রদর্শন করে এবং স্টোর্ড XSS-এর জন্য ঝুঁকিপূর্ণ হয়, তবে একজন আক্রমণকারী আপনার ট্রাস্টেড অরিজিন হিসেবে কাজ করে এমন একটি স্ক্রিপ্ট চালাতে পারে। সংবেদনশীল নেটিভ প্ল্যাটফর্ম অপারেশন চালানোর আগে মেসেজ পেলোডগুলিতে ভ্যালিডেশন প্রয়োগ করার কথা বিবেচনা করুন।
পৃষ্ঠতলের ক্ষেত্রফল হ্রাস করুন : শুধুমাত্র সেই নির্দিষ্ট পদ্ধতি বা ডেটা প্রদর্শন করুন যা ওয়েব পেজটির জন্য প্রয়োজন।
রানটাইমে ফিচারগুলো যাচাই করুন :
addWebMessageListenerসহ সাম্প্রতিক ব্রিজ এপিআইগুলো জেটপ্যাক ওয়েবকিট লাইব্রেরির অংশ। তাই, এগুলো কল করার আগে সর্বদাWebViewFeature.isFeatureSupported()ব্যবহার করে সাপোর্ট যাচাই করে নিন।