- Использование сокетов в Android
- Особенности использования сокетов
- Клиентский android-сокет
- Класс Connection
- Листинг Connection
- Класс активности MainActivity
- Листинг активности
- Методы управления сокетным соединением
- Серверное приложение
- Листинг ConnectionWorker
- Серверный класс
- Листинг CallableDelay
- Листинг CallableServer
- Листинг серверного класса Server
- Native Socket.IO and Android
- Introduction#
- Installing the Dependencies#
- Using socket in Activity and Fragment#
- Emitting events#
- Listening on events#
- Managing Socket State#
- Further reading#
- 29 июля 2016 г. Android WebSocket. Обратная связь, или Когда важна каждая доля секунды
- Что такое WebSocket и как приложения его используют
- Реализация WebSocket-соединения в приложении
- Жизненный цикл WebSocket соединения
- Observer pattern и EventBus
- Вывод
Использование сокетов в Android
Создано большое количество приложений как для Android, так и для других ОС, которые взаимодействуют друг с другом с помощью установления соединенией по сети. К таким приложениям относятся, например, мессенджеры социальных сетей WhatsApp, Viber. Как правило, для установления соединения между такими приложениями используются сокеты.
Сокет (socket) — это интерфейс, позволяющий связывать между собой программы различных устройств, находящихся в одной сети. Сокеты бывают двух типов: клиентский (Socket) и серверный (ServerSocket). Главное различие между ними связано с тем, что сервер «открывает» определенный порт на устройстве, «слушает» его и обрабатывает поступающие запросы, а клиент должен подключиться к этому серверу, зная его IP-адрес и порт. В Android сокеты для передачи данных используют по умолчанию протокол TCP/IP, важной особенностью которого является гарантированная доставка пакетов с данными от одного устройства до другого.
Особенности использования сокетов
Что важно знать при использовании сокетов в Android ?
- соединения сокетов отключаются при переходе устройства в спящий режим;
- чтобы не «рвать» соединение при наступлении спящего режима в устройстве можно использовать сервис;
- для использования интернет-сети необходимо Android-приложению предоставить нужные права в манифесте.
Для определения прав в манифесте необходимо в файл AndroidManifest.xml добавить следующую строку :
Теперь android-приложения будет иметь доступ к сети.
Далее в статье рассмотрим пример клиент-серверного сокетного соединения с передачей сообщения. Функции клиента будет выполнять android-приложение. Серверное java-приложение выполним в IDE Eclipse с использованием пакета concurrent. В конце страницы можно скачать оба приложения.
Клиентский android-сокет
Интерфейс andriod-приложения представлен на следующем скриншоте. Форма приложения включает поле ввода текстового сообщения и кнопки установления соединения сервером, передачи сообщения и закрытия соединения.
Клиентское приложение создадим из двух классов : класс взаимодействия с серверным сокетом Connection и класс стандартной активности MainActivity.
Класс Connection
Класс взаимодействия с сервером Connection получает при создании (через конструктор) параметры подключения : host и port. Методы Connection вызываются из активности и выполняют следующие функции :
Метод | Описание |
---|---|
openConnection | Метод открытия сокета/соединения. Если сокет открыт, то он сначала закрывается. |
closeConnection | Метод закрытия сокета |
sendData | Метод отправки сообщения из активности. |
finalize | Метод освобождения ресурсов |
Листинг Connection
Класс активности MainActivity
В активности MainActivity определены параметры сервера : host, port. Помните, что IP-адрес сервера для Вашего android-примера не может быть localhost (127.0.0.1), иначе Вы будете пытаться связаться с сервером внутри Andriod-системы. Кнопки интерфейса связаны с методами обращения к классу Connection. Кнопки отправки сообщения mBtnSend и закрытия соединения mBtnClose с сервером блокируются при старте приложения. После установления соединения с сервером доступ к кнопкам открывается.
Листинг активности
Методы управления сокетным соединением
Ниже представлены методы обработки событий, связанных с нажатием кнопок интерфейса. Обратите внимание, что подключение к серверу выполняется в отдельном потоке, а открытие доступа к кнопкам в основном потоке, для чего вызывается метод runOnUiThread. Для отправки сообщения серверу также создается отдельный поток.
Серверное приложение
Серверное приложение включает 2 класса : Server и ConnectionWorker. Серверный класс Server будет выполнять обработку взаимодействия с клиентом с использованием ConnectionWorker в отдельном потоке. Конструктор ConnectionWorker в качестве параметра получает объект типа Socket для чтения сообщений клиента из потока сокета.
Листинг ConnectionWorker
ConnectionWorker получает входной поток inputStream из клиентского сокета и читает сообщение. Если сообщение отсутствует, т.е. количество прочитанных байт равно -1, то это значит, что соединение разорвано, то клиентский сокет закрывается. При закрытии клиентского соединения входной поток сокета также закрывается.
Серверный класс
Серверный класс Server создадим с использованием многопоточного пакета util.concurrent. На странице описания сетевого пакета java.net и серверного ServerSocket был приведен пример серверного модуля с использованием обычного потока Thread, при работе с которым необходимо решать задачу его остановки : cтарый метод Thread.stop объявлен Deprecated и предан строжайшей анафеме, а безопасная инструкция Thread.interrupt безопасна, к сожалению, потому, что ровным счетом ничего не делает (отправляет сообщение потоку : «Пожалуйста, остановись»). Услышит ли данный призыв поток остается под вопросом – все зависит от разаработчика.
Чтобы иметь возможность остановить сервер «снаружи» в серверный класс Server включим 2 внутренних реализующих интерфейс Callable класса : CallableDelay и CallableServer. Класс CallableDelay будет функционировать определенное время, по истечении которого завершит свою работу и остановит 2-ой серверный поток взаимодействия с клиентами. В данном примере CallableDelay используется только для демонстрации остановки потока, организуемого пакетом util.concurrent.
Листинг CallableDelay
CallableDelay организует цикл с задержками. После завершения последнего цикла cycle поток завершает цикл, останавливает вторую задачу futureTask[1] и закрывает сокет. В консоль выводится соответствующее сообщение.
Листинг CallableServer
Конструктор CallableServer в качестве параметров получает значение открываемого порта для подключения клиентов. При старте (метод call) создается серверный сокет ServerSocket и поток переходит в режим ожидания соединения с клиентом. Остановить поток можно вызовом метода stopTask, либо завершением «задачи» типа FutureTask с данным потоком.
При подключении клиента метод serverSoket.accept возвращает сокет, который используется для создания объекта ConnectionWorker и его запуска в отдельном потоке. А сервер (поток) переходит к ожиданию следующего подключения.
В случае закрытия сокета (завершение внешней задачи FutureTask с данным потоком) будет вызвано исключение Exception, где выполняется проверка закрытия сокета; при положительном ответе основной цикл прерывается и поток завершает свою работу.
Листинг серверного класса Server
Cерверный класс Server создает два потоковых объекта (callable1, callable2), формирует из них две задачи futureTask и запускает задачи на выполнение методом execute исполнителя executor. После этого контролируется завершение выполнение обоих задач методом isTasksDone. При завершении выполнения обеих задач завершается также и цикл работы executor’а.
Два внутренних описанных выше класса (CallableDelay, CallableServer) не включены в листинг.
Источник
Native Socket.IO and Android
In this tutorial well learn how to create a chat client that communicates with a Socket.IO Node.JS chat server, with our native Android Client! If you want to jump straight to the code, it’s on GitHub. Otherwise, read on!
Introduction#
To follow along, start by cloning the repository: socket.io-android-chat.
The app has the following features:
- Sending a message to all users joining to the room.
- Notifies when each user joins or leaves.
- Notifies when an user start typing a message.
Socket.IO provides an event-oriented API that works across all networks, devices and browsers. It’s incredibly robust (works even behind corporate proxies!) and highly performant, which is very suitable for multiplayer games or realtime communication.
Installing the Dependencies#
The first step is to install the Java Socket.IO client with Gradle.
For this app, we just add the dependency to build.gradle :
We must remember adding the internet permission to AndroidManifest.xml .
Now we can use Socket.IO on Android!
Using socket in Activity and Fragment#
First, we have to initialize a new instance of Socket.IO as follows:
IO.socket() returns a socket for http://chat.socket.io with the default options. Notice that the method caches the result, so you can always get a same Socket instance for an url from any Activity or Fragment. And we explicitly call connect() to establish the connection here (unlike the JavaScript client). In this app, we use onCreate lifecycle callback for that, but it actually depends on your application.
Emitting events#
Sending data looks as follows. In this case, we send a string but you can do JSON data too with the org.json package, and even binary data is supported as well!
Listening on events#
Like I mentioned earlier, Socket.IO is bidirectional, which means we can send events to the server, but also at any time during the communication the server can send events to us.
We then can make the socket listen an event on onCreate lifecycle callback.
With this we listen on the new message event to receive messages from other users.
This is what onNewMessage looks like. A listener is an instance of Emitter.Listener and must be implemented the call method. Youll notice that inside of call() is wrapped by Activity#runOnUiThread() , that is because the callback is always called on another thread from Android UI thread, thus we have to make sure that adding a message to view happens on the UI thread.
Managing Socket State#
Since an Android Activity has its own lifecycle, we should carefully manage the state of the socket also to avoid problems like memory leaks. In this app, we’ll close the socket connection and remove all listeners on onDestroy callback of Activity.
Calling off() removes the listener of the new message event.
Further reading#
If you want to explore more, I recommend you look into:
Other features of this app. They are just implemented with emit() , on() and off() .
Источник
29 июля 2016 г. Android WebSocket. Обратная связь, или Когда важна каждая доля секунды
Мобильные приложения, работающие в сети, общаются с сервером посредством отправки запросов-ответов. Сервер — это что-то вроде справочного бюро, которое отвечает на любые вопросы, принимает справки в определенной форме, а также обрабатывает и хранит их. Эта модель решает большинство поставленных перед сервисом задач. А что делать, если нам нужно сообщить клиенту о поступлении свежих данных, которые соответствуют его запросу? Давайте смоделируем эту ситуацию на примере бюро.
Итак, клиенту понадобилась какая-то важная для него информация, для ее получения он обратился в справочное бюро. Подав запрос, он получает ответ (в рамках данной задачи не важно какой именно). Проходит некоторое время, в бюро обновляется информация, которая может быть полезна этому пользователю. Что делать дальше?
Можно, конечно, сообщить о новостях при следующем обращении клиента в бюро (Long pulling). Что не совсем удобно, но, как-никак, решает часть проблемы. Но что же делать, если мы имеем дело с информацией, которая быстро теряет актуальность? Возникает потребность в канале двусторонней связи между клиентом и бюро, к примеру в телефоне (WebSocket).
Прекрасно, у бюро есть телефон клиента, в любой момент оно может позвонить ему для предоставления оперативной информации. Hо и здесь есть одно НО. Клиент может не взять трубку. Тогда бюро отправляет ему текстовое извещение (Push-уведомление) об обновлении актуальной для него информации, в зависимости от объема которой ему достаточно будет извещения, либо вскоре после прочтения извещения он сам обратится в бюро за подробностями.
Такой механизм работы реализован во всех популярных сервисах, где срок актуальности данных играет важную роль и может измеряться секундами.
Что такое WebSocket и как приложения его используют
WebSocket — протокол полнодуплексной связи поверх TCP-соединения, предназначенный для обмена сообщениями между клиентом и веб-сервером в режиме реального времени. В настоящее время в W3C осуществляется стандартизация API Web Sockets. Черновой вариант стандарта этого протокола утвержден IETF (Wikipedia). Углубляться в подробности реализации протокола не буду, так как по этому поводу в сети есть достаточно много технической документации. Здесь же мы рассмотрим кейсы, в которых используется WebSocket, упрощенную бизнес логику и вспомогательные библиотеки.
Где уместно его использование? WebSocket будет полезен разработчикам, которые создают приложения с интенсивным обменом данными, требовательные к скорости обмена и стабильности канала.
Приведу несколько приложений которые используют сокет для реализации своей бизнес логики. Котировки валют, акций, биржевая статистика — это все мониторинг в реальном времени, данные на клиентах постоянно обновляются посредством сокет-соединения. Еще примеры:
- В текстовых чатах, независимо от количества пользователей, все участники беседы практически одновременно получают сообщения друг от друга по одному и тому же соединению.
- Букинг (заказ или бронирование чего-либо) подразумевает, что на мой запрос кто-то должен оперативно откликнуться (к примеру, водитель такси), либо сервер должен забронировать для меня услугу (к примеру, место в кинозале), оповестив при этом меня и других пользователей о том, что данная услуга была забронирована.
Это все конечно довольно абстрактно, и бизнес логика данных процессов в жизни немного сложнее. Но главное требование, которое выполняет здесь сокет-соединение — полная двусторонняя связь (полнодуплексное соединение), сервер в любой момент может обратиться к клиенту, а тот ему ответить и наоборот.
Реализация WebSocket-соединения в приложении
Итак, для использования сокет-соединения была выбрана библиотека, поддерживающая стандарт RFC 6455 — Java-WebSocket, но в скором времени выяснилось, что при использовании wss-соединения она повреждает пакеты на некоторых версиях Android. Установить закономерность не удалось, при этом на репозитории есть уже несколько сообщений о таких проблемах, поэтому было решено ее заменить. Следующим подходящим вариантом был nv-websocket-client, который хорошо себя зарекомендовал.
nv-websocket-client реализует полный стек методов и интерфейсов для работы с сокет-соединением. Для разработчиков, имеющих хоть небольшой опыт в написании сетевых приложений, не составит труда разобраться с документацией.
Когда вы приступаете к имплементации сокета в приложении, возникает вполне уместный вопрос: каким образом лучше архитектурно реализовать соединение внутри приложения? Исходя из логики работы соединения и его жизненного цикла (ЖЦ), это должен быть компонент, который будет работать вне зависимости от того, на каком экране сейчас находится пользователь.
Реализовывать соединение в виде отдельного сервиса неудобно в силу нужды в постоянном биндинге последнего, да и не целесообразно по причине того, что соединение нам нужно только в момент активности пользователя. Соединение можно реализовать компонентом, который будет осуществлять коммуникацию в отдельном потоке, экземпляр данного компонента хранить в Application вашего приложения и управлять его состоянием через публичные методы, к примеру так:
Сам компонент и клиент есть на нашем GitHub.
Рассмотрим подробнее клиент ClientWebSocket . Простой конструктор получает на вход коллбек для возврата сообщений и адрес соединения. Клиент поддерживает wss-соединение, которое открывается в отдельном потоке. Взаимодействие с соединением осуществляется с помощью экземпляра WebSocket для отправки сообщений и слушателя SocketListener для их получения.
Для отправки текстового сообщения используется метод sendText() , можно также отправлять бинарные последовательности посредством sendBinary() . В свою очередь, для получения SocketListener имеет ряд методов, и некоторые из них мы переопределили:
Большая часть данных методов касается ЖЦ соединения, но о них немного позже. Сейчас нас интересует onTextMessage() , в данном случае мы получаем текстовые сообщения, которые содержат обертку json c отправленной нам моделью данных. Обертка выглядит так:
Получив сообщение, определяем его тип и используя дженерик получаем присланную нам модель. К примеру, вот таким образом:
Все очень просто и удобно в использовании. Имея перечень всех событий, которые могут прийти по сокету, парсим и обновляем UI.
Также есть интересный момент с поддержкой соединения. Менеджментом всех соединений занимается сервер. Для того, чтобы понимать, какое соединение активно, а какое можно закрывать, он периодически их опрашивает. Данный процесс называется PingPong, и на каждый Pong от сервера, клиент должен ответить Ping-значением. Это выглядит так:
Жизненный цикл WebSocket соединения
Что же насчет жизненного цикла? А он напрямую вытекает из бизнес-логики приложения. У нас есть несколько кейсов поведения клиента и сервера:
- Со стороны сервера: у нас появилась информация, которую нужно передать. Проверяем, есть ли сокет-соединение. Если есть, то отправляем данные на сокет, нет — отправляем push-уведомление.
- Со стороны клиента: когда клиент онлайн, соединение активно и сообщение придет на сокет. Если же он выключил экран либо свернул приложение — соединение закрывается*, все сообщения придут в виде push-уведомлений.
*Можно конечно не закрывать соединение, реализовав для этого тот же сервис и передав в него экземпляр компонента. Но такой случай должен быть обоснован бизнес-логикой приложения и разработчики серверной части должны предусмотреть тот факт, что соединения будут практически постоянными и их количество будет прямо пропорционально расти с количеством пользователей.
Из сказанного выше, ЖЦ сокет соединения принуждает разработчика следить за состоянием девайса, приложения, статуса авторизации пользователя, и в момент ухода в оффлайн или разлогинивания закрывать соединение. Реализуется это несколькими путями. За состоянием девайса следит Receiver, настроенный на прием событий включения-выключения экрана.
За состоянием приложения следит специальный компонент BackgroundManager . Тут нужно немного объяснить целесообразность его существования. Дело в том что, логичным путем было бы закрывать соединение на методе onPause жизненного цикла активности. Но у нас, как правило, не одна активность в приложении, и при переходе между экранами происходит переподключение соединения. Это можно было бы решить банальным булевым флажком, но тут есть еще один скользкий момент… активности работают асинхронно, то есть возникают ситуации, когда onStart одной срабатывает раньше чем onPause другой и наоборот. В общем, это вносит некоторую непрозрачность в реализации ЖЦ соединения.
BackgroundManager дает возможность абстрагироваться от активности и через Application подписаться на onActivityResumed и onActivityPaused . На onPause он генерирует отложенное действие, которое впоследствии сообщит о том, что приложение свернуто. На onResume действие отменяется, если оно до этого не было выполнено, и при необходимости следует оповещение о том, что приложение находится в фокусе.
А также не забываем об открытии соединения при авторизации пользователя и закрытии при его разлогинивании. Это просто сделать через публичные методы Application.
Observer pattern и EventBus
Observer — поведенческий шаблон проектирования. Также известен как «подчиненные» (Dependents). Создает механизм у класса, который позволяет получать экземпляру объекта этого класса оповещения от других объектов об изменении их состояния, тем самым наблюдая за ними (Wikipedia). С помощью данного шаблона следует реализовывать обновление данных в приложении после получения сообщения в реальном времени. Observer позволяет построить слабосвязанную архитектуру, что в дальнейшем позитивно скажется на масштабировании приложения в целом.
В реализации данного паттерна поможет библиотека EventBus, которую мы и используем. Она проста до безобразия. В любой класс-наблюдатель можно добавить метод-обработчик определенного события, пометив его аннотацией @Subscribe , ну и зарегистрировать наблюдателя с помощью метода EventBus.getDefault().register(this) . Важно не забывать отписывать наблюдателей после того, как в них отпадет надобность, таким вот образом: EventBus.getDefault().unregister(this) , иначе получим серьезные утечки памяти. Пример активности-наблюдателя в данном случае будет выглядеть так:
Наблюдателем может быть любой объект, главное чтобы все логично вписывалось в архитектуру приложения и правильно взаимодействовало с ЖЦ. К примеру, имплементировать наблюдателя в холдере не является целесообразным, так как у него свой специфичный ЖЦ и зона доступа к данным. А вот переместив логику наблюдения в адаптер мы улучшим ситуацию, но, опять же, не совсем правильно заставлять такой архитектурный элемент как адаптер заниматься менеджментом данных, он ведь занимается лишь их отображением. Потому правильным решением будет реализовывать логику наблюдения в активности или в модели представления, в зависимости от того, какая у вас архитектура.
Отправлять события можно с любого контекста, не зависимо от времени. Если наблюдатель, который умеет обрабатывать данное событие, подписан на обновления — он его получит. Отправка выглядит так:
Ну вот, Greenrobot Eventbus, можно сказать, делает всю работу сам. В итоге получаем удобный связующий мост внутри приложения, все компоненты, не имея прямой связи, могут мгновенно получить и обработать предназначенные им данные.
P.S. И о дополнительной логике, которая появляется в приложении в связи с обработкой событий в реальном времени. Казалось бы, получил событие, обновил UI, вывел непрочитанное сообщение, сменил статус пользователя — что тут сложного? Да, сложного ничего вроде как и нет, но, как правило, разработчики привыкли проектировать линейные приложения, а вот в данном случае возникает серьезный такой момент асинхронности. А проблем будет ой как много, если на старте разработки забыть, что мы разрабатываем приложение с асинхронной логикой.
К примеру, было у вас 2 Fragment со списками с одинаковых элементов, но отсортированных по разным критериям. Если элемент одного обновил свое состояние, значит тот же элемент другого должен обновиться. И не забывайте, что правило действует и в другую сторону — если пользователь сам обновляет состояние элемента, во втором списке мы опять же должны увидеть обновление. Задумайтесь на минутку, как вы будете это реализовывать?
Так же в UI возникает множество моментов, которые не всегда могут быть предусмотрены дизайнером и отображены в мокапах. К примеру, пользователь перешел на некий экран с контентом, состояние которого могут менять другие пользователи, использующие приложение. В таком случае контент должен обновиться, при этом не вызвав у текущего пользователя замешательства.
Вывод
Как видим, сама реализация сокет-соединения не является чем-то сложным и трудоемким. Главное учесть все моменты, связанные с бизнес-логикой работы приложения, а потом уже приступать непосредственно к его разработке.
Нужен MVP, разработка под iOS, Android или прототип приложения? Ознакомьтесь с нашим портфолио и сделайте заказ уже сегодня!
Источник