Подключение SignalR для Android на Kotlin

Kate

Administrator
Команда форума
Если ты читаешь эту статью, наверно тебе, как и мне понадобилось подключить SignalR в своем мобильном приложении. К сожалению в русскоязычном сегменте не так уж много информации о том, как это сделать, а то что есть уже устарело.

В рамках этой статьи я не буду касаться серверной части, но постараюсь предоставить все необходимое примеры кода с комментариями для реализации SignalR в Android, а получившийся пример выложу на GitHub.

Что такое SignalR и когда он используется?​

Это библиотека с открытым исходным кодом, которая упрощает добавление веб-функций в режиме реального времени в приложения. Веб-функции в режиме реального времени позволяют коду на стороне сервера мгновенно отправлять содержимое на клиенты.

  • Управляет автоматическим управлением соединениями.
  • Отправляет сообщения всем подключенным клиентам одновременно. Например, комната чатов.
  • Отправляет сообщения конкретным клиентам или группам клиентов.
  • Масштабируется для управления увеличением трафика.
Схематично
3c41cb990529a5f41c59bd01bd1021a1.png

Более подробно здесь
Создаем проект и подключаем зависимости.
Создаем новый проект на основе Empty Activity, я назову его SignalR_Example
Создание нового проекта
309e6c7cf09f6312f2d4994cf5ba30cf.png
08efb165d5dc3a20faa50eb1e1fc2ea7.png

Добавляем 3 необходимые зависимости в build.gradle проекта:

implementation "com.microsoft.signalr:signalr:5.0.10"
Смотрим актуальную версию здесь

implementation "com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2"
Смотрим актуальную версию здесь

implementation 'com.google.code.gson:gson:2.8.8'

Смотрим актуальную версию здесь
Зависимости в build.gradle
56cbc2f920e578e3fd65a24152657b4f.png

Создаем необходимые классы​

Класс SignalRListener и соответствующий ему интерфейс ISignalRListener
Он будет содержать подключение к HubConnection и основную реализацию слушателя внешних событий.
Класс SignalRListener
/**
* Представляет SignalR-реализацию слушателя внешних событий.
* @param sslSocketFactory Модифицированный протокол безопасной передачи данных.
* @param trustAllCerts Модифицированный X509TrustManager для доверия всем сертификатам.
*/
class SignalRListener (
private val sslSocketFactory: SSLSocketFactory,
private val trustAllCerts: TrustAllCerts
) : ISignalRListener {

private val apiUrl = "https://something.com/api/" //Адрес Вашего api
private val hubConnection = HubConnectionBuilder.create(apiUrl + "GatewayHub") //
.setHttpClientBuilderCallback {
it.sslSocketFactory(sslSocketFactory, trustAllCerts)
}
.build()

override val connectionId: String?
get() = hubConnection.connectionId
override val connectionState: HubConnectionState
get() = hubConnection.connectionState

override fun startConnection() {
hubConnection.start()
}

override fun stopConnection() {
hubConnection.stop()
}

override fun subscribe(
eventName: String,
handler: (event: LinkedTreeMap<String, String>) -> Unit
) {
hubConnection.on(
eventName,
handler,
LinkedTreeMap::class.java
)
}

override fun unsubscribe(eventName: String) {
hubConnection.remove(eventName)
}
}
Интерфейс ISignalRListener
/**
* Описывает реализацию слушателя внешних событий.
*/
interface ISignalRListener {
/**
* Возвращает id подключения к SignalR.
*/
val connectionId: String?

/**
* Возвращает статус подключения к SignalR.
*/
val connectionState: HubConnectionState

/**
* Выполняет подключение к серверу SignalR.
*/
fun startConnection()

/**
* Выполняет отключение от сервера SignalR.
*/
fun stopConnection()

/**
* Подписывается на событие.
* @param eventName Имя события.
* @param handler Обработчик события.
*/
fun subscribe(eventName: String, handler: (LinkedTreeMap<String, String>) -> Unit)

/**
* Отписывается от события.
* @param eventName Имя события.
*/
fun unsubscribe(eventName: String)
}
Класс SignalRService и соответствующий ему интерфейс ISignalRService
Он будет содержать реализацию подписок на ресурсы.
Класс SignalRService
/**
* Представляет реализацию подписок на ресурсы.
*/
class SignalRService (
private val signalRListener: ISignalRListener
) : ISignalRService {
/**
* Возвращает или устанавливает подписку на ресурсы.
*/
private val resourcesSubscriptions = mutableListOf<ResourceSubscriptionModel>()

init {
subscribeOnResourceChanged()
}

/**
* Подписывается на события изменения ресурса.
*/
private fun subscribeOnResourceChanged() {
signalRListener.subscribe(
"ResourceChangedEventOccurred"
) { event ->
resourcesSubscriptions.forEach { subscription ->
val resourceName = event["resourceName"]
if (resourceName != null && resourceName.contains(subscription.resourceName)) {
//Реагируем на изменение ресурса.
subscription.callback()
}
}
}
}

override fun subscribe(subscriptionsForAdd: List<ResourceSubscriptionModel>) {
subscriptionsForAdd.forEach { resourceSubscription ->
resourcesSubscriptions.add(resourceSubscription)
}
}

override fun unsubscribe(subscriptionsForRemove: List<ResourceSubscriptionModel>) {
subscriptionsForRemove.forEach { resourceSubscription ->
resourcesSubscriptions.remove(resourceSubscription)
}
}
}
Интерфейс ISignalRService
/**
* Описывает методы подписки на внешние события.
*/
interface ISignalRService {
/**
* Добавляет ресурсы в подписку.
* @param subscriptionsAdd Список ресурсов для подписки.
*/
fun subscribe(subscriptionsAdd: List<ResourceSubscriptionModel>)

/**
* Удаляет ресурсы из подписки.
* @param subscriptionsRemove Список ресурсов для отписки.
*/
fun unsubscribe(subscriptionsRemove: List<ResourceSubscriptionModel>)
}
Дата класс ResourceSubscriptionModel

