mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
refactor(service): harden KMP service layer — database init, connection reliability, handler decomposition (#4992)
This commit is contained in:
parent
e111b61e4e
commit
6af3ad6f0c
62 changed files with 3808 additions and 735 deletions
|
|
@ -61,6 +61,7 @@ import okio.Path.Companion.toPath
|
|||
import org.jetbrains.skia.Image
|
||||
import org.koin.core.context.startKoin
|
||||
import org.meshtastic.core.common.util.MeshtasticUri
|
||||
import org.meshtastic.core.database.desktopDataDir
|
||||
import org.meshtastic.core.navigation.SettingsRoutes
|
||||
import org.meshtastic.core.navigation.TopLevelDestination
|
||||
import org.meshtastic.core.navigation.rememberMultiBackstack
|
||||
|
|
@ -248,7 +249,7 @@ fun main(args: Array<String>) = application(exitProcessOnExit = false) {
|
|||
},
|
||||
) {
|
||||
setSingletonImageLoaderFactory { context ->
|
||||
val cacheDir = System.getProperty("user.home") + "/.meshtastic/image_cache_v3"
|
||||
val cacheDir = desktopDataDir() + "/image_cache_v3"
|
||||
ImageLoader.Builder(context)
|
||||
.components {
|
||||
add(KtorNetworkFetcherFactory(httpClient = httpClient))
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import okio.Path.Companion.toPath
|
|||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.module
|
||||
import org.meshtastic.core.common.BuildConfigProvider
|
||||
import org.meshtastic.core.database.desktopDataDir
|
||||
import org.meshtastic.core.datastore.serializer.ChannelSetSerializer
|
||||
import org.meshtastic.core.datastore.serializer.LocalConfigSerializer
|
||||
import org.meshtastic.core.datastore.serializer.LocalStatsSerializer
|
||||
|
|
@ -43,16 +44,6 @@ import org.meshtastic.proto.LocalConfig
|
|||
import org.meshtastic.proto.LocalModuleConfig
|
||||
import org.meshtastic.proto.LocalStats
|
||||
|
||||
/**
|
||||
* Resolves the desktop data directory for persistent storage (DataStore files, Room database). Defaults to
|
||||
* `~/.meshtastic/`. Override via `MESHTASTIC_DATA_DIR` environment variable.
|
||||
*/
|
||||
private fun desktopDataDir(): String {
|
||||
val override = System.getenv("MESHTASTIC_DATA_DIR")
|
||||
if (!override.isNullOrBlank()) return override
|
||||
return System.getProperty("user.home") + "/.meshtastic"
|
||||
}
|
||||
|
||||
/** Creates a file-backed [DataStore]<[Preferences]> at the given path under the data directory. */
|
||||
private fun createPreferencesDataStore(name: String, scope: CoroutineScope): DataStore<Preferences> {
|
||||
val dir = desktopDataDir() + "/datastore"
|
||||
|
|
@ -90,7 +81,14 @@ private class DesktopProcessLifecycleOwner : LifecycleOwner {
|
|||
*/
|
||||
@Suppress("InjectDispatcher")
|
||||
fun desktopPlatformModule() = module {
|
||||
includes(desktopPreferencesDataStoreModule(), desktopProtoDataStoreModule())
|
||||
// Application-lifetime scope shared by all DataStore instances. Per the DataStore docs:
|
||||
// "The Job within this context dictates the lifecycle of the DataStore's internal operations.
|
||||
// Ensure it is an application-scoped context that is not canceled by UI lifecycle events."
|
||||
// DataStore has no close() API — the in-memory cache is released only when this Job is cancelled
|
||||
// (at process exit). Using SupervisorJob so a single store's failure doesn't cascade.
|
||||
val dataStoreScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
includes(desktopPreferencesDataStoreModule(dataStoreScope), desktopProtoDataStoreModule(dataStoreScope))
|
||||
|
||||
// -- Build config --
|
||||
single<BuildConfigProvider> {
|
||||
|
|
@ -109,10 +107,7 @@ fun desktopPlatformModule() = module {
|
|||
}
|
||||
|
||||
/** Named [DataStore]<[Preferences]> instances for all preference domains. */
|
||||
@Suppress("InjectDispatcher")
|
||||
private fun desktopPreferencesDataStoreModule() = module {
|
||||
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
private fun desktopPreferencesDataStoreModule(scope: CoroutineScope) = module {
|
||||
single<DataStore<Preferences>>(named("AnalyticsDataStore")) { createPreferencesDataStore("analytics", scope) }
|
||||
single<DataStore<Preferences>>(named("HomoglyphEncodingDataStore")) {
|
||||
createPreferencesDataStore("homoglyph_encoding", scope)
|
||||
|
|
@ -135,9 +130,7 @@ private fun desktopPreferencesDataStoreModule() = module {
|
|||
}
|
||||
|
||||
/** Proto [DataStore] instances (OkioStorage-backed). */
|
||||
@Suppress("InjectDispatcher")
|
||||
private fun desktopProtoDataStoreModule() = module {
|
||||
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
private fun desktopProtoDataStoreModule(scope: CoroutineScope) = module {
|
||||
val protoDir = desktopDataDir() + "/datastore"
|
||||
|
||||
single<DataStore<LocalConfig>>(named("CoreLocalConfigDataStore")) {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,10 @@ class DesktopRadioTransportFactory(
|
|||
TCPInterface(service, dispatchers, address.removePrefix(InterfaceId.TCP.id.toString()))
|
||||
}
|
||||
address.startsWith(InterfaceId.SERIAL.id) -> {
|
||||
SerialTransport(portName = address.removePrefix(InterfaceId.SERIAL.id.toString()), service = service)
|
||||
SerialTransport.open(
|
||||
portName = address.removePrefix(InterfaceId.SERIAL.id.toString()),
|
||||
service = service,
|
||||
)
|
||||
}
|
||||
else -> error("Unsupported transport for address: $address")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ class NoopRadioInterfaceService : RadioInterfaceService {
|
|||
|
||||
override val receivedData = MutableSharedFlow<ByteArray>()
|
||||
override val meshActivity = MutableSharedFlow<MeshActivity>()
|
||||
override val connectionError = MutableSharedFlow<String>()
|
||||
|
||||
override fun sendToRadio(bytes: ByteArray) {
|
||||
logWarn("NoopRadioInterfaceService.sendToRadio(${bytes.size} bytes)")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue