How to design a basic socket client application

Asked

Viewed 1,891 times

28

First of all I apologise for the long text, and for the various points raised. I thought it best to cast everyone on a question just because they are interrelated and because I think anyone with experience in the type of app described (an Android client that maintains a continuous socket connection by receiving and sending data) is able to give your opinion on all or most of these points. Basically I need to make design decisions and I have doubts about what would be the "recommended Android way" to do.

I am trying to define what would be the minimum skeleton necessary to have a dsse type application, that is, to connect to a server via socket (Websocket, Socket.IO, TCP, etc.) and can exchange messages with him). I don’t have a specific use example; chat is not one of these examples, because in this case the ideal would be to use push notification.

This connection shall not be linked to 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. It is recommended to delegate connection management (connect, disconnect) and sending messages to a separate class (for example a Singleton)?

  2. The use of Broadcasts to pass a received message to the Activity current 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) at a service?

  3. The system is expected to kill the application process to free up memory, but the connection should be reestablished as soon as there is memory available for it. What’s the best way to do this? Schedule periodic connection checks via AlarmManager I don’t think it’s appropriate startForeground(), that in addition to not ensuring that the process is killed (it just makes it less likely to happen) still displays to the user an uncomfortable notification that the application is running, which on top of that if it is clicked leads to a screen offering the option to terminate the application. I thought I’d return START_STICKY in the method Service.startCommand() could help in that direction, but he doesn’t seem to be made 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 a reestablished connection event. What is the best way to do this, ensuring that the app tries to send messages when possible?

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

    a) Once the connection is established, the device can enter Sleep mode calmly that the arrival of data will awaken you. 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 dormant and the connection becomes available, a desirable requirement may be that the message queue to be sent be processed in its entirety before the device goes back to sleep. I’m not sure where to add Wake Lock in that case.

    c) Even if the library that works with the chosen communication protocol is prepared to work with Android (that is, manage the input/output in one or more threads secondary and not in the thread of UI), may be that it is programmed to execute your callbacks (in particular the callback receiving messages) on thread of UI. This can affect the way I dispose Wake Locks in my code.

  6. There is a need to maintain a thread Continuous loop active to keep the connection open? How can I avoid this?

I appreciate any feedback. I also put this question on ONLY in English.

  • 2

    Piovezan, did you ever consider GCM as a way to implement Push Notifications? I have a suggestion to make, but it involves the same, when I have time I put an answer.

  • So, the idea in this case is not to use push notification; I have as an example an application that keeps the connection open only while the user is "logged in" on it for example, and lose the connection when dropping. There is no obligation for messages to be delivered to the customer if the connection is closed. They are short-lived and expendable data, for example in an application that runs a central alarm installed in a residence.

    1. Matter of taste, I think Service is already the class that takes care of network issues. 2. Broadcast is the simplest to use, but would be more "clean" a direct bind between Activity and Service. I don’t know how security/privacy issues look when using broadcast; a priori each application needs permission to receive a particular broadcast. 3. You can run Service in a separate process (process and isolatedProcess in the manifest). It makes you use less memory and decreases the chance of being killed. At least in my use, never happened of a running service so be in fact dead.

2 answers

5


I ended up implementing part of these requirements and solving most of the doubts. Follows what I learned from this.

Before though, a recommendation: socket on Android might not be what you want. When I implemented, what I really needed was push Notifications (for example, with Google Cloud Messaging). Therefore, before deciding on a particular technology, study all possibilities.

It is recommended to delegate connection management (connect, disconnect) and send messages to a separate class (by example of 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 reply, some people consider Singleton a anti-pattern and prefer not to use.

Using Broadcasts to send an incoming message to Activity current seems to be the appropriate choice in the case of an relatively simple application. Anyone disagree? It would be preferable a Event Bus or else coupling (bind) to a service?

Some prefer bind, other Broadcasts. If used the second option is preferable to do local Broadcasts with LocalBroadcastManager, that perform better than global Broadcasts.

The system is expected to kill the application process to release memory, but the connection should be reestablished as soon as there is memory available for this. What is the best way to do this? (...) I thought to return START_STICKY in the Service.startCommand() method could help in that direction, but it doesn’t seem to be made for it.

I was wrong, START_STICKY serves precisely for this. However according to the documentation the service is restarted with an Intent null, then if there are parameters to read from the Intent the solution is to use START_REDELIVER_INTENT.

I’m not sure how connection reestablishment should work, in order to process the queue of messages to send. I suppose it involves listen to the broadcast CONECTIVITY_CHANGE or else a connection event reestablished. What is the best way to do this, ensuring that the app try to send messages when possible?

That part has not been implemented and I have no immediate response 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_CHANGE connectivity loss may not trigger a IOException immediately; it may be necessary to notify the service that the connection has been lost for it to actively terminate the connection. If this is the case, a way of communicating to the service would have to be thought out that the loss of connectivity occurred. Similarly, you need to think about how to warn the service about a reestablishment of connectivity to try to connect again (for battery savings, it is recommended to do a back-off exponential, that in case of restored connectivity can restart from scratch).

c) Same as the library working with the communication protocol chosen be prepared to work with Android (i.e., manage input/output on one or more secondary threads and not on UI thread), it may be that it is programmed to run its callbacks (in particular the callback that receives messages) in the UI. This may affect the way I dispose of Wake Locks in my code.

