Checklist
Which apps should have this feature?
All Fossify Apps
Feature description
Refactor the internal implementation of ensureBackgroundThread with a coroutine-based approach while preserving the exact same function signature. This requires no updates from callers
The approach I propose is as follows:
1 - Add the coroutines bill of materials 'bom' dependency to libs.versions.toml. We also declare the specific coroutines libraries we want:
# commons/gradle/libs.versions.toml
[versions]
coroutines = "1.9.0"
[libraries]
kotlinx-coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version.ref = "coroutines" }
# BOM libraries we want to use (no versioning needed)
kotlinx-coroutines-test = { "org.jetbrains.kotlinx:kotlinx-coroutines-test" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android" }
2 - Declare the coroutine dependencies in the commons project build.gradle.kts
// commons/build.gradle.kts
dependencies {
// ... existing dependencies ...
api(platform(libs.kotlinx.coroutines.bom)) // platform wrapper needed for automated version alignment
api(libs.kotlinx.coroutines.android)
testImplementation(libs.kotlinx.coroutines.test)
}
3 - Create a dedicated org.fossify.commons.concurrency package and add the following:
DispatcherProviders.kt which contains the coroutine dispatchers. This interface wrapper helps with testability as the callers aren't tightly coupled to a hard-coded dispatcher implementation
// commons/.../concurrency/DispatchersProvider.kt
interface DispatchersProvider {
fun io(): CoroutineDispatcher = Dispatchers.IO
fun main(): CoroutineDispatcher = Dispatchers.Main.immediate
fun default(): CoroutineDispatcher = Dispatchers.Default
}
// For run time. We can make a different implementation for testing while keeping the same calling interface
object DefaultDispatcherProvider : DispatcherProvider {
override val main: CoroutineDispatcher = Dispatchers.Main
override val io: CoroutineDispatcher = Dispatchers.IO
override val default: CoroutineDispatcher = Dispatchers.Default
override val unconfined: CoroutineDispatcher = Dispatchers.Unconfined
}
4 - Then we refactor ensureBackgroundThread to be the following:
// commons/.../helpers/Constants.kt (existing location — no import breakage)
// Provides a container for structured concurrency
private val backgroundScope = CoroutineScope(
SupervisorJob()
+ AppDispatchers.provider.io()
+ CoroutineExceptionHandler { _, throwable ->
Log.e("BackgroundScope", "Uncaught exception in background work", throwable)
}
)
fun ensureBackgroundThread(callback: () -> Unit) {
backgroundScope.launch {
callback()
}
}
The existing function signature is retained which means no changes needed to ensureBackgroundThread.
We get a good foundation for any future concurrency enhancements in future.
Why do you want this feature?
Additional information
Checklist
Which apps should have this feature?
All Fossify Apps
Feature description
Refactor the internal implementation of
ensureBackgroundThreadwith a coroutine-based approach while preserving the exact same function signature. This requires no updates from callersThe approach I propose is as follows:
1 - Add the coroutines bill of materials 'bom' dependency to libs.versions.toml. We also declare the specific coroutines libraries we want:
2 - Declare the coroutine dependencies in the commons project build.gradle.kts
3 - Create a dedicated
org.fossify.commons.concurrencypackage and add the following:DispatcherProviders.ktwhich contains the coroutine dispatchers. This interface wrapper helps with testability as the callers aren't tightly coupled to a hard-coded dispatcher implementation4 - Then we refactor
ensureBackgroundThreadto be the following:The existing function signature is retained which means no changes needed to
ensureBackgroundThread.We get a good foundation for any future concurrency enhancements in future.
Why do you want this feature?
The current implementation of
ensureBackgroundThreadcreates new, unmanaged threads which can be a source of cpu and memory performance issues.By utilising Kotlin's coroutines, we leverage the in-built thread pool management to smartly manage thread creation and caching to mitigate performance issues.
We can support graceful handling of exceptions and cancellation of concurrency jobs.
Coroutines library come with an extensive testing framework to enable testing of concurrent workflows and help with Setup unit tests #821
The coroutines framework is quite extensible enabling future opportunities of better integration to other async workflows e.g. Room's in-built coroutine support or lifecycle-aware components
Enable future opportunities to implement unidirectional reactive flows (e.g. MVVM) with the Flows API
Additional information