Он будет представлять подписку на ресурс.
Дата класс ResourceSubscriptionModel
/**
* Представляет подписку на ресурс.
*/
data class ResourceSubscriptionModel(
/**
* Имя ресурса для отслеживания событий.
*/
val resourceName: String,
/**
* Метод выполняемый при получении события.
*/
val callback: () -> Unit
)
Класс TrustAllCerts

Он позволит доверять всем сертификатам.
Класс TrustAllCerts
/**
* Позволяет доверять всем сертификатом.
*/
class TrustAllCerts : X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) = Unit
override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) = Unit
}
Класс SignalRApplication
Он представляет класс нашего приложения и будет хранить синглтоны ISignalRListener и ISignalRService. По хорошему следует использовать какой ни будь DI инструмент, но для упрощения примера сделаем так.
Класс SignalRApplication
/**
* Представляет приложение.
*/
class SignalRApplication : Application() {

lateinit var signalRListener: ISignalRListener // Слушатель внешних событий.
lateinit var signalRService: ISignalRService // Сервис подписок на ресурсы.

companion object {
lateinit var application: SignalRApplication
}

override fun onCreate() {
super.onCreate()
application = this

// Модифицированный X509TrustManager для доверия всем сертификатам.
val trustAllCerts = TrustAllCerts()
// Модифицированный протокол безопасной передачи данных.
val sslSocketFactory = SSLContext.getInstance("TLSv1.2").apply {
init(null, arrayOf<TrustManager>(trustAllCerts), SecureRandom())
}.socketFactory

signalRListener = SignalRListener(sslSocketFactory, trustAllCerts)
signalRService = SignalRService(signalRListener)
}

}
Абстрактный класс BaseActivity

Он будет представлять базовый класс для наших Activity и содержать методы на подписку и отписку от событий.
Абстрактный класс BaseActivity
/**
* Представляет базовый класс Activity
*/
abstract class BaseActivity : AppCompatActivity() {

/**
* Сервис подписок на ресурсы.
*/
private val _signalRService = SignalRApplication.application.signalRService

/**
* Лист подписок.
*/
private val _resourceSubscriptions: MutableList<ResourceSubscriptionModel> = mutableListOf()

override fun onStop() {
super.onStop()
_signalRService.unsubscribe(_resourceSubscriptions)
}

/**
* Устанавливает подписки на ресурсы.
* @param resourceSubscriptions Лист подписок.
*/
fun setSignalRSubscriptions(
resourceSubscriptions: List<ResourceSubscriptionModel>
) {
_resourceSubscriptions.addAll(resourceSubscriptions)
_signalRService.subscribe(resourceSubscriptions)
}
}

 
Сверху