WebView Initialization¶
1. “JavaScript is enabled.”¶
The WebView has javaScriptEnabled = true (Android) or equivalent, so JS code inside the loaded page can actually run.
Without this, <script> tags and any JS logic in your HTML would be completely ignored by the WebView.
Enabling JS is required for:
- Calling native bridge functions from JS.
- Handling click events, dynamic UI updates, AJAX/fetch calls, etc.
Security angle:
- Only enable JS for pages you trust or control (e.g. your own app’s HTML, not arbitrary untrusted URLs).
- Combine this with other restrictions (e.g. restricted URL loading, content security policies).
2. “A secure WebViewClient/WebChromeClient is attached.”¶
WebViewClient:
- Controls how URLs are loaded (inside the app vs external browser).
- Lets you intercept and validate navigation (e.g. block unknown domains, deep links).
- Good for enforcing rules like “only allow https://my-app-domain.com”
WebChromeClient:
- Handles advanced browser-like features:
- JavaScript dialogs (alert, confirm, prompt)
- Console logs
- Permissions (camera, geolocation, etc.)
-
Gives you hooks to approve/deny things that JS is trying to do.
-
“Secure” here means you’re not just using the default behavior:
- You override methods to:
- Validate target URLs before loading.
- Prevent mixed content or cleartext HTTP if not needed.
- Control file upload, camera, or other sensitive features.
You disable or limit dangerous features (e.g. arbitrary file access, universal access from file URLs, etc.) unless explicitly required.
3. “All bridge handlers are registered during initialization.”¶
Your native bridge exposes specific “handlers” (endpoints) that JS can call, e.g.:
- takePhoto
- getUserToken
- openSettings
“Registered during initialization” means:
- As soon as the WebView (or the bridge module) is created, you call something like:
bridge.registerHandler("takePhoto", handlerFunction)
- By the time the page finishes loading, all the expected handlers exist.
Why this is important:
- Avoids race conditions where JS calls a handler that isn’t registered yet → errors like “handler not found”.
- Provides a clear contract between JS and native:
- The set of available APIs is known up front.
- You can document them and version them.
Security benefit:
- You only register what you explicitly want to expose.
- You don’t accidentally open random native methods to JS.
- You can add auth/permission checks inside each handler.
4. “JS can call native methods using window.bridge.callHandler(...).”¶
This is the API surface that JS sees for talking to native code. Typical call shape:
window.bridge.callHandler("takePhoto", { quality: "high" }, function (response) { /* handle result */ });
Under the hood:
callHandlerpackages the handler name + data into a message.- Sends the message across the JS–native bridge (via addJavascriptInterface, postMessage, or custom URL schemes, depending on your implementation).
- Native code receives it, looks up the registered handler, runs it, then sends a response back, which triggers the JS callback.
Design benefits:
- Single entry point from JS to native, instead of exposing many random globals.
- Easy to log, audit, and validate:
- You can log every callHandler call and its arguments on the native side.
- Supports async operations nicely: native can complete later and then invoke the JS callback with success/error.
Registering Bridge Handlers¶
Handlers are registered like:
bridgeWebView.registerHandler("mqttConnect", new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
// native code
}
});
What this line does conceptually¶
-
Registers a named bridge API
"mqttConnect"is the handler name (the API name JS will call).- You’re telling the bridge:
“When JS calls mqttConnect, run this Java code.”
-
Connects JS world → native MQTT logic:
- This handler is meant for MQTT connection logic (e.g. connecting to a broker, authenticating, etc.).
- All the heavy lifting (sockets, credentials, retry, etc.) stays in native code, not in JS.
Scan and Connecting to a BLE Device¶
This snippet shows how the WebView JavaScript code communicates with the native layer (Android/iOS) through bridgeWebView to:
- Register callbacks for BLE events
- Start scanning for BLE devices
- Connect to a device using its MAC address
// Step 1: Register callbacks
bridgeWebView.registerHandler("findBleDeviceCallBack", function(data) {
const device = JSON.parse(data);
console.log("Found:", device.name, device.macAddress);
});
bridgeWebView.registerHandler("bleConnectSuccessCallBack", function(macAddress) {
console.log("Connected to:", macAddress);
});
// Step 2: Start scanning
bridgeWebView.callHandler("startBleScan", "", function(response) {
const result = JSON.parse(response);
if (result.success) {
console.log("Scanning started...");
}
});
// Step 3: Connect to device (when you find the one you want)
setTimeout(() => {
bridgeWebView.callHandler("connBleByMacAddress", "AA:BB:CC:DD:EE:FF", function(response) {
const result = JSON.parse(response);
if (result.success) {
console.log("Connecting...");
}
});
}, 5000);
Overview¶
bridgeWebViewis the JavaScript–native bridge object exposed inside the WebView.- registerHandler(name, callback) registers a JavaScript function that the native app can call.
- callHandler(name, data, callback) calls a native function and optionally receives an asynchronous response.
The general flow is:
- The JS side registers handlers so native code can notify the web page.
- JS tells native code to start scanning for BLE devices.
- When a device is found, native calls back into JS.
- JS decides which device to connect to and calls the native “connect” function.
Step 1: Registering Callbacks from Native to JS¶
findBleDeviceCallBack
bridgeWebView.registerHandler("findBleDeviceCallBack", function(data) {
const device = JSON.parse(data);
console.log("Found:", device.name, device.macAddress);
});
- Purpose: This handler is triggered by the native layer whenever a BLE device is discovered during a scan.
-
Parameters:
data: a JSON string sent from native code. It is expected to represent a device object, for example:{ "name": "My BLE Device", "macAddress": "AA:BB:CC:DD:EE:FF" }
-
Logic:
-
JSON.parse(data) converts the JSON string into a JavaScript object.
- device.name and device.macAddress are read from the parsed object.
- The device information is logged:
console.log("Found:", device.name, device.macAddress);
-
How it’s used:
- You can later replace the
console.logwith UI updates, e.g. adding the device to a list for the user to select.
- You can later replace the
bleConnectSuccessCallBack
bridgeWebView.registerHandler("bleConnectSuccessCallBack", function(macAddress) {
console.log("Connected to:", macAddress);
});
- Purpose: Notifies the JS side that a BLE connection has been successfully established.
-
Parameters:
macAddress: a simple string containing the MAC address of the connected device.
-
Logic:
- Logs the MAC address of the device that was successfully connected:
console.log("Connected to:", macAddress);
- Logs the MAC address of the device that was successfully connected:
-
How it’s used:
- This is a good place to update the UI to “Connected” state or enable features that require an active BLE connection.
Step 2: Starting the BLE Scan¶
bridgeWebView.callHandler("startBleScan", "", function(response) {
const result = JSON.parse(response);
if (result.success) {
console.log("Scanning started...");
}
});
- Purpose: Requests the native layer to start scanning for BLE devices.
-
Arguments:
- First argument: "startBleScan" — the name of the native method.
- Second argument: "" — an empty string; no extra parameters are needed for this call.
- Third argument: callback function to handle the native response.
-
Response Handling:
- response is expected to be a JSON string, e.g.:
{ "success": true }
- response is expected to be a JSON string, e.g.:
-
It is parsed into a JavaScript object:
const result = JSON.parse(response); -
If result.success is true, scanning has successfully started:
if (result.success) { console.log("Scanning started..."); } -
Flow:
- After scanning starts, the native side will typically start calling findBleDeviceCallBack whenever it discovers devices.
Step 3: Connecting to a Device by MAC Address¶
setTimeout(() => {
bridgeWebView.callHandler("connBleByMacAddress", "AA:BB:CC:DD:EE:FF", function(response) {
const result = JSON.parse(response);
if (result.success) {
console.log("Connecting...");
}
});
}, 5000);
- Purpose: Connects to a specific BLE device using its MAC address.
-
setTimeout:
setTimeout(..., 5000);delays this call by 5 seconds.- This simulates: “wait some time while scanning and discovering devices, then try to connect”.
- In a real app, you would likely call this after the user selects a device from a list (instead of using a fixed timeout).
Arguments to callHandler:
"connBleByMacAddress": the native method responsible for connecting to a device."AA:BB:CC:DD:EE:FF": the MAC address of the target device (placeholder; should be replaced with the actual device MAC from findBleDeviceCallBack or user selection).function(response) { ... }: callback that handles the native response.
Response Handling: - response is expected to be a JSON string, e.g.:
{ "success": true }
-
It is parsed:
const result = JSON.parse(response); -
If
result.successistrue, it logs:console.log("Connecting...");
After the connection is actually established, the native side should trigger the bleConnectSuccessCallBack handler registered in Step 1.
End-to-End Flow Summary¶
-
Register callbacks
findBleDeviceCallBack: called by native when a device is found.bleConnectSuccessCallBack: called by native when a connection succeeds.
-
Start scan
- JS calls startBleScan.
- Native starts scanning and returns
{ success: true }if successful. - As devices are found, native repeatedly calls
findBleDeviceCallBackwith device data.
-
Select and connect
- JS decides which device to connect to (in the example: a hardcoded MAC address after 5 seconds).
- JS calls connBleByMacAddress with the chosen MAC address.
- Native attempts a connection and replies with
{ success: true }if the connection process starts correctly. - Once fully connected, native triggers
bleConnectSuccessCallBackwith the MAC address of the device. You can adapt the logs to UI updates (showing devices in a list, showing connection status, etc.) and add error handling by checkingresult.success === falseand displaying appropriate messages.