We’ve had problems with running the hivemq client in the background on android in a foreground service. Our service is running as a location foreground service, and it reliably wakes up and does stuff when we get a location or a work manager timer goes off. However, sometimes when we talk to the hivemq client, hivemq reports that it is connected, but it isn’t actually communicating with the server to send or receive messages. We also see that keepalive pingreqs fail to send while the app isn’t in the foreground or the device plugged in. With the device plugged in, pings seem to flow out as expected even with the app in the background. When looking into the hivemq code, it looks like it is using an executor to schedule ping tasks, which doesn’t appear to work when the app is in the background.
While trying to debug/work around this issue, we’ve found that if the client gets into the disconnected-but-reporting-connected state, it can be resolved by telling it to disconnect from the server and reconnect, either from a watchdog in a work manager, or manually calling disconnect in the debugger.
We’ve also run into a problem with some NAT routers having a short lived expiration (5 minutes) and dropping our tcp connection to the server, which is likely impacting some of these findings. Since hivemq isn’t sending pings, we can’t keep these sessions alive by shortening our keepalive.
The pings not being sent out were verified in wireshark and breakpoints in the debugger.
I’m not sure if this is one issue or multiple issues, so I’m presenting what I’m seeing in the hopes that someone on the hivemq team can look into this.
Thank you for the detailed explanation and the debugging information. That was very helpful.
Based on what you described (pings not being sent while the app is in the background and on battery, NAT timeouts around 5 minutes, and the client remaining in a “connected” state), this appears to be related to Android background execution and power management behavior rather than a broker-side issue.
Recommended Actions
Enable automatic reconnect
Ensure .automaticReconnectWithDefaultConfig() is set so the client recovers when the stale socket is detected.
Use a short keepalive (≤60 seconds)
Set keepAlive(60) to stay ahead of NAT timeouts when background execution works.
Verify foreground service configuration
Confirm startForeground() is called and foregroundServiceType is declared correctly.
Disable battery optimization for the app (where possible)
On many devices, battery optimization can restrict background network scheduling.
Consider adding an application-level watchdog
For example, monitor inbound/outbound activity and force a reconnect if no traffic is observed for 2–3 keepalive intervals. Since you’ve already confirmed that a reconnect resolves the state, this can be an effective safeguard.
Optional: Server-side session expiry
Configure session-expiry-interval on the broker to clean up stale sessions.
This behavior is expected under Android power management. However, if you’d like to share your client configuration, Android version range, and HiveMQ client version, we can provide more targeted recommendations.
I’m working with Erik on the same product, and I want to share some feedback relating to the suggestions you provided:
Regarding 1, 3, and 5, we have done this
Regarding 2, this is a non-starter due to the battery impact such a short interval would have. In practice we use 20m keep alive interval, or up to an hour. There should be no problem with this sort of interval because the Android framework allows for long running background polling style tasks with the WorkManager API.
Regarding #4, we have done this to the extent possible, but we can’t poll with such a small interval because it will burn up our battery.
Regarding #5, we have a watchdog, but when the connection is lost the HiveMQ library seems to be unaware that it has happened, so there’s no way for us to know whether we have to reconnect. We’re left with reconnecting every time because of that.
Regarding #6, we have last will in place, but that doesn’t help us when we want to push information to the Android client, which is the point of using MQTT in the first place.