首页 文章

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

提问于
浏览
4

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

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

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

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

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

2 回答

  • 1

    一个非常简单明了的方法如下:

    • 您创建一个新的 Service ,它在后台运行并通过套接字与服务器通信

    • Service 从套接字接收数据并将其转发/广播到您有兴趣接收它的所有活动(例如更新UI),使用 LocalBroadcastManager

    • 您的所有活动都实现 BroadcastReceiver 并从 onReceive() 方法中接收 Service 中的数据

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

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

    您可以随时通过调用 stopService() 来停止 Service ,但如果您不想/需要 Service 的所有功能,也可以采用不同的方式 . 您也可以创建一个与服务器进行通信的简单 ThreadHandlerThread ,而不是 Service . 从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() 方法中,您可以使用标识符 SOCKET_DATA_IDENTIFIERintent 对象中提取数据 .

    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
            }
        }
    }
    
  • 1

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

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

    意义:当然,这样的事情是可能的 . 但你必须坐下来,设计并实施它 . 您可以从定义一个合理的接口开始,该接口允许您的其他线程使用该中央服务 communicate ,例如:

    enum RequestType { DO_THIS, DO_THAT };
    
    interface ServerConnectionService<T> {
       List<T> performRequest(RequestType request);
    }
    

    含义:不是让你的不同线程在该套接字上进行“低级别”交谈,而是创建一个抽象,允许你说:“当我需要这种信息时,我会使用我的服务;它会返回一些特定的答案当然,这是一个非常通用的答案,但是,你的问题也不完全具体 .

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

    最后的警告:如果您没有该服务器,并且它有 low quality 并且给您带来麻烦 - 那就是 not 一个很好的设置 . 因为无论你在代码中做了什么,如果服务器做得不好,用户就会认为你的问题是 app . 由于某些远程服务器崩溃,用户不关心操作是否失败 . 所以你问题的另一个方面是:现在,你处在一个不好的地方 . 你应该花一些时间来寻找出路 . 否则,您将浪费 a lot 的时间来为您正在处理的服务器构建变通方法 .

相关问题