Skip to content

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:

  1. Connect to MQTT broker via connectMqtt
  2. Subscribe to topics via mqttSubTopic
  3. Receive messages via the mqttMsgArrivedCallBack JavaScript callback
  4. Publish messages via mqttPublishMsg
  5. Unsubscribe via mqttUnSubTopic when 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:

  • connectMqtt
  • mqttSubTopic
  • mqttUnSubTopic
  • mqttPublishMsg

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 data is empty → returns Result.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)

  • data is a JSON string with:
    • topic – MQTT topic string (e.g. "devices/abc123/status")
    • qos – QoS level (0, 1, or 2)

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);
  }
});
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);
  }
});

Native Logic (simplified)

  1. Parse data into a JSONObject. 2.Extract topic and qos.
  2. Retrieve MQTT client:

    MqttClientUtil mqttClientUtil = bleService.getMqttClientUtil();
    

  3. Check connection:

    • If mqttClientUtil == null or !mqttClientUtil.isConnected()Result.fail(MQTT_CURRENT_NOT_CONNECTED, false)

Call:

boolean subscribe = mqttClientUtil.subscribe(topic, qos);
6. Return: - If subscribe == trueResult.ok(true) - Otherwise →Result.fail(FAIL, false)

On any exceptionResult.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)

  1. Parse topic from JSON.
  2. Retrieve MqttClientUtil via bleService.getMqttClientUtil().
  3. If MQTT is not connected → Result.fail(MQTT_CURRENT_NOT_CONNECTED, false).
  4. Call:
    boolean ok = mqttClientUtil.unSubscribe(topic);
    
  5. If okResult.ok(true) Else → Result.fail(FAIL, false)
  6. On exceptionResult.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)

  • data is 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)

  1. Parse topic, content, and qos from JSON.
  2. Retrieve MqttClientUtil via bleService.getMqttClientUtil().
  3. Check connection; if disconnected → Result.fail(MQTT_CURRENT_NOT_CONNECTED, false).
  4. 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
  }
});
Typical flow: 1. Connect using connectMqtt. 2. Subscribe using mqttSubTopic. 3. Incoming messages on subscribed topics are delivered via mqttMsgArrivedCallBack.

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 is null or disconnected.
  • PARAMETER_ERROR – request payload is missing required fields or cannot be parsed.
  • FAIL – generic failure (e.g. subscribe / publish returned false).

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)

  1. Configure & Connect
    • Build an MqttConfig object.
    • Call connectMqtt with the JSON config.
    • Wait for connectMqttCallBack with success = true.
  2. Subscribe to Topics
    • For each topic, call mqttSubTopic with { topic, qos }.
  3. Register Message Handler
    • Register mqttMsgArrivedCallBack to process incoming messages.
  4. Publish Messages
    • Use mqttPublishMsg to send commands or data to the broker.
  5. 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 disconnectMqtt bridge handler is exposed in BaseWebViewActivity).

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 WebViewJavascriptBridge indicates 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.