多个线程从同一个套接字读取数据

4
我正在开发一款从服务器显示数据的应用程序。但是,该服务器不是我的,也不是很稳定。过多的连接会导致服务器崩溃。
在我的主要活动中有一个到服务器的套接字,但有时我想打开读取数据并显示它的子活动。我的问题是,我无法使用相同的套接字来实现这一点,必须为每个活动打开一个新套接字。 每个活动都有一个线程,负责从套接字读取数据,并根据需要更新该活动上的UI元素。
为了在多个活动中使用相同的套接字,我尝试在启动新活动之前关闭一个活动的inputReader,但这只会使应用程序挂起。如果我将其保持打开状态,则新活动中的新线程永远不会收到任何数据。在启动新活动之前杀死线程是不可能的,因为线程通常会被read()函数阻塞。
是否有任何方法可以拥有一个集中化的线程来读取数据,然后将数据发送到其他所有活动中的所有其他线程,以便我不必在每个活动中打开新的套接字?
我感觉这是一个非常基本的问题,但我却找不到解决方案。

算了吧,你永远做不好这件事。使用单个线程从套接字读取数据,并将其工作分发到幕后的任何线程/线程池/线程工作者系统中去。 - user207421
2个回答

1
基本上,您自己回答了您的问题:
我可以有一个集中的线程来读取数据,然后将数据发送到其他活动中的所有其他线程。
意思是:当然,这样的事情是可能的。但你必须坐下来,设计并实现它。您可以从定义一个合理的接口开始,允许您的其他线程与该中央服务进行通信,例如:
enum RequestType { DO_THIS, DO_THAT };

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

意思是:不要让你的不同线程在该套接字上进行“低级”对话,而是创建一个抽象层,使您可以说:“当我需要这种信息时,我使用我的服务;它会向我返回一些特定的答案)。当然,这是一个非常通用的答案,但好吧,你的问题也不是很具体。

下一步将是拥有某个中央(可能是单例)实现该接口;它在自己的线程上运行,并且可以被其他线程以同步的、明确定义的方式使用。

最后警告一句:如果你不拥有那台服务器,并且它质量低劣,给你带来麻烦 - 那就不是一个好的设置。因为无论你在代码中做什么,如果服务器做得不好,用户都会认为你的应用程序有问题。用户不关心操作失败是因为某个远程服务器崩溃了。因此,你问题的另一个方面是:现在,你处于一个糟糕的境地。你应该花费一些时间找到解决方法。否则,你将浪费很多时间来构建处理你正在处理的服务器的解决方法。


嗯,不知怎么的。我想我的建议通过抽象化你的代码与该服务交互的方式更进一步。 - GhostCat

1
一个非常直接简单的方法如下:
  1. 创建一个新的在后台运行的 Service,并通过你的socket与服务器通信
  2. Service从socket接收数据,并通过使用 LocalBroadcastManager 转发/广播给所有感兴趣接收它的活动(例如更新UI)
  3. 所有活动实现一个 BroadcastReceiver 并在 onReceive() 方法中接收来自你的 Service 的数据
为了实现这一点,你应该阅读 Services BroadcastReceivers 的介绍以了解它们的工作原理。另外,为了首先获得基本概述,你应该阅读有关可用 App Components 的内容。
编辑,回答评论中的问题:

您可以随时通过调用stopService()停止Service,但如果您不想/不需要Service的所有功能,也可以采用不同的方式进行。除了Service之外,您还可以创建一个简单的ThreadHandlerThread与服务器通信。从您的线程内部,您可以使用上述技术(LocalBroadcastManager)将数据转发/广播到您的活动中。


仅举一个基本结构的例子(代码未经测试):

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 ...
    }
}

然后您有自己的活动,例如MyActivity1MyActivity2,... MyActivityN。它们都注册了嵌入式的SocketDataReceiver来接收广播意图SOCKET_DATA_RECEIVED,该意图由您的线程发送。
在您的onReceive()方法中,您可以使用标识符SOCKET_DATA_IDENTIFIER从您的intent对象中提取数据。
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
        }
    }
}

这似乎正是我需要的。我会详细阅读这三个方面的内容。但我有一个问题。 我需要一个服务还是一个线程就可以了呢? 据我理解,服务是为了在应用程序关闭时继续运行。在我这种情况下,我不需要它。只有当应用程序正在使用时,我才需要显示信息。 - Rish K
好的,请给我一点时间修改我的回答,以便向您提供更多信息。 - reflective_mind
已更新答案,并提供了一个示例,以帮助您入门。 - reflective_mind
太有帮助了,谢谢! :D - Rish K
只有一件事。这是为那些最终进入此线程的任何人准备的。在该示例中,在发送部分,初始化意图时,您必须将SOCKET_DATA_RECEIVED字符串传递给构造函数。 - Rish K
很高兴它有帮助 ;) - reflective_mind

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接