WebView-এ JavaScript Interface দিয়ে HTML বাটন হ্যান্ডলিং
এই টিউটোরিয়ালে দেখানো হয়েছে কীভাবে Android WebView-এ লোড করা HTML পেজের বাটন ক্লিক ইভেন্ট নেটিভ Android কোডে (Java/Kotlin-এ) রিসিভ করা যায়। এটি @JavascriptInterface এবং addJavascriptInterface() ব্যবহার করে করা হয়।
সুরক্ষা সতর্কতা ⚠️
@JavascriptInterface অ্যানোটেশন যুক্ত মেথডهاয় যেকোনো পাবলিক মেথড JavaScript থেকে কল করা যায়। শুধু বিশ্বস্ত কন্টেন্ট (লোকাল asset, নিজের সার্ভার) লোড করুন। থার্ড-পার্টি ওয়েবসাইট লোড করলে সিকিউরিটি রিস্ক থাকে।
ধাপ ১: প্রজেক্ট সেটআপ
Package: com.example.webviewjsbridge
ধাপ ২: লেআউট (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
<Button
android:id="@+id/btnLoad"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:onClick="loadWebPage"
android:text="Web Page লোড করুন" />
</RelativeLayout>
ধাপ ৩: HTML ফাইল (assets/page.html)
src/main/assets/page.html ফাইল তৈরি করুন (assets ফোল্ডার না থাকলে New → Folder → Assets Folder):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebView JS Bridge Demo</title>
<style>
body { font-family: sans-serif; padding: 20px; }
.input-group { margin-bottom: 16px; }
label { display: block; margin-bottom: 4px; font-weight: bold; }
input { width: 100%; padding: 12px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }
button { background: #4CAF50; color: white; border: none; padding: 12px 24px; border-radius: 4px; font-size: 16px; cursor: pointer; width: 100%; }
button:active { background: #45a049; }
#result { margin-top: 16px; padding: 12px; background: #f5f5f5; border-radius: 4px; display: none; }
</style>
</head>
<body>
<h2>JavaScript Bridge Demo</h2>
<div class="input-group">
<label>First Name:</label>
<input type="text" id="txtFirstName" placeholder="Enter first name">
</div>
<div class="input-group">
<label>Last Name:</label>
<input type="text" id="txtLastName" placeholder="Enter last name">
</div>
<button type="button" id="btnSubmit" onclick="sendToAndroid()">Android-এ পাঠান</button>
<div id="result"></div>
<script>
function sendToAndroid() {
var firstName = document.getElementById('txtFirstName').value.trim();
var lastName = document.getElementById('txtLastName').value.trim();
if (!firstName || !lastName) {
alert('দয়া করে উভয় ফিল্ড পূরণ করুন');
return;
}
var fullName = firstName + ' ' + lastName;
// Android Interface কল
if (typeof AndroidBridge !== 'undefined' && AndroidBridge.onButtonClick) {
AndroidBridge.onButtonClick(fullName);
} else {
document.getElementById('result').style.display = 'block';
document.getElementById('result').innerText = 'Android Bridge পাওয়া যায়নি। অ্যাপে ইনজেক্ট করা হয়নি।';
}
}
// Android থেকে কলব্যাক রিসিভ করার ফাংশন
function onAndroidResponse(message) {
document.getElementById('result').style.display = 'block';
document.getElementById('result').innerText = 'Android থেকে প্রতিক্রিয়া: ' + message;
}
</script>
</body>
</html>
ধাপ ৪: JavaScript Interface ক্লাস
package com.example.webviewjsbridge;
import android.webkit.JavascriptInterface;
import android.widget.Toast;
public class WebAppInterface {
private final MainActivity activity;
public WebAppInterface(MainActivity activity) {
this.activity = activity;
}
@JavascriptInterface
public void onButtonClick(String fullName) {
// এই মেথড JavaScript থেকে কল হবে
// UI thread-এ রান করানোর জন্য runOnUiThread ব্যবহার করুন
activity.runOnUiThread(() -> {
Toast.makeText(activity, "JavaScript থেকে পাওয়া: " + fullName, Toast.LENGTH_LONG).show();
// অপশনাল: JavaScript-এ ফিরিয়ে পাঠান
activity.sendResponseToJs("নাম রিসিভ করা হয়েছে: " + fullName);
});
}
}
ধাপ ৫: MainActivity.java
package com.example.webviewjsbridge;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void loadWebPage(View view) {
webView = new WebView(this);
setContentView(webView);
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true); // জাভাস্ক্রিপ্ট সক্ষম করুন
settings.setDomStorageEnabled(true);
settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
// জাভাস্ক্রিপ্ট ইন্টারফেস যোগ করুন
webView.addJavascriptInterface(new WebAppInterface(this), "AndroidBridge");
// WebViewClient সেট করুন (লিঙ্ক ক্লিকে ব্রাউজার খুলবে না)
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
Toast.makeText(MainActivity.this, "পেজ লোড সম্পন্ন", Toast.LENGTH_SHORT).show();
}
});
// Assets ফোল্ডার থেকে HTML লোড
webView.loadUrl("file:///android_asset/page.html");
}
// JavaScript-এ ফিরিয়ে পাঠানোর মেথড
public void sendResponseToJs(String message) {
if (webView != null) {
// JavaScript ফাংশন কল: onAndroidResponse(message)
// escapeSingleQuotes দিয়ে স্ট্রিং স্যানিটাইজ করুন
String escaped = message.replace("'", "\\'").replace("\n", "\\n").replace("\r", "");
webView.evaluateJavascript("javascript:onAndroidResponse('" + escaped + "')", null);
}
}
@Override
public void onBackPressed() {
if (webView != null && webView.canGoBack()) {
webView.goBack();
} else {
super.onBackPressed();
}
}
@Override
protected void onDestroy() {
if (webView != null) {
webView.removeJavascriptInterface("AndroidBridge");
webView.destroy();
}
super.onDestroy();
}
}
ধাপ ৬: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.example.webviewjsbridge">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.WebViewJSBridge">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
গুরুত্বপূর্ণ সিকিউরিটি বেস্ট প্র্যাকটিস
| নীতিমালা | কারণ |
|---|---|
| শুধু @JavascriptInterface অ্যানোটেশনযুক্ত মেথড এক্সপোজ করুন | API 17+ এ অন্য মেথডها অ্যাক্সেসযোগ্য নয়, কিন্তু সাবধান থাকুন |
| শুধু লোকাল (assets) বা HTTPS ওয়েবসাইট লোড করুন | HTTP বা অজানা সাইটে XSS আটাক সম্ভব |
| Input validation করুন | JavaScript থেকে আসা ডাটা স্যানিটাইজ করুন |
| ProGuard/R8-এ interface ক্লাস keep করুন | অন্যথায় রিলিজ বিল্ডে মেথড রিমুভ হয়ে যাবে |
| onDestroy-এ removeJavascriptInterface কল করুন | মেমরি লিক এবং সিকিউরিটি হোল প্রতিরোধ |
ProGuard/R8 Rules (proguard-rules.pro)
-keepclassmembers class com.example.webviewjsbridge.WebAppInterface {
public *;
}
-keep class com.example.webviewjsbridge.WebAppInterface { *; }
উন্নত ফিচার: দ্বিমুখী যোগাযোগ
// JavaScript-এ (page.html)
function onNativeEvent(eventName, data) {
console.log('Native event:', eventName, data);
// UI আপডেট করুন
}
// Android-এ (MainActivity)
public void sendEventToJs(String eventName, String data) {
if (webView != null) {
String js = String.format("javascript:onNativeEvent('%s', '%s')",
eventName.replace("'", "\\'"), data.replace("'", "\\'"));
webView.evaluateJavascript(js, null);
}
}
পরীক্ষা পদ্ধতি
- অ্যাপ চালু করুন → "Web Page লোড করুন" চাপুন
- First Name এবং Last Name ফিল্ড পূরণ করুন
- "Android-এ পাঠান" বাটন চাপুন
- টোস্ট মেসেজ দেখুন: "JavaScript থেকে পাওয়া: [নাম]"
- WebView-এ "Android থেকে প্রতিক্রিয়া: নাম রিসিভ করা হয়েছে: [নাম]" দেখুন
সাধারণ সমস্যা ও সমাধান
| সমস্যা | সমাধান |
|---|---|
| JavaScript Interface কল হয় না | addJavascriptInterface() কল হয়েছে কিনা, নাম "AndroidBridge" মিলছে কিনা চেক করুন |
| Release build-এ কাজ করে না | ProGuard/R8 rules যোগ করুন interface ক্লাস keep করার জন্য |
| evaluateJavascript কাজ করে না | API 19+ দরকার, WebViewClient সেট আছে কিনা চেক করুন |
| Security Exception | শুধু trusted origin (file:///android_asset/ বা HTTPS) লোড করুন |