How to design a basic socket client application

First of all I apologize for the long text, and for the various points asked. I thought it best to throw everyone into a question just because they are interrelated and because I think anyone with experience in the type of application described (an Android client that maintains a continuous socket connection by receiving and sending data) is able to give their opinion regarding all or most of these points. Basically I'm needing to make design decisions and I have doubts about how it would be the "Android recommended way" to do.

I am trying to define what would be the minimum skeleton required to have a dsse-like application, that is, that connects to a server via socket (WebSocket, Socket.IO TCP, etc.) and can exchange messages with him). I don't have a specific usage example; chat is not one of those examples, because in this case the ideal would be to use push notification.

This connection should not be tied to the Activities of the application, that is, Messages can be received even after the user has left the application.

Questions I'm trying to answer:

  1. Is it recommended to delegate connection management (connect, disconnect) and sending messages to a separate class (e.g. a Singleton)?

  2. Using broadcasts to pass an incoming message to the current Activity seems to be the appropriate choice in the case of a relatively simple application architecture. Does anyone disagree? Would a Event Bus or coupling (bind) be preferable to a service?

  3. The system is expected to kill the application process to free up memory, but the connection must be reestablished as soon as memory is available for this. What is the best way to do this? Scheduling periodic checks of the connection via AlarmManager does not seem appropriate, nor use startForeground(), which besides not ensuring that the process is killed (just makes it less likely to happen) still displays the user a bothersome notification that the app is running, which on top of that if clicked leads to a screen offering the option to terminate the app. I thought that returning START_STICKY in the Service.startCommand() method could help in this sense, but it doesn't seem to be done for it.

  4. I'm not sure how connection reestablishment should work in order to process the queue of messages to send. I suppose it involves listening to the broadcast CONNECTIVITY_CHANGE or else a reestablished connection event. What is the best way to do this by ensuring that the app tries to send the messages when possible?

  5. After there is consensus regarding the modeling of the application, I believe that it will be necessary to include Wake Locks in the code to prevent the device from entering sleep mode while processing incoming or outgoing messages in the send queue. But I'm not sure which part of the code includes them and would like recommendations. For this I also have the following additional considerations:

    A) once the connection is established, the device can enter sleep mode quietly that the arrival of data will wake you up. However, it is not guaranteed that this data will be processed in its completeness without a Wake Lock , before the device goes back to sleep;

    B) if the application is sleeping and the connection becomes available, a desirable requirement may be that the queue of messages to be sent be processed in its completeness before the device goes back to sleep. I'm not sure where I should add the Wake Lock in this case.

    C) even if the library that works with the chosen communication protocol is prepared to work with Android (that is, it manages the input / output on one or more secondary threads and not on the thread of UI), it may be that it is programmed to run its callbacks (in particular the callback that receives messages) in the thread of UI. This can affect the way I arrange the Wake Locks in my code.

  6. Is there a need to keep an active thread in a continuous loop to keep the connection open? How can I avoid this?

I thank any feedback. I threw this question also in SO in English .

Author: Comunidade, 2014-10-16

2 answers

It all depends on the application you want to do, I will answer in a generic way so that you apply the use on different occasions.

  1. it is recommended to delegate connection management (connect, disconnect) and sending messages to a separate class (by example a Singleton)?

See this: https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons

Some programmers consider Singleton an" anti-pattern", avoid using it, it can brings you barriers while developing the application. But yes, you should have one class specialized in receiving connections, another to create connections and one shared by the previous two to transmit your messages, reduce the scope to this at this stage to avoid any unnecessary coupling (Activties for example, these classes do not know that they exist).

  1. the use of broadcasts to pass an incoming message to the current Activity seems to be the appropriate choice in the case of a relatively simple application architecture. Does anyone disagree? It would be preferable an event Bus or else coupling (bind) to a service?

The application is simple, so its code should be simple, but on occasions when multiple Activities can receive messages, notifications, alerts, etc delegate all these tasks to a "Bound Service".

Http://developer.android.com/guide/components/bound-services.html

