Using MQTT on Android and Common Problem Solutions

1. Basic Usage of MQTT

1. Add Dependencies

// build.gradle (app)
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'

2. Configure Permissions

<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<service android:name="org.eclipse.paho.android.service.MqttService" />

3. Core Code Example

class MqttManager(context: Context) {
    private val serverUri = "tcp://mqtt.eclipse.org:1883"
    private val clientId = "android-client-${System.currentTimeMillis()}"
    private val qos = 1
    private var mqttClient: MqttAndroidClient? = null

    init {
        mqttClient = MqttAndroidClient(context, serverUri, clientId).apply {
            setCallback(object : MqttCallback {
                override fun messageArrived(topic: String, message: MqttMessage) {
                    Log.d("MQTT", "Received: ${String(message.payload)}")
                }
                override fun connectionLost(cause: Throwable) {
                    Log.e("MQTT", "Connection lost", cause)
                }
                override fun deliveryComplete(token: IMqttDeliveryToken) {}
            })
        }
    }

    // Connect to the server
    fun connect() {
        try {
            val options = MqttConnectOptions().apply {
                isCleanSession = true
                keepAliveInterval = 60
                userName = "username" // Optional
                password = "password".toCharArray() // Optional
            }
            mqttClient?.connect(options, null, object : IMqttActionListener {
                override fun onSuccess(asyncActionToken: IMqttToken) {
                    subscribe("topic/example")
                }
                override fun onFailure(asyncActionToken: IMqttToken, exception: Throwable) {
                    Log.e("MQTT", "Connection failed", exception)
                }
            })
        } catch (e: MqttException) {
            e.printStackTrace()
        }
    }

    // Subscribe to a topic
    fun subscribe(topic: String) {
        mqttClient?.subscribe(topic, qos, null, object : IMqttActionListener {
            override fun onSuccess(asyncActionToken: IMqttToken) {
                Log.d("MQTT", "Subscribed to $topic")
            }
            override fun onFailure(asyncActionToken: IMqttToken, exception: Throwable) {
                Log.e("MQTT", "Subscribe failed", exception)
            }
        })
    }

    // Publish a message
    fun publish(topic: String, message: String) {
        try {
            val mqttMessage = MqttMessage(message.toByteArray()).apply {
                qos = [email protected]
                isRetained = false
            }
            mqttClient?.publish(topic, mqttMessage)
        } catch (e: MqttException) {
            e.printStackTrace()
        }
    }

    // Disconnect
    fun disconnect() {
        mqttClient?.disconnect()
    }
}

2. Common Problems and Solutions

1. Connection Failure

Problem Manifestation:<span>MqttException (0) - MqttException</span>

  • Possible Causes:
    • Network unavailable
    • Incorrect server address/port
    • Protocol version mismatch (e.g., server requires MQTT 3.1.1)
  • Solution:
    val options = MqttConnectOptions().apply {
        mqttVersion = MqttConnectOptions.MQTT_VERSION_3_1_1 // Force protocol version
    }
    

2. Message Loss

Problem Manifestation: Subscription client does not receive messages after publishing

  • Possible Causes:
    • QoS level set too low (0)
    • Client does not maintain persistent session
  • Solution:
    // Use QoS 1 or 2 when publishing
    mqttMessage.qos = 2
    
    // Enable persistent session when connecting
    options.isCleanSession = false
    

3. Background Running Limitations

Problem Manifestation: Disconnects when the app goes to the background

  • Solution: Use a foreground service to maintain connection
    // Start in Service
    val notification = NotificationCompat.Builder(this, "mqtt_channel")
        .setContentTitle("MQTT Service")
        .setSmallIcon(R.drawable.ic_notification)
        .build()
    
    startForeground(1, notification)
    

4. Certificate Verification Failure (SSL/TLS)

Problem Manifestation:<span>javax.net.ssl.SSLHandshakeException</span>

  • Solution: Use a custom SSLSocketFactory
    val sslContext = SSLContext.getInstance("TLSv1.2")
    sslContext.init(null, trustAllCerts, SecureRandom())
    options.socketFactory = sslContext.socketFactory
    

5. Memory Leak

Problem Manifestation: Callbacks still execute after Activity is destroyed

  • Solution: Lifecycle binding
    class MainActivity : AppCompatActivity() {
        private lateinit var mqttManager: MqttManager
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            mqttManager = MqttManager(this)
            mqttManager.connect()
        }
    
        override fun onDestroy() {
            mqttManager.disconnect()
            super.onDestroy()
        }
    }
    

6. Duplicate Message Reception

Problem Manifestation: Receiving duplicate messages (QoS 1/2 scenarios)

  • Solution: Implement a message deduplication mechanism
    private val receivedMessageIds = mutableSetOf<String>()
    
    override fun messageArrived(topic: String, message: MqttMessage) {
        val messageId = message.id.toString()
        if (!receivedMessageIds.contains(messageId)) {
            receivedMessageIds.add(messageId)
            // Process message
        }
    }
    

3. Best Practices

  1. Connection Management:
  • Use<span>keepAliveInterval</span> to maintain heartbeat
  • Implement automatic reconnection mechanism
  • Thread Safety:
    • MQTT callbacks execute on non-UI threads, need to use<span>runOnUiThread</span> to update UI
  • QoS Selection:
    • Real-time data: QoS 0
    • Important notifications: QoS 1
    • Critical commands: QoS 2
  • Topic Design:
    • Use hierarchical structure:<span>sensors/temperature/room1</span>
    • Avoid using<span>#</span> wildcard to subscribe to too many topics

    By implementing the above methods and considerations, MQTT communication can be integrated stably and efficiently in Android applications.

    Follow me for more knowledge or to contribute

    Using MQTT on Android and Common Problem Solutions

    Using MQTT on Android and Common Problem Solutions

    Leave a Comment