Fix heartbeats on Android when CPU sleeps #2
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
We're using RabbitMQ along with the Java client library for http://telerivet.com/ to send push messages to Android phones.
For the most part, RabbitMQ client library works great for push messaging on Android, except that heartbeats don't work when the CPU sleeps. It's important to let the CPU sleep as much as possible to maximize battery life -- for example the phone would run for only 10 hours if we use a WakeLock to keep the CPU on, but it could last for 3 days if we let the CPU sleep most of the time and just wake it up every 5 minutes to send a heartbeat.
There are a few assumptions in com.rabbitmq.client.impl.HeartbeatSender that break on Android when the CPU sleeps:
To fix the first issue, I added a method ConnectionFactory.setHeartbeatExecutor that allows application code on Android to pass in an executor that wraps Android's AlarmManager to wake up the CPU if it's asleep.
Here are the relevant parts from my application code as an example:
The application code to implement ScheduledExecutorService is pretty tedious, but it only requires a small change in the RabbitMQ Java client library to use a custom executor instead of Executors.newSingleThreadScheduledExecutor().
Also, I removed the optimization to skip sending heartbeats if there has been recent activity. It doesn't work on Android because System.nanoTime() doesn't include the time that the CPU is asleep. As a result, the client will miss heartbeats and the server will shut down the connection.
On Android, real time can be determined with SystemClock.elapsedRealtime(), but this isn't cross-platform.
Also, the optimization to skip sending heartbeats after recent activity was actually less efficient on Android because it requires waking the CPU up 2x as often. To maximize battery life, it is better to wake up the CPU the absolute minimum necessary (even if it may require sending more heartbeat frames if there has been recent activity).