Use the service to send and receive messages over the network and it can create notifications about new messages.

  1. the system is expected to kill the application process to free up memory, but the connection must be reestablished as soon as there is memory available for this. What is the best way to do this? Schedule periodic connection checks via AlarmManager does not seem to me proper, and neither use startForeground (), which besides not ensuring that the process is killed (just makes it less likely to happen) it still displays the user an annoying notification that the app is running, which on top of that if clicked leads to a screen offering the option to terminate the application. I thought returning START_STICKY in the Service method.startCommand() could help in this sense, but it does not seem to be made for it.

In general, keep this simple:

  1. When creating the service: Open Connection

  2. Close service( not guaranteed to be called): Try to close connection.

  3. Service doesn't have an "onServiceRestoreState" method or something, so you can take advantage of the methods onDestroy, onUnbind, onTrimMemory, startForeGround, stopForeground to undo the connection. use this as well: START_REDELIVER_INTENT . This will that onStartCommand is called again and you can reestablish the connection.

About AlarmManager: https://stackoverflow.com/questions/19411744/android-when-a-service-is-killed-how-can-we-persist-the-service-state-for-late

The use of AlarmManager is designated for tasks that must be performed from time to time, as it says in the question above,

Intensive use of a service is hostile.

Tip: you don't need to be listening for new messages or notifications from a server all the time (imagine the memory load of a server with multiple open connections, many perhaps without transmitting data for long periods). Do periodic checks (let it be every 1 minute if so necessary, open connection to the server, check and close it).

  1. I'm not sure how connection reestablishment should work in order to process the queue of messages to be sent. I suppose involve listen to the broadcast CONNECTIVITY_CHANGE or else a connection reestablished. How best to do this, ensuring that the app try to send the messages when possible?

Is trivial, but there are aspects that should be considered: yes, check CONNECTIVITY_CHANGE to know whether or not there is connection available

Monitoring connections

Handle Socket, Tcp, etc exceptions (you can have Network on the device, but the server may be offline or some network error occurs) as a lost connection in your implementation. when performing any action: attempt to send message, read server notifications check this "disconnected state" and if disconnected try reconnecting to send the request.

When you are offline or receive an exception clean the resources (Dispose Socket, Tcp, etc), when you try to reconnect you will create them again.

But in short: when perform network operations you treat the state of the connection.

  1. after there is consensus regarding the modeling of the application, I believe it will be necessary to include Wake Locks in the code to prevent the device enters sleep mode while processing incoming messages or placed in the send queue. But I'm not sure which part of the code includes them and would like recommendations. For this I have also the following considerations additional:

A) once the connection established, the device can enter sleep mode quietly that the arrival of data will wake you up. However, it is not guaranteed that these data is processed in its completeness without a Wake Lock, before the apparatus to go back to sleep;

The WakeLook is designed to prevent the device from going into Idle, but this is not a livable means of waking you up when you receive a notification/message.

The most indicated in this case would be the WifiLock which prevents the network device from being turned off when the mobile device goes into Idle.

You will use the aquire method to say that the device cannot turn off your network device, and release after closing connections, terminating the service, or terminating application actions.

B) if the application is asleep and the connection become available, a desirable requirement may be that the row of messages to be sent are processed in their completeness before the apparatus go back to sleep. Not sure where I should add the Wake Lock in that case.

For both WakeLock and WifiLock:

 wakeLock.acquire();
 this.processQueue();
 wakeLock.release();

C) same as the library that works with the communication protocol chosen is prepared to work with Android (i.e., manage input / output on one or more threads in the thread of UI), it may be that it is programmed to run your callbacks (in particular the callback you receive messages) in the UI thread. This may affect the way I dispose the Wake Locks in my code.

Yes, keep the WakeLocks inside the classes that manage the messages (like coupling, maybe an IoC here) and try to keep the code "Thread-Safe", the rest is on account of dependency injection.

  1. there is a need to keep an active thread in continuous loop to maintain the connection open? How can I avoid this?

