From fc5255a0f0b25a236fa1d9a6af703ddb7b6b676b Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Mon, 26 Jan 2026 06:59:53 -0600 Subject: [PATCH] refactor(core): Optimize and share network state flows (#4320) Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com> --- .../repository/network/NetworkRepository.kt | 36 +++++++++++++-- .../repository/radio/RadioInterfaceService.kt | 44 +++++++++++-------- 2 files changed, 58 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/repository/network/NetworkRepository.kt b/app/src/main/java/com/geeksville/mesh/repository/network/NetworkRepository.kt index 5f64e2119..2266cdc4f 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/network/NetworkRepository.kt +++ b/app/src/main/java/com/geeksville/mesh/repository/network/NetworkRepository.kt @@ -19,10 +19,16 @@ package com.geeksville.mesh.repository.network import android.net.ConnectivityManager import android.net.nsd.NsdManager import android.net.nsd.NsdServiceInfo +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.coroutineScope import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.shareIn import org.meshtastic.core.di.CoroutineDispatchers +import org.meshtastic.core.di.ProcessLifecycle import javax.inject.Inject import javax.inject.Singleton @@ -33,13 +39,35 @@ constructor( private val nsdManagerLazy: dagger.Lazy, private val connectivityManager: dagger.Lazy, private val dispatchers: CoroutineDispatchers, + @ProcessLifecycle private val processLifecycle: Lifecycle, ) { - val networkAvailable: Flow - get() = connectivityManager.get().networkAvailable().flowOn(dispatchers.io).conflate() + val networkAvailable: Flow by lazy { + connectivityManager + .get() + .networkAvailable() + .flowOn(dispatchers.io) + .conflate() + .shareIn( + scope = processLifecycle.coroutineScope, + started = SharingStarted.WhileSubscribed(5000), + replay = 1, + ) + .distinctUntilChanged() + } - val resolvedList: Flow> - get() = nsdManagerLazy.get().serviceList(SERVICE_TYPE).flowOn(dispatchers.io).conflate() + val resolvedList: Flow> by lazy { + nsdManagerLazy + .get() + .serviceList(SERVICE_TYPE) + .flowOn(dispatchers.io) + .conflate() + .shareIn( + scope = processLifecycle.coroutineScope, + started = SharingStarted.WhileSubscribed(5000), + replay = 1, + ) + } companion object { internal const val SERVICE_PORT = 4403 diff --git a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt index 25a7a7bfd..d4ef20107 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt +++ b/app/src/main/java/com/geeksville/mesh/repository/radio/RadioInterfaceService.kt @@ -109,26 +109,34 @@ constructor( */ private var isStarted = false - private fun initStateListeners() { - bluetoothRepository.state - .onEach { state -> - if (state.enabled) { - startInterface() - } else if (radioIf is NordicBleInterface) { - stopInterface() - } - } - .launchIn(processLifecycle.coroutineScope) + @Volatile private var listenersInitialized = false - networkRepository.networkAvailable - .onEach { state -> - if (state) { - startInterface() - } else if (radioIf is TCPInterface) { - stopInterface() + private fun initStateListeners() { + if (listenersInitialized) return + synchronized(this) { + if (listenersInitialized) return + listenersInitialized = true + + bluetoothRepository.state + .onEach { state -> + if (state.enabled) { + startInterface() + } else if (radioIf is NordicBleInterface) { + stopInterface() + } } - } - .launchIn(processLifecycle.coroutineScope) + .launchIn(processLifecycle.coroutineScope) + + networkRepository.networkAvailable + .onEach { state -> + if (state) { + startInterface() + } else if (radioIf is TCPInterface) { + stopInterface() + } + } + .launchIn(processLifecycle.coroutineScope) + } } companion object {