Несколько потоков, считываемых из одного сокета

Я разрабатываю приложение, которое отображает данные с сервера. Сервер не мой, и он не очень стабилен. Создание слишком большого количества соединений приводит к сбою сервера.

У меня есть один сокет на сервере в моем основном действии, но иногда я хочу открыть вспомогательные действия, которые читают данные и отображают их. Моя проблема в том, что я не могу добиться этого с помощью того же сокета и должен открыть новый сокет для каждого действия. Каждое действие имеет поток, который выполняет чтение из сокета и обновляет элементы пользовательского интерфейса при этом действии по мере необходимости.

Чтобы использовать тот же сокет в нескольких действиях, я попытался закрыть inputReader активности перед запуском нового действия, но это просто заставляет приложение зависать. Если я оставлю его открытым, то новый поток в новом действии никогда не получит никаких данных. Убийство потока перед запуском нового действия невозможно, потому что поток, как правило, блокируется функцией read ().

Есть ли в любом случае, что я могу иметь централизованный поток, который выполняет чтение, а затем отправляет данные ко всем другим потокам в других действиях, так что мне не нужно открывать новые сокеты в каждом действии?

Я чувствую, что это очень простая вещь, о которой я прошу, но пока не могу найти решение.

Довольно простой и простой подход заключается в следующем:

  1. Вы создаете новую Service которая работает в фоновом режиме и взаимодействует с сервером через ваш сокет
  2. Service получает данные из сокета и пересылает / передает его во все ваши действия, которые заинтересованы в его получении (например, для обновления пользовательского интерфейса) с помощью LocalBroadcastManager
  3. Все ваши действия реализуют BroadcastReceiver и получают данные из вашей Service внутри onReceive()

Чтобы добиться этого, вы должны прочитать введение в Службы и BroadcastReceivers, чтобы получить представление о том, как они работают. Также, чтобы сначала получить общий обзор, вы должны прочитать о доступных компонентах приложения .

EDIT, чтобы ответить на вопрос в комментарии:

Вы всегда можете остановить Service , вызвав stopService() но вы также можете сделать это по-другому, если вам не нужны / не нужны все функции Service . Вместо Service вы также можете создать простой Thread или HandlerThread который HandlerThread с сервером. Изнутри вашей темы вы можете пересылать / передавать данные в свои действия, используя вышеупомянутую технику ( 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 ... } } 

Затем у вас есть свои действия, например MyActivity1 , MyActivity2 , … MyActivityN . Все они регистрируют свой встроенный SocketDataReceiver для получения намерения трансляции SOCKET_DATA_RECEIVED , которое отправляется вашим потоком.

Внутри методов onReceive() вы можете извлечь данные из объекта intent с помощью идентификатора 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 } } } 

В основном вы сами ответили на свой вопрос:

Я могу иметь централизованный поток, который выполняет чтение, а затем отправляет данные ко всем другим потокам в других действиях.

Смысл: конечно, такая вещь возможна. Но вам нужно сесть, спроектировать и реализовать его. Вы должны начать с определения разумного интерфейса, который позволяет другим вашим потокам связываться с этой центральной службой, например:

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

Значение: вместо того, чтобы ваши разные потоки выполняли «низкий уровень», говорящий на этом сокете, вы создаете абстракцию, которая позволяет вам сказать: «Когда мне нужна такая информация, я использую свою услугу, и она возвращает определенный ответ на Я). Конечно, это очень общий ответ, но, ну, ваш вопрос не совсем конкретный.

Следующим шагом было бы иметь некоторую центральную (возможно, одноэлементную) реализацию этого интерфейса; Который работает в своем потоке и может использоваться другими потоками в синхронизированном, четко определенном виде.

Заключительное слово предупреждения: если вы не владеете этим сервером, и оно имеет низкое качество и создает проблемы для вас – это не очень хорошая настройка. Поскольку независимо от того, что вы делаете в своем коде, если сервер не делает хорошую работу, пользователи будут воспринимать ваше приложение как проблему. Пользователям все равно, если операция завершится с ошибкой, так как сбой удаленного сервера. Итак, другой аспект в вашем вопросе: прямо сейчас вы находитесь в плохом месте. Вы должны были провести какое-то серьезное время, чтобы найти пути оттуда. В противном случае вы будете тратить много времени на создание обходных решений для этого сервера, с которым имеете дело.