If you need the connection open all the time: yes (not recommended) in some Thread it will need to be (this is inevitable), it does not need to be looped, but the process can be Idle, waiting for the messages. Keeping a connection open for long periods is well prone to failure( especially mobile networks), consider that the client questions the server about notifications over long periods instead of keeping the connection open and wait for messages.

 5
Author: Leonardo Bosquett, 2017-05-23 12:37:28

I ended up implementing some of these requirements and solving most of the doubts. Follow what I learned from it.

Before though, a recommendation: socket on Android may not be what you want. When I implemented, what I really needed was push notifications (e.g. with Google Cloud Messaging). Therefore, before deciding on a certain technology, study all the possibilities.

It is recommended to delegate the management of connection (connect, disconnect) and sending messages to a separate class (by example a Singleton)?

Whatever. If Singleton is used, it will be so closely connected with the service that the result will be the same as implementing only the service. As spoken in @LeonardoBosquett's answer, some people consider Singleton an anti-pattern and prefer not to use it.

The use of broadcasts to pass an incoming message to the Activity current seems to be the appropriate choice in the case of a relatively simple application. Does anyone disagree? It would be preferable a Event Bus or else coupling (bind) to a service?

Some prefer bind , others broadcasts. If the second option is used it is preferable to make local broadcasts with LocalBroadcastManager, which have better performance compared to global broadcasts.

The system is expected to kill the application process for release memory, but the connection must be reestablished as soon as there is memory available for this. What is the best way to do this? (...) I found that returning START_STICKY in the Service method.startCommand() it could help in this regard, but it does not seem to be made for it.

Was wrong, START_STICKY is just that. But according to the documentation the service is restarted with an intent null, so if there are parameters to read from intent the solution is to use START_REDELIVER_INTENT.

Not sure how connection reestablishment should work, in order to process the queue of messages to be sent. I suppose it involves listen to broadcast CONNECTIVITY_CHANGE or else a connection event reinstated. How best to do this, ensuring that the app try to send the messages when possible?

This part has not been implemented and I don't have an immediate answer to that. In an established connection event generated by the communication library the service can process the queue as soon as it acquires a Wake Lock.

Doubt: given the way TCP works, I suspect that a connectivity loss CONNECTIVITY_CHANGE may not trigger a IOException Immediately; you may need to warn the service that the connection has been lost in order for it to actively terminate the connection. If this is the case, a way would have to be thought of to communicate to the service that the loss of connectivity has occurred. Of in the same way, you have to think about how to warn the service about a connectivity restoration to try to connect again (for battery saving, it is recommended that a exponential back-off be done, which in case of restored connectivity can restart from scratch).

C) same as the library that works with the communication protocol be prepared to work with Android (i.e., manage input / output in one or more threads secondary and not in thread), it may be that it is programmed to execute its callbacks (in particular the callback that receives messages) in the UI. This can affect the way I arrange the Wake Locks on my code.

The concern here is when to acquire the Wake Lock. There are two situations in which it is required: when receiving a message and when processing the queue of messages to be sent. In the first case it can be acquired as soon as it is received a message and released as soon as you finish processing it. In the second it can be acquired when reconnecting with the server.

B) if the app is asleep and the connection becomes available, a desirable requirement may be that the message queue to send is processed in its completeness before the appliance returns to sleep. I'm not sure where I should add Wake Lock in that case.

The concern here is when release the acquired Wake Lock to process the queue of messages to be sent. It would need to be removed in two locations: in case queue processing ends (either naturally or if a IOException signalling connection break occurs) and in the onDestroy() of the service. The first case in terms of code would look something like this (borrowing from @LeonardoBosquett's code):

wakeLock.acquire();
try {
    this.processQueue();
catch (IOException e) {
    // Lida com a exceção de alguma forma
} finally {
    wakeLock.release();
}

There is a need to keep an active thread in a continuous loop to keep the connection open? How can I avoid this?

Depends on how the communication library is implemented. Preferably the thread can be blocked waiting for the arrival of a message, it does not need to be running.

 5
Author: Piovezan, 2015-03-21 22:00:34