इस पेज पर, नेटिव ब्रिज (इसे JavaScript ब्रिज भी कहा जाता है) बनाने के अलग-अलग तरीकों और सबसे सही तरीकों के बारे में बताया गया है. इससे WebView में मौजूद वेब कॉन्टेंट और होस्ट किए गए Android ऐप्लिकेशन के बीच आसानी से कम्यूनिकेट किया जा सकता है.
इससे वेब डेवलपर, JavaScript का इस्तेमाल करके नेटिव प्लैटफ़ॉर्म की सुविधाओं को ऐक्सेस कर पाते हैं. जैसे, कैमरा, फ़ाइल सिस्टम या ऐडवांस हार्डवेयर सेंसर. ये सुविधाएं, स्टैंडर्ड वेब एपीआई आम तौर पर उपलब्ध नहीं कराते हैं.
इस्तेमाल के उदाहरण
JavaScript ब्रिज को लागू करने से, इंटिग्रेशन के अलग-अलग तरीके इस्तेमाल किए जा सकते हैं. इनमें वेब कॉन्टेंट को Android ऑपरेटिंग सिस्टम का ज़्यादा ऐक्सेस चाहिए होता है. यहां कुछ उदाहरण दिए गए हैं:
- प्लैटफ़ॉर्म इंटिग्रेशन: किसी वेब पेज से, Android के नेटिव यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट (उदाहरण के लिए, बायोमेट्रिक प्रॉम्प्ट,
BottomSheetDialog) को ट्रिगर करना. - परफ़ॉर्मेंस: कंप्यूटेशनल टास्क को नेटिव Java या Kotlin कोड पर ऑफ़लोड करना.
- डेटा परसिस्टेंस: एन्क्रिप्ट (सुरक्षित) किए गए लोकल डेटाबेस या शेयर की गई प्राथमिकताओं को ऐक्सेस करना.
- बड़े डेटा ट्रांसफ़र: ऐप्लिकेशन और वेब रेंडरर के बीच मीडिया फ़ाइलें या जटिल डेटा स्ट्रक्चर पास करना.
कम्यूनिकेशन के तरीके
Android, नेटिव ब्रिज बनाने के लिए तीन मुख्य जनरेशन के एपीआई उपलब्ध कराता है. ये सभी अब भी उपलब्ध हैं. हालांकि, सुरक्षा, इस्तेमाल करने में आसानी, और परफ़ॉर्मेंस के मामले में ये एक-दूसरे से काफ़ी अलग हैं.
addWebMessageListener का इस्तेमाल करें (सुझाया गया)
addWebMessageListener, वेब कॉन्टेंट और खास ऐप्लिकेशन कोड के बीच कम्यूनिकेशन करने का सबसे नया और सुझाया गया तरीका है. यह JavaScript इंटरफ़ेस को इस्तेमाल करने में आसान बनाता है. साथ ही, मैसेजिंग सिस्टम को सुरक्षित बनाता है.
यह कैसे काम करता है: ऐप्लिकेशन, एक लिसनर को किसी खास नाम और अनुमति वाले ऑरिजिन के नियमों के सेट के साथ जोड़ता है. इसके बाद, वेबव्यू यह पक्का करता है कि पेज लोड होना शुरू होने के बाद से ही, JavaScript ऑब्जेक्ट ग्लोबल स्कोप (window.objectName) में मौजूद हो.
शुरू करना: यह पक्का करने के लिए कि WebView, JavaScript ऑब्जेक्ट को किसी भी स्क्रिप्ट के चलने से पहले इंजेक्ट करे, आपको पेज पर नेविगेट करने से पहले addWebMessageListener को कॉल करना होगा. जैसे, WebViewCompat.navigate या loadUrl को कॉल करना.
मुख्य सुविधाएं:
सुरक्षा और भरोसा: लेगसी एपीआई के उलट, इस तरीके में शुरुआत के दौरान
allowedOriginRulesकेSet<String>की ज़रूरत होती है. यह भरोसा जीतने का मुख्य तरीका है.भरोसेमंद ऑरिजिन, जैसे कि
https://example.comतय करने पर, WebView यह गारंटी देता है कि वह इंजेक्ट किए गए JavaScript ऑब्जेक्ट को सिर्फ़ उसी ऑरिजिन से लोड किए गए वेब पेजों पर दिखाएगा.नेटिव लिसनर कॉलबैक को हर मैसेज के साथ
sourceOriginपैरामीटर मिलता है. अगर आपका ब्रिज, अनुमति वाले कई ऑरिजिन के साथ काम करता है, तो इस पैरामीटर का इस्तेमाल करके, भेजने वाले के ऑरिजिन की पुष्टि की जा सकती है.WebView, प्लैटफ़ॉर्म लेवल पर ओरिजिन की इन जांचों को सख्ती से लागू करता है. इसलिए, आपका ऐप्लिकेशन आम तौर पर भरोसेमंद
sourceOriginसे मिले मैसेज पर भरोसा कर सकता है. इससे ज़्यादातर स्टैंडर्ड लागू करने के लिए, पेलोड की पुष्टि करने की ज़रूरत नहीं होती.- WebView, स्कीम (एचटीटीपी/एचटीटीपीएस), होस्ट, और पोर्ट के हिसाब से नियमों का पालन करता है.
- WebView, पाथ को अनदेखा करता है. उदाहरण के लिए,
https://example.comमेंhttps://example.com/loginऔरhttps://example.com/homeकी अनुमति है. - WebView, सबडोमेन के लिए होस्ट की शुरुआत में वाइल्डकार्ड को सीमित करता है. उदाहरण के लिए,
https://*.example.com,https://foo.example.comसे मेल खाता है, लेकिनhttps://example.comसे नहीं. अगर आपकोhttps://example.comऔर इसके सबडोमेन, दोनों को मैच करना है, तो आपको अनुमति वाली सूची में हर ऑरिजिन के नियम को अलग से जोड़ना होगा. उदाहरण के लिए,"https://example.com", "https://*.example.com". स्कीम या डोमेन के बीच में वाइल्डकार्ड का इस्तेमाल नहीं किया जा सकता.
इससे ब्रिज को सिर्फ़ पुष्टि किए गए डोमेन तक सीमित किया जाता है. इससे, तीसरे पक्ष के ऐसे कॉन्टेंट या इंजेक्ट किए गए iFrame को नेटिव कोड को एक्ज़ीक्यूट करने से रोका जाता है जिन्हें अनुमति नहीं मिली है.
मल्टी-फ़्रेम के साथ काम करता है: यह उन सभी फ़्रेम पर काम करता है जो ऑरिजिन के नियमों के मुताबिक होते हैं.
थ्रेडिंग: लिसनर कॉलबैक, ऐप्लिकेशन के मुख्य (यूआई) थ्रेड पर चलता है. अगर आपके ब्रिज को डेटा प्रोसेसिंग, JSON पार्सिंग या डेटाबेस लुकअप जैसे मुश्किल काम करने हैं, तो आपको उस काम को बैकग्राउंड थ्रेड पर ऑफ़लोड करना होगा. इससे "ऐप्लिकेशन काम नहीं कर रहा" (एएनआर) वाली गड़बड़ी की वजह से, ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) को फ़्रीज़ होने से रोका जा सकेगा.
दोनों दिशाओं में: जब वेब पेज कोई मैसेज भेजता है, तो ऐप्लिकेशन को एक
JavaScriptReplyProxyमिलता है. इसका इस्तेमाल करके, वह उस फ़्रेम को वापस मैसेज भेज सकता है. इसreplyProxyऑब्जेक्ट को सेव करके रखा जा सकता है. इसका इस्तेमाल करके, पेज को कभी भी मैसेज भेजे जा सकते हैं. ऐसा सिर्फ़ पेज से मिले हर मैसेज का जवाब देने के लिए नहीं किया जाता. अगर ओरिजिन फ़्रेम बंद हो जाता है या उसे हटा दिया जाता है, तो प्रॉक्सी परpostMessage()का इस्तेमाल करके भेजे गए मैसेज को अनदेखा कर दिया जाता है.ऐप्लिकेशन से शुरू होने वाली प्रोसेस: वेब पेज को हमेशा ऐप्लिकेशन के साथ कम्यूनिकेशन चैनल शुरू करना चाहिए. हालांकि, नेटिव ऐप्लिकेशन, वेब पेज को इस प्रोसेस को शुरू करने के लिए एकतरफ़ा तौर पर प्रॉम्प्ट कर सकता है. खास ऐप्लिकेशन, वेब पेज से कम्यूनिकेट कर सकता है. इसके लिए,
addDocumentStartJavaScript()(पेज लोड होने से पहले JavaScript का आकलन करने के लिए) याevaluateJavaScript()(पेज लोड होने के बाद JavaScript का आकलन करने के लिए) का इस्तेमाल किया जा सकता है.
सीमा: यह एपीआई, डेटा को स्ट्रिंग या byte[] ऐरे के तौर पर भेजता है. JSON ऑब्जेक्ट जैसे ज़्यादा जटिल डेटा स्ट्रक्चर के लिए, आपको इसे इनमें से किसी एक फ़ॉर्मैट में क्रम से लगाना होगा. इसके बाद, डेटा स्ट्रक्चर को फिर से बनाने के लिए, दूसरी तरफ़ इसे क्रम से हटाना होगा.
इस्तेमाल का उदाहरण:
दोनों तरफ़ से मैसेज के आदान-प्रदान के पूरे क्रम को समझने के लिए, इवेंट इस क्रम में होते हैं:
- शुरुआत (ऐप्लिकेशन): खास ऐप्लिकेशन, लिसनर को
addWebMessageListenerके साथ रजिस्टर करता है और पेज नेविगेशन शुरू करता है. जैसे,WebViewCompat.navigateयाloadUrlके साथ. - मैसेज भेजने की सुविधा (वेब): वेब पेज के JavaScript कॉल
myObject.postMessage(message)कम्यूनिकेशन शुरू करने के लिए. - मैसेज पाना और जवाब देना (ऐप्लिकेशन): ऐप्लिकेशन को लिसनर कॉलबैक में मैसेज मिलता है और वह दिए गए
replyProxy.postMessage()का इस्तेमाल करके जवाब देता है. - जवाब मिलना (वेब): वेब पेज को एसिंक्रोनस जवाब,
myObject.onmessage()कॉलबैक फ़ंक्शन में मिलता है.
Kotlin
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)
}
Java
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);
}
यहां दिए गए JavaScript से, addWebMessageListener को क्लाइंट-साइड पर लागू करने के बारे में पता चलता है. इससे वेब कॉन्टेंट, नेटिव ऐप्लिकेशन से मैसेज पा सकता है और myObject प्रॉक्सी के ज़रिए अपने मैसेज भेज सकता है.
myObject.onmessage = function(event) {
console.log("App says: " + event.data);
};
myObject.postMessage("Hello world!");
postWebMessage (वैकल्पिक) का इस्तेमाल करें
Android ने इसे एसिंक्रोनस और मैसेजिंग पर आधारित विकल्प के तौर पर लॉन्च किया है. यह वेब के window.postMessage की तरह काम करता है.
यह कैसे काम करता है: ऐप्लिकेशन, वेब पेज के मुख्य फ़्रेम में पेलोड भेजने के लिए WebViewCompat.postWebMessage का इस्तेमाल करता है. दोनों दिशाओं में कम्यूनिकेशन चैनल बनाने के लिए, WebMessageChannel बनाया जा सकता है. इसके बाद, इसके किसी एक पोर्ट को मैसेज के साथ वेब कॉन्टेंट पर भेजा जा सकता है.
खास बातें:
- एसिंक्रोनस:
addWebMessageListenerकी तरह, यह तरीका एसिंक्रोनस मैसेजिंग का इस्तेमाल करता है. इससे यह पक्का होता है कि ऐप्लिकेशन बैकग्राउंड में डेटा प्रोसेस करते समय, वेब पेज उपयोगकर्ता की कार्रवाइयों के हिसाब से काम करता रहे. - ऑरिजिन की जानकारी रखने वाला:
targetOriginतय किया जा सकता है, ताकि यह पक्का किया जा सके कि WebView सिर्फ़ भरोसेमंद वेबसाइट को डेटा डिलीवर करे.
सीमाएं:
- स्कोप: यह एपीआई, कम्यूनिकेशन को मुख्य फ़्रेम तक सीमित करता है. यह सीधे तौर पर iframe को मैसेज भेजने या उनसे बातचीत करने की सुविधा नहीं देता.
- यूआरआई से जुड़ी पाबंदियां: इस तरीके का इस्तेमाल,
data:यूआरआई,file:यूआरआई याloadData()का इस्तेमाल करके लोड किए गए कॉन्टेंट के लिए नहीं किया जा सकता. हालांकि, अगर आपने टारगेट ऑरिजिन के तौर पर "*" चुना है, तो इसका इस्तेमाल किया जा सकता है. ऐसा करने से, किसी भी पेज को मैसेज मिल सकता है. - पहचान से जुड़ा जोखिम: वेब कॉन्टेंट के लिए, भेजने वाले की पहचान की पुष्टि करने का कोई तरीका उपलब्ध नहीं है. वेब पेज को मिला मैसेज, आपके नेटिव ऐप्लिकेशन या किसी दूसरे iframe से मिला हो सकता है.
इस तरीके का इस्तेमाल तब करें, जब आपको Android के पुराने वर्शन में स्ट्रिंग पर आधारित डेटा के लिए, एक सामान्य और एसिंक चैनल की ज़रूरत हो. इन वर्शन में addWebMessageListener काम नहीं करता.
addJavascriptInterface (लेगसी) का इस्तेमाल करना
सबसे पुराने तरीके में, नेटिव ऑब्जेक्ट इंस्टेंस को सीधे तौर पर WebView में इंजेक्ट करना शामिल है.
यह सुविधा कैसे काम करती है: Kotlin या Java क्लास तय करें. इसके बाद, अनुमति वाले तरीकों को @JavascriptInterface से एनोटेट करें. इसके बाद, addJavascriptInterface(Object, String) का इस्तेमाल करके, क्लास का इंस्टेंस WebView में जोड़ें.
खास बातें:
- सिंक्रोनस: JavaScript एक्ज़ीक्यूशन एनवायरमेंट तब तक ब्लॉक रहता है, जब तक आपके Android कोड में मौजूद तरीका वापस नहीं आ जाता.
- थ्रेड सुरक्षा: सिस्टम, बैकग्राउंड थ्रेड पर तरीकों को कॉल करता है. इसके लिए, Kotlin या Java की तरफ़ से सावधानीपूर्वक सिंक्रनाइज़ेशन की ज़रूरत होती है.
- सुरक्षा से जुड़ा जोखिम: डिफ़ॉल्ट रूप से,
addJavascriptInterfaceवेबव्यू में मौजूद हर फ़्रेम के लिए उपलब्ध होता है. इसमें iframe भी शामिल हैं. इसमें ओरिजन के आधार पर ऐक्सेस कंट्रोल की सुविधा नहीं है. WebView के एसिंक्रोनस व्यवहार की वजह से, उस फ़्रेम के यूआरएल का सुरक्षित तरीके से पता नहीं लगाया जा सकता जो आपके इंटरफ़ेस को कॉल कर रहा है. सुरक्षा की पुष्टि करने के लिए, आपकोWebView.getUrl()जैसे तरीकों पर भरोसा नहीं करना चाहिए. ऐसा इसलिए, क्योंकि इस बात की कोई गारंटी नहीं है कि ये तरीके सटीक हों. साथ ही, इनसे यह भी पता नहीं चलता कि अनुरोध किस फ़्रेम ने किया है.
डेटा टाइप कन्वर्ज़न और कोअर्शन
addJavascriptInterface का इस्तेमाल करने पर, Chromium पर आधारित Java Bridge, JavaScript रनटाइम और आपके Android ऐप्लिकेशन कोड के बीच डेटा टाइप को बदलता है.
तरीके के पैरामीटर और रिटर्न वैल्यू पर, टाइप बदलने से जुड़े ये नियम लागू होते हैं.
पैरामीटर टाइप मैपिंग (JavaScript से Java)
जब JavaScript, एनोटेट की गई Java या Kotlin मेथड को आर्ग्युमेंट पास करता है, तो ब्रिज JavaScript वैल्यू को Java के पैरामीटर टाइप में बदल देता है:
| Java पैरामीटर टाइप | JavaScript आर्ग्युमेंट की वैल्यू | दबाव डालने से जुड़ी गतिविधि |
|---|---|---|
byte, short, int, long |
संख्या (पूर्णांक) | वैल्यू को टारगेट इंटिजर टाइप में बदला जाता है. सीमा से बाहर की वैल्यू, स्टैंडर्ड न्यूमेरिक कास्टिंग के नियमों के हिसाब से रैप होती हैं. |
byte, short, int, long |
NaN |
0 में बदलता है. |
byte, short, int, long |
Infinity |
byte और short के लिए -1 या int और long के लिए Integer.MAX_VALUE और Long.MAX_VALUE को मजबूर करता है. |
float, double |
नंबर | यह Java की फ़्लोटिंग-पॉइंट वैल्यू में बदल जाता है. |
float, double |
NaN / Infinity |
Float.NaN, Double.NaN, Float.POSITIVE_INFINITY या Double.POSITIVE_INFINITY में बदलता है. |
char |
संख्या (पूर्णांक) | इसे यूनिकोड कोड पॉइंट में बदल दिया गया है. |
char |
पूर्णांक नहीं है, NaN, Infinity |
\u0000 में बदलता है. |
boolean |
true / false |
Java true या false में बदलता है. |
boolean |
संख्या, स्ट्रिंग, ऑब्जेक्ट | false में बदलता है. इसमें ऐसी स्ट्रिंग और संख्याएं शामिल हैं जो खाली नहीं हैं और शून्य नहीं हैं. |
String |
स्ट्रिंग | स्ट्रिंग वैल्यू को सुरक्षित रखा जाता है. |
String |
संख्या, बूलियन | इसे स्ट्रिंग के तौर पर दिखाया जाता है. उदाहरण के लिए, "42", "true", "false". |
String |
null / undefined |
null को Java null में बदला जाता है; undefined को लिटरल स्ट्रिंग "undefined" में बदला जाता है. |
String |
Object, ArrayBuffer, TypedArray | "undefined" लिटरल स्ट्रिंग में बदलता है. |
प्रिमिटिव ऐरे (जैसे कि int[], byte[], boolean[]) या String[] |
कलेक्शन ([...]) |
यह टारगेट एलिमेंट टाइप के 1D Java ऐरे में बदलता है. स्पार्स ऐरे, असाइन न किए गए इंडेक्स को डिफ़ॉल्ट वैल्यू (0, false, null) से भरते हैं. |
प्रिमिटिव कलेक्शन (जैसे कि int[], byte[]) |
TypedArray (Int8Array, Uint8Array, Int32Array, Float64Array) |
एलिमेंट को, उससे जुड़े Java प्रिमिटिव ऐरे में बदल दिया जाता है. |
एक से ज़्यादा डाइमेंशन वाला ऐरे (जैसे कि int[][]) |
नेस्ट किया गया ऐरे ([[...]]) |
काम नहीं करता. मल्टी-डाइमेंशनल ऐरे पैरामीटर की वैल्यू null होती है. |
ArrayBuffer, DataView |
ArrayBuffer, DataView |
ऐरे के तौर पर इस्तेमाल नहीं किया जा सकता. ArrayBuffer और DataView इंस्टेंस, null के तौर पर दिखते हैं. |
Object या कस्टम क्लास |
JavaScript ऑब्जेक्ट ({...}) |
काम नहीं करता. Java में, आर्बिट्ररी JavaScript ऑब्जेक्ट लिटरल की वैल्यू null होती है. |
Object या कस्टम क्लास |
इंजेक्ट किया गया Java ऑब्जेक्ट रैपर | काम करता है (राउंड-ट्रिपिंग). यह Java इंस्टेंस को Java तरीके से पास करता है. अगर Java टाइप, पैरामीटर के सिग्नेचर से मेल नहीं खाता है, तो यह फ़ंक्शन JavaScript अपवाद दिखाता है. |
बॉक्स वाले टाइप (जैसे कि Integer, Double, Boolean) |
संख्या, बूलियन | काम नहीं करता. बॉक्स किए गए प्रिमिटिव टाइप को अपारदर्शी ऑब्जेक्ट माना जाता है और उनकी वैल्यू null होती है. |
| कोई भी प्रिमिटिव टाइप | null / undefined |
डिफ़ॉल्ट वैल्यू (0, 0.0, \u0000, false) पर ले जाता है. |
Object, String, ऐरे |
null |
Java null में बदलता है. |
रिटर्न टाइप मैपिंग (Java से JavaScript)
जब एनोटेट किया गया कोई Java या Kotlin मेथड, वैल्यू दिखाता है, तो ब्रिज उसे JavaScript टाइप में बदल देता है:
| Java रिटर्न टाइप | JavaScript वैल्यू | JavaScript typeof |
|---|---|---|
boolean |
true / false |
"boolean" |
byte, short, int, long, float, double |
नंबर | "number" |
char |
नंबर (यूनिकोड कोड पॉइंट) | "number" |
String (शून्य नहीं है) |
स्ट्रिंग मान | "string" |
String (null) |
undefined |
"undefined" |
void |
undefined |
"undefined" |
Java ऐरे (जैसे, int[], String[]) |
undefined |
"undefined". ऐरे की रिटर्न वैल्यू इस्तेमाल नहीं की जा सकतीं. Java तरीके को लागू नहीं किया जाता है और अपवाद बढ़ाए बिना undefined को वापस कर दिया जाता है. |
| Java ऑब्जेक्ट / कस्टम टाइप (शून्य नहीं) | ऑब्जेक्ट रैपर | "object". यह Java इंस्टेंस के चारों ओर एक JavaScript रैपर बनाता है. JavaScript कोड, इस ऑब्जेक्ट पर मौजूद किसी भी सार्वजनिक तरीके को कॉल कर सकता है. हालांकि, इसके लिए ज़रूरी है कि उस तरीके को @JavascriptInterface के साथ एनोटेट किया गया हो. |
Java ऑब्जेक्ट / कस्टम टाइप (null) |
null |
"object" |
बॉक्स किया गया प्रिमिटिव (जैसे कि Integer, Double) |
ऑब्जेक्ट रैपर | "object". इसे ओपेक Java ऑब्जेक्ट रैपर के तौर पर दिखाया जाता है. इसमें @JavascriptInterface के कोई भी ऐक्सेस किए जा सकने वाले तरीके नहीं होते. इसलिए, JavaScript में इस वैल्यू का इस्तेमाल नहीं किया जा सकता. |
तरीका और सदस्यों के लिए उपलब्धता
JavaScript ब्रिज, सदस्यों के ऐक्सेस और दिखने से जुड़े नियमों को सख्ती से लागू करता है, ताकि अनचाहे कोड के एक्ज़ीक्यूट होने से बचा जा सके:
- फ़ील्ड ऐक्सेस नहीं किए जा सकते: Java फ़ील्ड (इसमें
publicऔरpublic finalफ़ील्ड शामिल हैं) को JavaScript से ऐक्सेस नहीं किया जा सकता. इनकी वैल्यूundefinedहोती है. - एनोटेशन की ज़रूरी शर्त: सिर्फ़
@JavascriptInterfaceके साथ साफ़ तौर पर एनोटेट किए गए तरीकों को JavaScript के लिए उपलब्ध कराया जाता है. - दिखने से जुड़ी पाबंदियां: तरीके
publicहोने चाहिए.privateऔरprotectedतरीकों को कभी भी JavaScript के लिए उपलब्ध नहीं कराया जाता. भले ही, उनमें@JavascriptInterfaceएनोटेशन मौजूद हो. - स्टैटिक तरीके:
@JavascriptInterfaceके साथ एनोटेट किए गए स्टैटिक तरीकों को JavaScript से कॉल किया जा सकता है. - इनहेरिटेंस और ओवरराइड करना: जब कोई सबक्लास किसी तरीके को ओवरराइड करता है, तो
@JavascriptInterfaceएनोटेशन इनहेरिट नहीं किए जाते. अगर कोई सबक्लास, सुपरक्लास के एनोटेट किए गए किसी तरीके को ओवरराइड करता है, तो सबक्लास को ओवरराइड किए गए तरीके पर@JavascriptInterfaceएनोटेशन को साफ़ तौर पर शामिल करना होगा, ताकि इसे JavaScript के लिए उपलब्ध कराया जा सके. अगर सुपरक्लास में एनोटेशन किया गया है, तो सुपरक्लास से इनहेरिट किए गए ऐसे पब्लिक मेथड ऐक्सेस किए जा सकते हैं जिन्हें बदला नहीं गया है. - रिफ़्लेक्शन से जुड़ी सुरक्षा: स्टैंडर्ड Java रिफ़्लेक्शन के तरीकों (जैसे कि
getClass()) को ब्लॉक कर दिया जाता है. साथ ही, JavaScript अपवाद को थ्रो किया जाता है, ताकि रिमोट कोड एक्ज़ीक्यूशन की कमियों को रोका जा सके. - मेथड ओवरलोडिंग: ओवरलोड किए गए Java मेथड काम करते हैं. ब्रिज, सिर्फ़ पास किए गए आर्ग्युमेंट की संख्या के आधार पर, तरीके के कॉल को हल करता है. यह आर्ग्युमेंट के टाइप को ध्यान में नहीं रखता. तर्क की अमान्य संख्या के साथ ओवरलोड किए गए किसी तरीके को कॉल करने पर, JavaScript अपवाद बढ़ जाता है. अगर दो ओवरलोड में एक जैसे आर्ग्युमेंट हैं, तो उनमें से किसी एक को चुन लिया जाएगा.
मैकेनिज़्म की खास जानकारी
यहां दी गई टेबल में, नेटिव ब्रिज को लागू करने के तीन मुख्य तरीकों की तुलना की गई है:
| तरीका | addWebMessageListener |
postWebMessage |
addJavascriptInterface |
|---|---|---|---|
| लागू करना | एसिंक्रोनस (मुख्य थ्रेड पर लिसनर) | एसिंक्रोनस | सिंक्रोनस |
| सुरक्षा | सबसे ज़्यादा (अनुमति वाली सूची के आधार पर) | ज़्यादा (ऑरिजिन के हिसाब से) | कम (ऑरिजिन की जांच नहीं की जाती) |
| जटिलता | सामान्य | सामान्य | आसान सुविधा |
| दिशा | दोनों दिशाओं में काम करने वाला | दोनों दिशाओं में काम करने वाला | वेब से ऐप्लिकेशन पर हुआ कन्वर्ज़न |
| WebView का कम से कम वर्शन | वर्शन 82 (और Jetpack Webkit 1.3.0) | वर्शन 45 (और Jetpack Webkit 1.1.0) | सभी वर्शन |
| सुझाए गए | हां | नहीं | नहीं |
बड़े डेटा ट्रांसफ़र को मैनेज करना
आपको बड़े पेलोड ट्रांसफ़र करते समय, मेमोरी को सावधानी से मैनेज करना होगा. जैसे, मल्टी-मेगाबाइट स्ट्रिंग या बाइनरी फ़ाइलें. ऐसा इसलिए, ताकि 32-बिट डिवाइसों पर ऐप्लिकेशन के बंद होने या उसमें कोई समस्या होने (एएनआर) से जुड़ी गड़बड़ियों से बचा जा सके. इस सेक्शन में, होस्ट ऐप्लिकेशन और वेब कॉन्टेंट के बीच बड़ी मात्रा में डेटा ट्रांसफ़र करने से जुड़ी अलग-अलग तकनीकों और सीमाओं के बारे में बताया गया है.
बाइट कलेक्शन की मदद से बाइनरी डेटा ट्रांसफ़र करना
WebMessageCompat क्लास की मदद से, बाइनरी डेटा को Base64 स्ट्रिंग में बदलने के बजाय, सीधे byte[] ऐरे भेजे जा सकते हैं. Base64, डेटा साइज़ में करीब 33% ओवरहेड जोड़ता है. इसलिए, यह ज़्यादा मेमोरी-इफ़िशिएंट और तेज़ है.
- बाइनरी फ़ाइल ट्रांसफ़र करने की सुविधा: इस सुविधा की मदद से, इमेज फ़ाइलों या ऑडियो जैसे बाइनरी डेटा को अपने नेटिव ऐप्लिकेशन और वेब कॉन्टेंट के बीच ट्रांसफ़र किया जा सकता है.
- सीमा: बाइट ऐरे के साथ भी, सिस्टम ऐप्लिकेशन और आइसोलेटेड प्रोसेस के बीच इंटर-प्रोसेस कम्यूनिकेशन (आईपीसी) बाउंड्री में डेटा कॉपी करता है. वेबव्यू, वेब कॉन्टेंट को रेंडर करने के लिए इस प्रोसेस का इस्तेमाल करता है. हालांकि, बहुत बड़ी फ़ाइलों के लिए अब भी ज़्यादा मेमोरी खर्च होती है.
यहां दिए गए कोड के उदाहरणों में, खास ऐप्लिकेशन पर addWebMessageListener को सेट अप करने का तरीका बताया गया है. इससे WebMessageCompat.TYPE_ARRAY_BUFFER के तौर पर मार्क किए गए मैसेज मिल सकते हैं. साथ ही, WebViewFeature.MESSAGE_ARRAY_BUFFER की जांच करके, बाइनरी डेटा के साथ जवाब दिया जा सकता है.
Kotlin
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
)
}
}
Java
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
);
}
}
यहां दिए गए JavaScript कोड में, 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 एमबी) के लिए, डेटा स्ट्रीम करने के लिए shouldInterceptRequest तरीके का इस्तेमाल करें:
- वेब पेज, कस्टम प्लेसहोल्डर यूआरएल पर
fetch()कॉल शुरू करता है. उदाहरण के लिए,https://app.local/large-file. - Android ऐप्लिकेशन, इस अनुरोध को
WebViewClient.shouldInterceptRequest में इंटरसेप्ट करता है. - ऐप्लिकेशन, डेटा को
InputStreamके तौर पर दिखाता है.
इससे पूरे पेलोड को एक साथ मेमोरी में लोड करने के बजाय, डेटा को छोटे-छोटे हिस्सों में स्ट्रीम किया जा सकता है.
यहां दिया गया JavaScript फ़ंक्शन, क्लाइंट-साइड कोड दिखाता है. इसकी मदद से, नेटिव ऐप्लिकेशन से बड़ी बाइनरी फ़ाइल को आसानी से लोड किया जा सकता है. इसके लिए, कस्टम प्लेसहोल्डर यूआरएल को स्टैंडर्ड 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);
}
}
यहां दिए गए कोड के उदाहरणों में, खास ऐप्लिकेशन के बारे में बताया गया है. इसमें Kotlin और Java, दोनों में WebViewClient.shouldInterceptRequest तरीके का इस्तेमाल किया गया है. इससे वेब कॉन्टेंट से अनुरोध किए गए कस्टम प्लेसहोल्डर यूआरएल को इंटरसेप्ट करके, बड़ी बाइनरी फ़ाइल को स्ट्रीम किया जा सकता है.
Kotlin
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)
}
}
Java
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);
}
});
सुरक्षा से जुड़े सुझावों का पालन करना
अपने ऐप्लिकेशन और उपयोगकर्ता के डेटा को सुरक्षित रखने के लिए, ब्रिज लागू करते समय इन दिशा-निर्देशों का पालन करें:
एचटीटीपीएस लागू करें: यह पक्का करने के लिए कि तीसरे पक्ष का कॉन्टेंट, आपके ऐप्लिकेशन के नेटिव लॉजिक को लागू न कर पाए, सिर्फ़ सुरक्षित ऑरिजिन के साथ कम्यूनिकेशन की अनुमति दें.
ओरिजन के नियमों का पालन करें: भरोसा बनाए रखने का सबसे अच्छा तरीका यह है कि आप अपने
allowedOriginRulesको सख्ती से तय करें और मैसेज कॉलबैक में दिए गएsourceOriginकी जांच करें. जब तक बहुत ज़रूरी न हो, तब तक सिर्फ़ ओरिजिन के नियम के तौर पर पूरे वाइल्डकार्ड (*) का इस्तेमाल न करें. यह सभी ओरिजिन से मेल खाता है. सबडोमेन (उदाहरण के लिए,*.example.com) के लिए वाइल्डकार्ड का इस्तेमाल करना मान्य और सुरक्षित है. इससे एक से ज़्यादा सबडोमेन (उदाहरण के लिए,foo.example.com,bar.example.com) को मैच किया जा सकता है.ध्यान दें: ऑरिजिन के नियमों से, तीसरे पक्ष की नुकसान पहुंचाने वाली वेबसाइटों और छिपे हुए iframe से सुरक्षा मिलती है. हालांकि, इनसे आपके भरोसेमंद डोमेन में मौजूद क्रॉस-साइट स्क्रिप्टिंग (एक्सएसएस) की कमज़ोरियों से सुरक्षा नहीं मिलती. उदाहरण के लिए, अगर आपके वेब पेज पर यूज़र जनरेटेड कॉन्टेंट दिखता है और वह सेव किए गए XSS के लिए असुरक्षित है, तो हमलावर एक ऐसी स्क्रिप्ट को एक्ज़ीक्यूट कर सकता है जो आपके भरोसेमंद ऑरिजिन के तौर पर काम करती है. संवेदनशील नेटिव प्लैटफ़ॉर्म ऑपरेशन करने से पहले, मैसेज पेलोड पर पुष्टि करने की सुविधा लागू करें.
कम से कम डेटा शेयर करें: सिर्फ़ उन तरीकों या डेटा को शेयर करें जिनकी वेब पेज को ज़रूरत है.
रनटाइम के दौरान सुविधाओं की जांच करें: हाल ही के ब्रिज एपीआई, Jetpack Webkit लाइब्रेरी का हिस्सा हैं. इनमें
addWebMessageListenerशामिल है. इसलिए, कॉल करने से पहलेWebViewFeature.isFeatureSupported()का इस्तेमाल करके, हमेशा यह देखें कि सहायता उपलब्ध है या नहीं.