Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Smack 4.1 Re-connection issue

I am developing a Chat application in which

  1. I have a background service that is continuously running.
  2. The Application is running smoothly and I am able to receive and send messages without any issue.
  3. I am using default Smack 4.1 Re-connection Manager, which is connecting as soon the connection is lost (Almost 90% of the time).

But sometimes, when the connection is lost, the Smack Re-connection Manager stops working.

I have not been able to find out what is causing this issue.

When I check my phone, after a delay of 12 to 14 hours, I can see the Android service is still running, but the XMPP re-connection has stopped working. I must then force-stop the application in order to then restart the service and have it connect to the XMPP server again.

My assumption is that is happening when "Phone Network switches from Wifi-to-Data or Data-to-Wifi". In the meantime, Smack 4.1 Re-connection Manager stops working. Although, I am not sure about it.

And I have Questions

  1. What is the use of PingManager? Can PingManager be helpful in this case?
  2. What is StreamManagement? How can I enable it? Is it helpful in this case?

Any other solutions to conquer this problem? I am thinking of the following solutions:

  1. To use GCM as well with XMPP, so when XMPP failed to reconnect, I can get the PUSH and connect again on the basis of the PUSH.
  2. Use Alarm Services. (PROBLEM : Messages that were in Queue because of the connection lost will be dependent on next alarm schedule)
  3. Every time connect XMPP when user returns to the Application. (PROBLEM : Messages that were in Queue because of the connection lost will be dependent on the User returning back to app)
like image 991
shanraisshan Avatar asked May 12 '15 06:05

shanraisshan


2 Answers

A little late but maybe it helps others. I am not XMPP expert but i came across a presentation of Ignite Realtime called "XMPP and Android" and it points out this issue and how to handle this. page 1

page 2

It offers using Server ping with Alarm Manager ideally with 30 min intervals.

Also I found another post related to this issue and offers 3 option. It worths to see. https://ramzandroidarchive.wordpress.com/2016/03/14/handling-connection-break-issue-in-smack-4-1/.

like image 95
ugur Avatar answered Nov 08 '22 22:11

ugur


It is somewhat of an old question but I have personally struggled with maintaining a persistent connection using Smack in the past and had to figure out the answers based on multiple resources. I will try to address my findings in this answer.

Notice - do not use isConnected() method of your AbstractXMPPConnection to determine whether the connection is active. This is a mere internal state of the object. It has no real way of knowing whether a connection is actually active.
The only way to be sure is by pinging, either by the server or by the client.

The persistent connection route - NOT recommended

First thing to understand - if you indeed want a persistent connection with the server (and I've been this route and do not recommend it - more about it below), you have to do things manually.
On the server side - make sure you disconnect clients that are idle after a reasonable amount of time (too long and you will lose messages, as the connection will be terminated but the server will still consider it active. too short and you will have repeating broken connections if your users have poor internet connection. I personally user 30 seconds. I guess anything between 30 and 60 seconds is fine). This will allow you to detect on the client side when connection with the server is lost.
Second thing to do is implement the ConnectionListener Smack interface. It contains callbacks to various connection events such as connectionClosedOnError that allow you to resume your connection with the server.
Then you should listen to network connectivity events - if the user has changed networks you should probably disconnect and reconnect again.
This is arguable, but I personally had problems with keeping the connection alive this way, relying on server pings and callbacks. If you have too - you should ping the server from the client to be certain.
Smack has the ServerPingWithAlarmManager class precisely for that - it pings the server every 30 minutes. Alas, for me this did not work, as some OEMs (Xiaomi I am looking at you) have restricted background tasks in their custom Android skins. The way that worked for me was to ping the server using a custom PingManager. Get it using PingManager.getInstanceFor(connection) and schedule it to run every X minutes (7 minutes worked for me) using an AlarmManager.
For this ping manager you should register a PingFailedListener that will reconnect on its pingFailed() method.
All this sounds bad? here is the worst part - the whole things falles apart on api >= 23. This all works fine until Doze and App Standby come into play. This is why if you go the persisted connection route - you have to ask for the dreaded ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission which allows your app to bypass Doze and App Standby.
Congratulations! assuming Google did not ban your app from the Play Store for asking for the whitelist battery optimizations permission recklessly - you now have a reliable and persistent xmpp connection with your server inside an app that drains battery like crazy even when the user does nothing to engage with it for a long period of time. Not an ideal solution, as I have previously stated.

The better solution - FCM high-priority messages

After understanding the problems with persisting a connection with the server - this is the method I recommend using.
Do not try to fight the network connection and the battery optimizations that get more restrictive with each Android version.
Integrating FCM messages into your server will allow your users to receive the messages in real time even when the device is in Doze mode.
This is the approach I use nowadays and it works pretty well with the benefit of much much lower battery drainage.
I do not ping the server, and the server does not ping my app. when the device changes networks or the connection is closed due to am error - I just disconnect the connection.
While connected, messages are received by a Message type StanzaListener.
While disconnected, messages are received by the FirebaseMessagingService() implementation.
No need to persist the connection. No need to fight the system. No huge battery drainage.

Hope this answer helps somebody in the future and saves them some time, as it took me a lot of time, effort and trial and error to get to a solution I am satisfied with.

like image 2
Ilya Krol Avatar answered Nov 08 '22 21:11

Ilya Krol