The concern here is when to acquire the Wake Lock. These are two situations where it is needed: when receiving a message and processing the queue of messages to send. In the first case it can be acquired as soon as a message is received and released as soon as it is finished processing it. In the second it can be acquired when reconnection with the server occurs.

b) If the application is dormant and the connection becomes available, a desirable requirement may be that the queue of messages send be processed in its completeness before the device returns to sleep. I’m not sure where to add the Wake Lock in that case.

The concern here is when to release the Wake Lock acquired to process the queue of messages to send. It would need to be removed in two locations: if the processing of the queue ends (either naturally or if a IOException signaling connection break) and in onDestroy() service. The first case in terms of code would look something like this (borrowed 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 continuous loop for keep the connection open? How can I avoid this?

It depends on how the communication library is implemented. Preferably the thread can lock itself awaiting the arrival of a message, it is not necessary to stay running.

5

Everything depends on the application you want to make, I will respond in a generic way so that you apply the use in different occasions.

  1. It is recommended to delegate connection management (connect, disconnect) and messaging to a separate class (by example of a Singleton)?

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

Some programmers consider Singleton an "anti-pattern", avoid using it, it can bring you barriers while developing the application. But yes, you must have a 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. 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? It would be preferable an Event Bus or else coupling (bind) to a service?

The application is simple, so your code should be simple, but at times when multiple Activities may 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 should be reestablished as soon as it exists memory available for this. What is the best way to do this? Scheduling periodic connection checks via Alarmmanager does not seem to me appropriate, and not use startForeground(), which in addition does not guarantee that the process is killed (only makes it less likely to happen) still displays to the user a bothersome notification that the application is running, which on top of that if clicked leads to a screen offering the option to terminate the application. I thought to return START_STICKY in the Service.startCommand() method could help with this sense, but it doesn’t seem to be done for it.

in general, keep it simple:

  1. When creating the service: Open connection

  2. Closing the service (not guaranteed to be called): Trying to close connection.

  3. Service does not 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 too: START_REDELIVER_INTENT. This will cause onStartCommand to be called again and you can reestablish the connection.

on 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 designed for tasks to be performed from time to time, as stated 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 (that is 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 send. I suppose involves listening to the broadcast CONECTIVITY_CHANGE or else an event of reestablished connection. What is the best way to do this, ensuring the app tries to send messages when possible?

It is trivial, but there are aspects that should be considered: Yes, check CONECTIVITY_CHANGE to know whether or not you have connection available

Monitoring connections

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

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

But in short: When perform network operations you treats 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 to the code to prevent the device enters Sleep mode while processing incoming messages or placed in the upload queue. However, I’m not sure which part of the code include them and would like recommendations. I have for this also the following additional considerations:

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 are processed in its entirety without a Wake Lock, before the go back to sleep;

The Wakelook is designed to prevent the device from entering Idle, but this is not a living means of waking you up when you receive a notification/message.

The most appropriate in this case would be Wifilock which prevents the network device from shutting down when the mobile device enters Idle.

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

b) If the app is dormant and the connection becomes available, a desirable requirement may be that the queue of messages to be sent is processed in their entirety before the go back to sleep. I’m not sure where to add the Wake Lock in this case.

for both Wakelock and Wifilock:

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

c) Same as the library working with chosen communication protocol is prepared to work with Android (i.e., manage input/output in one or more threads secondary and not in the UI thread), it may be programmed to execute your callbacks (in particular the callback you receive messages) in the UI thread. This may affect the way I dispose Wake Locks in my code.

Yes, keep the Wakelocks within the classes that manage the messages (like coupling, maybe an Ioc here) and try to keep the code "Thread-Safe", the rest is because of dependency injection.

  1. Is there a need to keep an active thread in a continuous loop to keep 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 in loop, but the process may be in Idle, waiting for the messages. Keeping a connection open for long periods is quite prone to crashes (especially mobile networks), consider asking the server about notifications over long periods instead of keeping the connection open and waiting for messages.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.