From 07b1da9de1040a06ed9d49a31b1393ca86aae0a5 Mon Sep 17 00:00:00 2001 From: Jannis Mattheis Date: Sat, 15 Feb 2025 11:45:12 +0100 Subject: [PATCH] fix: don't reconnect when command was superseded We only want to do the reconnect if the previous command was the schedule reconnect, if the websocket connection got started another way or was closed, we don't want to do the reconnect. --- .../gotify/service/WebSocketConnection.kt | 29 +++++++++++++++---- .../github/gotify/service/WebSocketService.kt | 5 +--- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/app/src/main/kotlin/com/github/gotify/service/WebSocketConnection.kt b/app/src/main/kotlin/com/github/gotify/service/WebSocketConnection.kt index 5c55b0b..0199d56 100644 --- a/app/src/main/kotlin/com/github/gotify/service/WebSocketConnection.kt +++ b/app/src/main/kotlin/com/github/gotify/service/WebSocketConnection.kt @@ -1,6 +1,7 @@ package com.github.gotify.service import android.app.AlarmManager +import android.app.AlarmManager.OnAlarmListener import android.os.Build import android.os.Handler import android.os.Looper @@ -29,9 +30,10 @@ internal class WebSocketConnection( private val ID = AtomicLong(0) } + private var alarmManagerCallback: OnAlarmListener? = null + private var handlerCallback: Runnable? = null private val client: OkHttpClient private val reconnectHandler = Handler(Looper.getMainLooper()) - private val reconnectCallback = Runnable { start() } private var errorCount = 0 private var webSocket: WebSocket? = null @@ -106,6 +108,13 @@ internal class WebSocketConnection( @Synchronized fun close() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + alarmManagerCallback?.run(alarmManager::cancel) + alarmManagerCallback = null + } else { + handlerCallback?.run(reconnectHandler::removeCallbacks) + handlerCallback = null + } if (webSocket != null) { webSocket?.close(1000, "") closed() @@ -119,8 +128,10 @@ internal class WebSocketConnection( state = State.Disconnected } + fun scheduleReconnectNow(seconds: Long) = scheduleReconnect(ID.get(), seconds) + @Synchronized - fun scheduleReconnect(seconds: Long) { + fun scheduleReconnect(id: Long, seconds: Long) { if (state == State.Connecting || state == State.Connected) { return } @@ -130,17 +141,23 @@ internal class WebSocketConnection( Logger.info("WebSocket: scheduling a restart in $seconds second(s) (via alarm manager)") val future = Calendar.getInstance() future.add(Calendar.SECOND, seconds.toInt()) + + alarmManagerCallback?.run(alarmManager::cancel) + val cb = OnAlarmListener { syncExec(id) { start() } } + alarmManagerCallback = cb alarmManager.setExact( AlarmManager.RTC_WAKEUP, future.timeInMillis, "reconnect-tag", - { start() }, + cb, null ) } else { Logger.info("WebSocket: scheduling a restart in $seconds second(s)") - reconnectHandler.removeCallbacks(reconnectCallback) - reconnectHandler.postDelayed(reconnectCallback, TimeUnit.SECONDS.toMillis(seconds)) + handlerCallback?.run(reconnectHandler::removeCallbacks) + val cb = Runnable { syncExec(id) { start() } } + handlerCallback = cb + reconnectHandler.postDelayed(cb, TimeUnit.SECONDS.toMillis(seconds)) } } @@ -190,7 +207,7 @@ internal class WebSocketConnection( val minutes = (errorCount * 2 - 1).coerceAtMost(20) onFailure.execute(response?.message ?: "unreachable", minutes) - scheduleReconnect(TimeUnit.MINUTES.toSeconds(minutes.toLong())) + scheduleReconnect(id, TimeUnit.MINUTES.toSeconds(minutes.toLong())) } super.onFailure(webSocket, t, response) } diff --git a/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt b/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt index 3bfc698..60a37dd 100644 --- a/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt +++ b/app/src/main/kotlin/com/github/gotify/service/WebSocketService.kt @@ -172,10 +172,7 @@ internal class WebSocketService : Service() { } private fun doReconnect() { - if (connection == null) { - return - } - connection!!.scheduleReconnect(15) + connection?.scheduleReconnectNow(15) } private fun onFailure(status: String, minutes: Int) {