MQTT Connection & Messaging Overview¶
The MQTT integration allows the WebView (JavaScript) layer to connect to an MQTT broker through the native Android service (BleService), publish messages, and subscribe/unsubscribe to topics. All interaction happens via bridgeWebView handlers.
MQTT Communication Workflow¶
┌──────────┐
│ START │
└────┬─────┘
│
▼
┌─────────────────────────┐
│ Register Callbacks │
│ - connectCallback │
│ - messageArrivedCallback│
└────┬────────────────────┘
│
▼
┌─────────────────────────┐
│ Connect to Broker │ ──────> connectMqtt({
└────┬────────────────────┘ hostname,
│ port,
│ (Async Connection) clientId
│ })
▼
┌─────────────────────────┐
│ connectCallback │
│ Called │
└────┬────────────────────┘
│
├───────────┬───────────┐
▼ ▼ ▼
Success Failed Timeout
│
▼
┌─────────────────────────┐
│ Subscribe to Topics │ ──────> mqttSubTopic({
└────┬────────────────────┘ topic,
│ qos
│ })
│
├─────────────────────────────┐
│ │
│ ▼
│ ┌─────────────────┐
│ │ Publish Message │
│ │ │
│ │ mqttPublishMsg({│
│ │ topic, │
│ │ content, │
│ │ qos │
│ │ }) │
│ └─────────────────┘
│
▼
┌─────────────────────────┐
│ Wait for Messages │
│ │
│ messageArrivedCallback │ <──── (Messages arrive)
│ fired when msg received │
└────┬────────────────────┘
│
│ (Process messages...)
│
▼
┌─────────────────────────┐
│ Unsubscribe │ ──────> mqttUnSubTopic()
└────┬────────────────────┘
│
▼
┌──────────┐
│ END │
└──────────┘
At a high level the MQTT flow is:
- Connect to MQTT broker via
connectMqtt - Subscribe to topics via
mqttSubTopic - Receive messages via the
mqttMsgArrivedCallBackJavaScript callback - Publish messages via
mqttPublishMsg - Unsubscribe via
mqttUnSubTopicwhen no longer needed
Internally, MQTT connectivity is managed by BleService which wraps a MqttClientUtil instance.
MQTT Handlers Exposed to JavaScript¶
All MQTT operations are exposed as bridge handlers registered on bridgeWebView inside BaseWebViewActivity:
connectMqttmqttSubTopicmqttUnSubTopicmqttPublishMsg
Additionally, messages arriving from the broker are pushed from native to JavaScript through the callback: mqttMsgArrivedCallBack
Each handler accepts parameters as a JSON string and returns a JSON-encoded Result object to the JavaScript callback.
In JS, you typically use:
const result = JSON.parse(response);
if (result.success) {
// ...
}
1. Connecting to the MQTT Broker – connectMqtt¶
Purpose: - Establish a connection to an MQTT broker using configuration provided by JavaScript.
Bridge Handler¶
bridgeWebView.registerHandler("connectMqtt", new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
// ...
}
});
Input (from JavaScript)¶
data: JSON string representing an MqttConfig object.
Minimal required fields (validated in BaseWebViewActivity):
- hostname – broker host / IP
- port – broker port (e.g. 1883 / 8883)
- clientId – unique MQTT client identifier
Example payload from JS:
const config = {
hostname: "mqtt.example.com",
port: 1883,
clientId: "my-client-id-123",
// Possible additional fields depending on MqttConfig:
// username, password, ssl, keepAlive, etc.
};
bridgeWebView.callHandler("connectMqtt", JSON.stringify(config), function (response) {
const result = JSON.parse(response);
if (result.success) {
console.log("MQTT connect request accepted, waiting for final callback...");
} else {
console.error("Invalid MQTT config:", result);
}
});
Native Logic (simplified)¶
1. Validate payload¶
- If
datais empty → returnsResult.fail(PARAMETER_ERROR, false) - Parse JSON to
MqttConfig mqttConfig - Check required fields:
hostname,port,clientId
If any required field is missing or invalid → returns Result.fail(PARAMETER_ERROR, false)
2. Immediate response to JS¶
If validation passes, native immediately returns Result.ok(true) to the calling JS callback. The actual connection is done asynchronously in a background thread.
3. Async connection¶
ThreadPool.getExecutor().execute(() -> { boolean b = bleService.connectMqtt(mqttConfig); ... })- After
bleService.connectMqtt(mqttConfig)finishes, result is posted back on the UI thread.
4. Final result callback to JS¶
- On success:
bridgeWebView.callHandler("connectMqttCallBack", gson.toJson(Result.ok(true)), ...); - On failure (e.g. broker not reachable, auth failure):
bridgeWebView.callHandler("connectMqttCallBack", gson.toJson(Result.fail(MQTT_CONNECT_FAIL, false)), ...);
How to handle in JavaScript¶
You must define the connectMqttCallBack handler on the JS side to know when the connection actually succeeded or failed:
bridgeWebView.registerHandler("connectMqttCallBack", function (data) {
const result = JSON.parse(data);
if (result.success) {
console.log("MQTT connected successfully");
// Now it’s safe to subscribe and publish
} else {
console.error("MQTT connection failed:", result);
}
});
2. Subscribing to Topics – mqttSubTopic¶
Purpose¶
Subscribe the current MQTT client to a given topic with a specified QoS level.
Bridge Handler¶
bridgeWebView.registerHandler("mqttSubTopic", new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
// ...
}
});
Input (from JavaScript)¶
datais a JSON string with:topic– MQTT topic string (e.g."devices/abc123/status")qos– QoS level (0,1, or2)
Example call:
const payload = {
topic: "devices/abc123/status",
qos: 1,
};
bridgeWebView.callHandler("mqttSubTopic", JSON.stringify(payload), function (response) {
const result = JSON.parse(response);
if (result.success) {
console.log("Subscribed to topic successfully");
} else {
console.error("Failed to subscribe:", result);
}
});
const payload = {
topic: "devices/abc123/status",
qos: 1,
};
bridgeWebView.callHandler("mqttSubTopic", JSON.stringify(payload), function (response) {
const result = JSON.parse(response);
if (result.success) {
console.log("Subscribed to topic successfully");
} else {
console.error("Failed to subscribe:", result);
}
});
Native Logic (simplified)¶
- Parse
datainto aJSONObject. 2.Extracttopicandqos. -
Retrieve MQTT client:
MqttClientUtil mqttClientUtil = bleService.getMqttClientUtil(); -
Check connection:
- If
mqttClientUtil== null or!mqttClientUtil.isConnected()→Result.fail(MQTT_CURRENT_NOT_CONNECTED, false)
- If
Call:
boolean subscribe = mqttClientUtil.subscribe(topic, qos);
subscribe == true → Result.ok(true)
- Otherwise →Result.fail(FAIL, false)
On any exception → Result.fail(PARAMETER_ERROR, false)
3. Unsubscribing from Topics – mqttUnSubTopic¶
Purpose¶
Unsubscribe from a previously subscribed topic.
Bridge Handler¶
bridgeWebView.registerHandler("mqttUnSubTopic", new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
// ...
}
});
Input (from JavaScript)¶
data is a JSON string with:
- topic – MQTT topic to unsubscribe from
Example call:
const payload = {
topic: "devices/abc123/status",
};
bridgeWebView.callHandler("mqttUnSubTopic", JSON.stringify(payload), function (response) {
const result = JSON.parse(response);
if (result.success) {
console.log("Unsubscribed from topic");
} else {
console.error("Failed to unsubscribe:", result);
}
});
Native Logic (simplified)¶
- Parse topic from JSON.
- Retrieve MqttClientUtil via bleService.getMqttClientUtil().
- If MQTT is not connected → Result.fail(MQTT_CURRENT_NOT_CONNECTED, false).
- Call:
boolean ok = mqttClientUtil.unSubscribe(topic); - If
ok→Result.ok(true)Else →Result.fail(FAIL, false) - On
exception→Result.fail(PARAMETER_ERROR, false)
4. Publishing Messages – mqttPublishMsg¶
Purpose¶
Publish a message to a specific MQTT topic with a given QoS level.
Bridge Handler¶
bridgeWebView.registerHandler("mqttPublishMsg", new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
// ...
}
});
Input (from JavaScript)¶
datais a JSON string:topic– MQTT topic to publish to- content – payload/body of the message (string)
qos– QoS level (0,1,2)
Example call:
const payload = {
topic: "devices/abc123/commands",
content: JSON.stringify({ action: "reboot" }),
qos: 1,
};
bridgeWebView.callHandler("mqttPublishMsg", JSON.stringify(payload), function (response) {
const result = JSON.parse(response);
if (result.success) {
console.log("Message published");
} else {
console.error("Failed to publish:", result);
}
});
Native Logic (simplified)¶
- Parse
topic,content, andqosfrom JSON. - Retrieve
MqttClientUtilviableService.getMqttClientUtil(). - Check connection; if disconnected →
Result.fail(MQTT_CURRENT_NOT_CONNECTED, false). - Publish:
boolean ok = mqttClientUtil.publish(topic, qos, content.getBytes(StandardCharsets.UTF_8));
5. Receiving MQTT Messages – mqttMsgArrivedCallBack¶
Purpose¶
Deliver incoming MQTT messages from native (via EventBus) to JavaScript.
Native Flow¶
BleService (through MqttClientUtil) receives MQTT messages and posts them to an EventBus channel with tag EventBusEnum.MQTT_MSG_ARRIVED. BaseWebViewActivity listens for these events:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(EventBusMsg message) {
// ...
} else if (message.getTagEnum() == EventBusEnum.MQTT_MSG_ARRIVED) {
bridgeWebView.callHandler("mqttMsgArrivedCallBack",
(String) message.getT(),
new CallBackFunction() {
@Override
public void onCallBack(String data) { }
});
}
// ...
}
message.getT() is expected to be a String representation of the MQTT payload (and possibly metadata, depending on how MqttClientUtil formats it).
Handling in JavaScript¶
You must register a handler for mqttMsgArrivedCallBack to receive and parse incoming messages:
bridgeWebView.registerHandler("mqttMsgArrivedCallBack", function (payload) {
// `payload` is a string coming from native.
// It might be plain text or JSON-encoded, depending on your MQTT publisher.
try {
const parsed = JSON.parse(payload);
console.log("MQTT message (parsed):", parsed);
// Handle structured message here
} catch (e) {
console.log("MQTT message (raw):", payload);
// Handle plain string payload
}
});
6. Error Codes and Result Handling¶
The MQTT-related handlers use Result.ok(...) and Result.fail(code, ...) to wrap responses. Relevant error codes include:
MQTT_CONNECT_FAIL– connection attempt to the broker failed (invalid config, network error, auth error, etc.)MQTT_CURRENT_NOT_CONNECTED– any operation that requires an active MQTT connection (subscribe, unsubscribe, publish) was called while the client isnullor disconnected.PARAMETER_ERROR– request payload is missing required fields or cannot be parsed.FAIL– generic failure (e.g. subscribe / publish returnedfalse).
On the JS side, you should consistently check:
const result = JSON.parse(response);
if (!result.success) {
console.error("MQTT error:", result.code, result.message);
}
7. Typical MQTT Usage Flow (From JavaScript)¶
- Configure & Connect
- Build an
MqttConfigobject. - Call
connectMqttwith the JSON config. - Wait for connectMqttCallBack with
success = true.
- Build an
- Subscribe to Topics
- For each topic, call
mqttSubTopicwith{ topic, qos }.
- For each topic, call
- Register Message Handler
- Register
mqttMsgArrivedCallBackto process incoming messages.
- Register
- Publish Messages
- Use
mqttPublishMsgto send commands or data to the broker.
- Use
- Unsubscribe & Cleanup
- When leaving the screen or no longer needing a topic, call
mqttUnSubTopic. - MQTT disconnect is currently handled inside the native layer (no explicit
disconnectMqttbridge handler is exposed inBaseWebViewActivity).
- When leaving the screen or no longer needing a topic, call
8. State Update¶
Once all handlers are registered, dispatch({ type: "SET_BRIDGE_INITIALIZED", payload: true }) updates the app’s state to indicate that the bridge is now initialized. This flag prevents re-initialization on future calls, ensuring the app maintains a single point of communication between JavaScript and the WebView.
9. Function Summary¶
The setupBridge function provides an organized and secure method for handling various Bluetooth, QR code, and MQTT-based interactions within the app. Each handler validates and processes data, ensuring consistent state management and reducing potential issues with asynchronous data handling. This structure enables seamless communication between the WebView and the app’s state, allowing for complex, multi-source data exchange with a single initialization call.
Summary¶
The BLE technology in use here appears to be:
- Central-Peripheral with GATT Profile: The app (central) scans, connects, and interacts with peripherals, retrieving GATT services and characteristics.
- IoT Integration via MQTT: BLE data or status updates may be sent to a server using MQTT, suggesting integration with a broader IoT ecosystem.
- Hybrid App Structure: The presence of
WebViewJavascriptBridgeindicates a WebView or hybrid app setup, enabling BLE functionality in a cross-platform environment.
This setup would allow a mobile app to scan, connect to, initialize, and interact with BLE devices in an IoT context, potentially using MQTT to send data to a cloud service or backend.
This separation keeps the MQTT connection and low-level handling in native code, while the WebView/JavaScript layer only needs to use a small set of bridge methods and callbacks to integrate MQTT into the UI and business logic.