new: integrate a dispatcher provider and dispatch work

This commit is contained in:
2026-06-22 14:58:18 +02:00
parent 38d6107423
commit 8926b3237b
16 changed files with 151 additions and 9 deletions
+53
View File
@@ -0,0 +1,53 @@
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidMultiplatformLibrary)
}
kotlin {
iosArm64()
iosSimulatorArm64()
jvm()
js {
browser()
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser()
}
androidLibrary {
namespace = "fr.ajaury.gwenedeg.core.coroutines"
compileSdk =
libs.versions.androidCompileSdk
.get()
.toInt()
minSdk =
libs.versions.androidMinSdk
.get()
.toInt()
compilerOptions {
jvmTarget = JvmTarget.JVM_11
}
}
sourceSets {
commonMain.dependencies {
// Coroutines
implementation(libs.kotlinx.coroutinesCore)
// DI
implementation(project.dependencies.platform(libs.koin.bom))
implementation(libs.koin.core)
}
commonTest.dependencies {
implementation(libs.kotlin.test)
}
}
}
@@ -0,0 +1,6 @@
package fr.ajaury.gwenedeg.core.coroutines.data
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
internal actual val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
@@ -0,0 +1,11 @@
package fr.ajaury.gwenedeg.core.coroutines.data
import fr.ajaury.gwenedeg.core.coroutines.domain.DispatcherProvider
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
internal class DefaultDispatcherProvider : DispatcherProvider {
override val main: CoroutineDispatcher = Dispatchers.Main
override val default: CoroutineDispatcher = Dispatchers.Default
override val io: CoroutineDispatcher = ioDispatcher
}
@@ -0,0 +1,5 @@
package fr.ajaury.gwenedeg.core.coroutines.data
import kotlinx.coroutines.CoroutineDispatcher
internal expect val ioDispatcher: CoroutineDispatcher
@@ -0,0 +1,10 @@
package fr.ajaury.gwenedeg.core.coroutines.di
import fr.ajaury.gwenedeg.core.coroutines.data.DefaultDispatcherProvider
import fr.ajaury.gwenedeg.core.coroutines.domain.DispatcherProvider
import org.koin.core.module.Module
import org.koin.dsl.module
val coroutinesModule: Module = module {
single<DispatcherProvider> { DefaultDispatcherProvider() }
}
@@ -0,0 +1,9 @@
package fr.ajaury.gwenedeg.core.coroutines.domain
import kotlinx.coroutines.CoroutineDispatcher
interface DispatcherProvider {
val main: CoroutineDispatcher
val default: CoroutineDispatcher
val io: CoroutineDispatcher
}
@@ -0,0 +1,8 @@
package fr.ajaury.gwenedeg.core.coroutines.data
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
// `Dispatchers.IO` is not public API on Native; `Dispatchers.Default` is multi-threaded here, so it
// still runs work off the main thread.
internal actual val ioDispatcher: CoroutineDispatcher = Dispatchers.Default
@@ -0,0 +1,7 @@
package fr.ajaury.gwenedeg.core.coroutines.data
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
// JS is single-threaded; there is no dedicated I/O dispatcher, use default.
internal actual val ioDispatcher: CoroutineDispatcher = Dispatchers.Default
@@ -0,0 +1,6 @@
package fr.ajaury.gwenedeg.core.coroutines.data
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
internal actual val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
@@ -0,0 +1,7 @@
package fr.ajaury.gwenedeg.core.coroutines.data
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
// Wasm is single-threaded; there is no dedicated I/O dispatcher, use default
internal actual val ioDispatcher: CoroutineDispatcher = Dispatchers.Default
+5 -1
View File
@@ -39,9 +39,13 @@ kotlin {
sourceSets { sourceSets {
commonMain.dependencies { commonMain.dependencies {
// Logging // Core modules
implementation(projects.core.coroutines)
implementation(projects.core.logging) implementation(projects.core.logging)
// Coroutines
implementation(libs.kotlinx.coroutinesCore)
// DI // DI
implementation(project.dependencies.platform(libs.koin.bom)) implementation(project.dependencies.platform(libs.koin.bom))
implementation(libs.koin.core) implementation(libs.koin.core)
@@ -1,15 +1,19 @@
package fr.ajaury.gwenedeg.subtitle.data package fr.ajaury.gwenedeg.subtitle.data
import fr.ajaury.gwenedeg.core.coroutines.domain.DispatcherProvider
import fr.ajaury.gwenedeg.core.logging.domain.Logger import fr.ajaury.gwenedeg.core.logging.domain.Logger
import fr.ajaury.gwenedeg.subtitle.domain.Subtitle import fr.ajaury.gwenedeg.subtitle.domain.Subtitle
import fr.ajaury.gwenedeg.subtitle.domain.SubtitleRepository import fr.ajaury.gwenedeg.subtitle.domain.SubtitleRepository
import fr.ajaury.gwenedeg.subtitle.model.CantGetSubtitleException import fr.ajaury.gwenedeg.subtitle.model.CantGetSubtitleException
import kotlinx.coroutines.withContext
internal class LrcSubtitleRepository( internal class LrcSubtitleRepository(
private val parser: LrcParser, private val parser: LrcParser,
private val dispatcherProvider: DispatcherProvider,
private val logger: Logger, private val logger: Logger,
) : SubtitleRepository { ) : SubtitleRepository {
override suspend fun getSubtitle(content: String): Subtitle = override suspend fun getSubtitle(content: String): Subtitle =
withContext(dispatcherProvider.default) {
try { try {
parser.parse(content = content) parser.parse(content = content)
} catch (exception: Exception) { } catch (exception: Exception) {
@@ -17,4 +21,5 @@ internal class LrcSubtitleRepository(
logger.error(message = message, throwable = exception) logger.error(message = message, throwable = exception)
throw CantGetSubtitleException(message) throw CantGetSubtitleException(message)
} }
}
} }
+1
View File
@@ -39,6 +39,7 @@ include(":shared")
// Core modules // Core modules
include(":core:audioplayer") include(":core:audioplayer")
include(":core:coroutines")
include(":core:logging") include(":core:logging")
// Data modules // Data modules
+1
View File
@@ -60,6 +60,7 @@ kotlin {
commonMain.dependencies { commonMain.dependencies {
// Core modules // Core modules
implementation(projects.core.audioplayer) implementation(projects.core.audioplayer)
implementation(projects.core.coroutines)
implementation(projects.core.logging) implementation(projects.core.logging)
// Data modules // Data modules
@@ -7,6 +7,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation3.runtime.NavEntry import androidx.navigation3.runtime.NavEntry
import androidx.navigation3.ui.NavDisplay import androidx.navigation3.ui.NavDisplay
import androidx.savedstate.compose.serialization.serializers.SnapshotStateListSerializer import androidx.savedstate.compose.serialization.serializers.SnapshotStateListSerializer
import fr.ajaury.gwenedeg.core.coroutines.di.coroutinesModule
import fr.ajaury.gwenedeg.core.logging.di.loggingModule import fr.ajaury.gwenedeg.core.logging.di.loggingModule
import fr.ajaury.gwenedeg.di.sharedModule import fr.ajaury.gwenedeg.di.sharedModule
import fr.ajaury.gwenedeg.navigation.Route import fr.ajaury.gwenedeg.navigation.Route
@@ -26,6 +27,7 @@ fun App() {
modules( modules(
sharedModule, sharedModule,
audioPlayerModule, audioPlayerModule,
coroutinesModule,
loggingModule, loggingModule,
subtitleModule subtitleModule
) )
@@ -1,8 +1,15 @@
package fr.ajaury.gwenedeg.resourcereader.data package fr.ajaury.gwenedeg.resourcereader.data
import fr.ajaury.gwenedeg.core.coroutines.domain.DispatcherProvider
import fr.ajaury.gwenedeg.resourcereader.data.domain.ResourceReader import fr.ajaury.gwenedeg.resourcereader.data.domain.ResourceReader
import gwenedeg.shared.generated.resources.Res import gwenedeg.shared.generated.resources.Res
import kotlinx.coroutines.withContext
internal class ComposeResourceReader : ResourceReader { internal class ComposeResourceReader(
override suspend fun read(resourcePath: String): String = Res.readBytes(resourcePath).decodeToString() private val dispatcherProvider: DispatcherProvider,
) : ResourceReader {
override suspend fun read(resourcePath: String): String =
withContext(dispatcherProvider.io) {
Res.readBytes(resourcePath).decodeToString()
}
} }