In a KMP Compose Multiplatform app, I'm starting K...
# koin
j
In a KMP Compose Multiplatform app, I'm starting Koin with
KoinApplication(application = { ...
as described here. But the Android app sometimes crashes with:
org.koin.core.error.KoinAppAlreadyStartedException: Trying to run new Koin Application whereas Koin is already started. Use 'KoinContext()' instead of check for any 'startKoin' usage.
I'm assuming this happens when the Activity is recreated, as this is where the composable
App
is launched and where the stack trace stems from. Shouldn't
KoinApplication
expect this behavior of Activity recreation on Android?
n
An App is not an Activity. Don't start Koin every time you start an Activity, start Koin only at Application start
j
That's what I would expect. But why does the documentation say that
KoinApplication
can be used in an Android Jetpack Compose
App()
composable, which is started from an `Activity`'s
setContent
, not from the Android
Application
? > Else if you want to start a new Koin instance from your Compose app, The function
KoinApplication
helps to create Koin application instance, as a Composable. This is a replacement of the classic
startKoin
application function.
Does anyone have a proper example of how
KoinApplication(application = { ...
should be used in an Android Compose app? The documentation isn't clear how this should be expected to be called from an
Activity
.
y
you have to put this in your appActivity
Copy code
override fun onDestroy() {
    super.onDestroy()
    stopKoin()
}
j
This would make it so singletons are scoped to the
Activity
lifecycle then. In that case, I'd prefer to stick with
startKoin
in the
Application
rather than using this
KoinApplication
API on Android.
j
The compose example works differently, from what I recall its like a lazy loaded Singleton like Compose local composition. I do this manually myself with my own Koin context/component. There is nothing says Koin need to be started or stopped from Application level at all. However if its from library SDK or such I recommend using COntentProvider/androidx startup.
The actual code for KoinApplication is:
Copy code
@Composable
@Throws(ApplicationAlreadyStartedException::class)
fun KoinApplication(
    application: KoinAppDeclaration,
    content: @Composable () -> Unit
) {
    val koinApplication = remember(application) {
        val alreadyExists = KoinPlatformTools.defaultContext().getOrNull() != null
        if (alreadyExists) {
            throw ApplicationAlreadyStartedException("Trying to run new Koin Application whereas Koin is already started. Use 'KoinContext()' instead of check for any 'startKoin' usage. ")
        } else {
            startKoin(application)
        }
    }
    CompositionLocalProvider(
        LocalKoinApplication provides koinApplication.koin,
        LocalKoinScope provides koinApplication.koin.scopeRegistry.rootScope
    ) {
        content()
    }
}
See https://github.com/InsertKoinIO/koin/blob/main/projects/compose/koin-compose/src/commonMain/kotlin/org/koin/compose/KoinApplication.kt how they achieve things 🙂 Documentation I agree is not very clear for compose scenarios.
Also imo not very efficient launching Koin from Compose level or well Activity level. I recommend instead start it with koinApplication or such in Application level and store in your own singleton. Then in Compose attach into that one already launched in parallell in app launch. From my experience this is the most optimized way of doing this. And gets even better if using androidx startup I think.
j
This is what I've opted to do, just starting it with
startKoin
in the
Application
and using
KoinAndroidContext
to wrap the Compose app.
👍 1
760 Views