Untested: WS battery improvement, relates to #113 and binwiederhier/ntfy#1662

This commit is contained in:
Philipp Heckel 2026-04-07 10:30:28 -04:00
parent 7bb812662e
commit 3e93a3fb8d
2 changed files with 27 additions and 3 deletions

View file

@ -10,6 +10,7 @@ import io.heckel.ntfy.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
class Application : Application() {
val ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
@ -34,7 +35,18 @@ class Application : Application() {
val connectivityManager = getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityManager.registerDefaultNetworkCallback(object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
SubscriberServiceManager.refresh(this@Application)
// Force reconnect of all WebSocket/JSON connections so they're rebound to the new
// default network. This catches Wi-Fi <-> cellular handoffs and similar transitions
// where the underlying socket is bound to a network that's no longer the default.
// Without this, broken connections would only be detected via the (potentially
// long) ping/pong timeout.
ioScope.launch {
repository.getSubscriptions()
.map { it.baseUrl }
.distinct()
.forEach { repository.incrementConnectionForceReconnectVersion(it) }
SubscriberServiceManager.refresh(this@Application)
}
}
override fun onLost(network: Network) {
SubscriberServiceManager.refresh(this@Application)

View file

@ -46,12 +46,24 @@ object HttpUtil {
/**
* Client for WebSocket connections.
* No read timeout, 1 minute ping interval, 10s connect timeout.
* No read timeout, 5 minute ping interval, 10s connect timeout.
*
* Dead connections are normally caught by one of two faster mechanisms:
* 1. Device-side network changes (Wi-Fi <-> cellular, network drop/return) are
* detected instantly by Application.registerNetworkCallback's onAvailable
* handler, which bumps connectionForceReconnectVersion to force a reconnect.
* 2. Server-side failures (crash, restart, server's own pong timeout) surface as
* TCP FIN/RST and are detected instantly via OkHttp's onClosed/onFailure.
*
* The 5-minute client ping is only a fallback for the rare case where neither of
* the above fires: silent server hangs, NAT eviction, asymmetric routing breaks, etc.
* We use a long interval so the modem can fully power down between pings, which is
* the dominant battery factor for the foreground service.
*/
suspend fun wsClient(context: Context, baseUrl: String): OkHttpClient {
return emptyClientBuilder(context, baseUrl)
.readTimeout(0, TimeUnit.MILLISECONDS)
.pingInterval(1, TimeUnit.MINUTES) // Technically not necessary, the server also pings us
.pingInterval(5, TimeUnit.MINUTES)
.connectTimeout(10, TimeUnit.SECONDS)
.build()
}