多个线程从同一个套接字

时间:2016-09-27 08:56:32

标签: java android multithreading sockets

我正在开发一个显示服务器数据的应用程序。服务器不是我的,它不是很稳定。连接太多会导致服务器崩溃。

我的主要活动中有一个到服务器的套接字,但有时我想打开读取数据并显示它的子活动。我的问题是我无法使用相同的套接字实现此功能,并且必须为每个活动打开一个新套接字。 每个活动都有一个线程,它从套接字读取并根据需要更新该活动的UI元素。

要在多个活动中使用相同的套接字,我尝试在开始新活动之前关闭活动的inputReader,但这只是让应用程序挂起。如果我将其保持打开状态,则新活动中的新线程永远不会收到任何数据。在开始新活动之前杀死线程是不可能的,因为线程通常被read()函数阻止。

无论如何,我有一个集中的线程来执行读取,然后将数据发送到其他活动中的所有其他线程,这样我就不必在每个活动中打开新的套接字了吗?

我觉得这是我要问的一个非常基本的问题,但我无法找到解决方案。

2 个答案:

答案 0 :(得分:1)

基本上你自己回答了你的问题:

我可以有一个集中的线程来执行读取,然后将数据发送到其他活动中的所有其他线程。

意义:当然,这样的事情是可能的。但你必须坐下来,设计并实施它。首先,您应该定义一个合理的界面,允许您的其他线程与该中央服务进行沟通,例如:

WifiManager.NETWORK_STATE_CHANGED_ACTION

含义:不要让你的不同线程做“低级别”#34;谈到那个套接字,你创建一个抽象,让你说:"当我需要这种信息,然后我使用我的服务;它会给我一些具体的答案)。当然,这是一个非常通用的答案,但是,你的问题也不完全具体。

接下来的步骤是对该接口进行一些中心(可能是单例)实现;它在自己的线程上运行,并且可以由其他线程以同步的,明确定义的方式使用。

最后警告:如果您不拥有该服务器,并且低质量并且给您带来麻烦 - 好建立。因为无论您在代码中执行什么操作,如果服务器做得不好,用户都会认为您的应用是问题所在。用户不关心操作是否因某些远程服务器崩溃而失败。所以你问题的另一个方面是:现在,你处在一个不好的地方。你应该花一些时间来寻找出路。否则,您将浪费很多的时间来为您正在处理的服务器构建变通方法。

答案 1 :(得分:1)

以下是一种非常简单明了的方法:

  1. 您创建一个新的Service,它在后台运行并通过套接字与服务器通信
  2. Service从套接字接收数据并使用LocalBroadcastManager
  3. 将其转发/广播到您有兴趣接收它的所有活动(例如更新UI)
  4. 您的所有活动都会实施BroadcastReceiver并从Service方法
  5. 中的onReceive()接收数据

    要实现这一目标,您应该阅读ServicesBroadcastReceivers的简介,以了解它们的工作原理。另外,为了首先获得基本概述,您应该阅读可用的App Components

    编辑,回答评论中的问题:

    您可以随时致电Service来停止stopService(),但如果您不想要/需要Service的所有功能,也可以采取不同的方式。您还可以创建一个与服务器进行通信的简单ServiceThread,而不是HandlerThread。从Thread内部,您可以使用上面提到的技术(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()方法中,您可以使用标识符intentSOCKET_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
            }
        }
    }