简体   繁体   中英

Multiple threads reading from same socket

I am developing an app which displays data from a server. The server is not mine and it is not very stable. Making too many connections crashes the server.

I have one socket to the server in my main activity, but at times I want to open sub activities which read the data and display it. My problem is that I am unable to achieve this with the same socket and have to open a new socket for every activity. Every activity has a thread which does the reading from the socket and updates the UI elements on that activity as needed.

To use the same socket in multiple activities, I tried to close the inputReader of an activity before starting the new activity, but that simply make the application hang. If I leave it open, then the new thread in the new activity never receives any data. Killing the thread before starting the new activity is not possible because the thread is generally blocked by the read() function.

Is there anyway that I can have a centralized thread which does the reading and then sends the data to all the other threads in other activities so that I don't have to open new sockets in every activity?

I feel that this is a very basic thing that I am asking, but yet I am unable to find a solution.

Basically you answered your question yourself:

I can have a centralized thread which does the reading and then sends the data to all the other threads in other activities.

Meaning: of course, such a thing is possible. But you have to sit down, design and implement it. You would start by defining a reasonable interface that allows your other threads to communicate with that central service, something like:

enum RequestType { DO_THIS, DO_THAT };

interface ServerConnectionService<T> {
   List<T> performRequest(RequestType request);
}

Meaning: instead of having your different threads do "low level" talking on that socket, you create an abstraction that allows you to say: "when I need this kind of information, then I use my service; and it returns some specific answer to me). Of course, this is a very generic answer, but well, your question isn't exactly specific either.

The next step would then be to have some central (maybe singleton) implementation of that interface; which runs on its own thread, and can be used by other threads in a synchronized, well-defined way.

Final word of warning: if you don't own that server, and it has low quality and is causing trouble for you - that is not a good setup. Because no matter what you do in your code, if the server doesn't do a good job, users will perceive your app to be the problem. Users don't care if an operation fails because some remote server crashes. So the other aspect in your question is: right now, you are in a bad spot. You should spent some serious time to find ways out of there. Otherwise you will be wasting a lot of time to build workarounds for that server you are dealing with.

A pretty straightforward and simple approach is the following:

  1. You create a new Service which runs in the background and communicates with the server through your socket
  2. The Service receives data from the socket and forwards/broadcasts it to all of your Activities which are interested in receiving it (for example to update the UI) by using the LocalBroadcastManager
  3. All of your Activities implement a BroadcastReceiver and receive the data from your Service inside the onReceive() method

To accomplish that, you should read the introduction to Services and BroadcastReceivers to get an idea of how they work. Also to get a basic overview first, you should read about the available App Components .

EDIT, to answer the question in the comment:

You can always stop the Service by calling stopService() but you can also do it differently if you don't want/need all the functionality of a Service . Instead of a Service you could also create a simple Thread or a HandlerThread which communinicates with the server. From inside of your Thread, you can then forward/broadcast the data to your Activities by using the technique mentioned above ( LocalBroadcastManager ).


Just to give you an example of the basic structure (code untested though):

class SocketThread implements Runnable
{
    static final String SOCKET_DATA_RECEIVED = "com.your.package.SOCKET_DATA_RECEIVED";
    static final String SOCKET_DATA_IDENTIFIER = "com.your.package.SOCKET_DATA";
    private Context context;

    SocketThread(Context c) {
        context = c.getApplicationContext();
    }

    @Override
    public void run() { // code running in your thread
        // fetch data from socket ...
        Intent intent = new Intent();
        intent.putExtra(SOCKET_DATA_IDENTIFIER, data); // store data in your intent
        // send data to registered receivers
        LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
        // your code ...
    }
}

Then you have your Activities, for example MyActivity1 , MyActivity2 , ... MyActivityN . They all register their embedded SocketDataReceiver to receive the broadcast intent SOCKET_DATA_RECEIVED , which is sent by your thread.

Inside your onReceive() methods you can then extract the data from your intent object by using the identifier SOCKET_DATA_IDENTIFIER .

public class MyActivity1 extends Activity
{
    private SocketDataReceiver socketDataReceiver;

    @Override
    protected void onResume() {
        super.onResume();
        socketDataReceiver = new SocketDataReceiver();
        LocalBroadcastManager.getInstance(this).registerReceiver(
                socketDataReceiver, new IntentFilter(SocketThread.SOCKET_DATA_RECEIVED));
    }

    @Override
    protected void onPause() {
        super.onPause();
        LocalBroadcastManager.getInstance(this).unregisterReceiver(socketDataReceiver);
    }

    private class SocketDataReceiver extends BroadcastReceiver
    {
        @Override
        public void onReceive(Context context, Intent intent) {
            // intent contains your socket data,
            // get data from intent using SocketThread.SOCKET_DATA_IDENTIFIER
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM