InsertKoinIO / koin

Koin - a pragmatic lightweight dependency injection framework for Kotlin & Kotlin Multiplatform
https://insert-koin.io
Apache License 2.0
9.08k stars 719 forks source link

IllegalStateException: at org.koin.core.context.GlobalContext.get #1002

Closed brewin closed 3 years ago

brewin commented 3 years ago

I've just released an app using Koin 2.2.2 and I'm seeing crash reports on devices running Android 8.1 (SDK 27). I haven't seen it on any other versions of Android yet, so it could be a bug in 8.1. So far, the devices it's crashing on are Motorola Moto G Plus and something called DigiLand DL1016. Edit: Actually, I just got a crash report on Samsung Galaxy Note8 running Android 9 (SDK 28). So it's not just 8.1.

I'm just calling getViewModel<MyViewModel>() in MainActivity.onCreate().

java.lang.RuntimeException: 
  at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2861)
  at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2943)
  at android.app.ActivityThread.-wrap11 (Unknown Source)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1630)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:164)
  at android.app.ActivityThread.main (ActivityThread.java:6626)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:438)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:811)
Caused by: java.lang.IllegalStateException: 
  at org.koin.core.context.GlobalContext.get (GlobalContext.java:1)
  at org.koin.android.ext.android.ComponentCallbackExtKt.getKoin (ComponentCallbackExtKt.java:1)
  at org.koin.androidx.viewmodel.ext.android.ActivityExtKt.getViewModel (ActivityExtKt.java:1)
  at com.mypackage.MainActivity.onCreate (MainActivity.java:1)
  at android.app.Activity.performCreate (Activity.java:7032)
  at android.app.Activity.performCreate (Activity.java:7023)
  at android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1236)
  at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2814)
  at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2943)
  at android.app.ActivityThread.-wrap11 (Unknown Source)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1630)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:164)
  at android.app.ActivityThread.main (ActivityThread.java:6626)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:438)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:811)
LebonNic commented 3 years ago

Same for me on Samsung Galaxy S6, S7 and Galaxy Tab S2 (Android 7) and some Huawei phones (Android 7 and 8)... Any idea for solving the issue? I don't have the corresponding devices to test :(

brewin commented 3 years ago

No, I haven't solved it. It's the most common crash report I get. It happens on dozens of different devices running Android 8 to 10. I haven't seen it on Android 7 or 11, but that might be because not many of my users are running those versions.

I've had reports of it on a Galaxy S9, which I own and test on. But I haven't been able to reproduce this issue on it.

dnsknr commented 3 years ago

Hi everyone,

since I also got the error and it seems to be related to a similar issue I had with Dagger before migrating to Koin. It had to do with the app's custom Application class not being used on some Android versions.

See following issues which seem to be related: https://github.com/InsertKoinIO/koin/issues/286#issuecomment-437994119 https://github.com/google/dagger/issues/748#issuecomment-339235062

I updated my apps as follows:

object KoinStarter {
    @Synchronized
    fun start(context: Context): Koin {
        return GlobalContext.getOrNull() ?: startKoin {
            androidLogger()
            androidContext(context.applicationContext)
            modules(appModule)
        }.koin
    }
}

Instead of starting Koin in my Application class I am now starting it in my activities as follows:

override fun onCreate(savedInstanceState: Bundle?) {
    KoinStarter.start(this)
    super.onCreate(savedInstanceState)
}

I am still in staged rollout, but so far it looks promising. But with Dagger it took me several iterations because I could never reproduce this issue.

LebonNic commented 3 years ago

Hi!

Thanks for sharing this solution :) Indeed, I use a custom Application class to start my Koin modules. And if I understand correctly, there is no guarantee that this class is instanciated and used by Android when it starts my app...

So the solution is to move the Koin startup in the onCreate methods of the app's activities, but by doing this, don't you risk to call the startup method several times? Maybe, you should have a boolean to check if the KoinStarter.start method has already been called?

And the doc is not clear on this point because in the Android part they clearly say that the startKoin method should be called in the onCreate method of a custom Application class (see: https://insert-koin.io/docs/quickstart/android#start-koin).

dnsknr commented 3 years ago

You can call KoinStarter.start() as often as you want, GlobalContext.getOrNull() ?: startKoin{...} makes sure that Koin is only started if it was not called before. With @Synchronized I make sure that this works even if multiple activities try to start Koin in parallel.

So far I have not found any negative side effects. Maybe it has some performance implications when starting the activity the first time? Have not looked into this actually.

dnsknr commented 3 years ago

Oh and obviously you can not have any injections in your application class when starting it in the activity...

LebonNic commented 3 years ago

Thank you, I will try it :)

deinlandel commented 3 years ago

Is this Bug specific for Koin 2.2.2? Which lower version of Koin is safe to use?

dnsknr commented 3 years ago

I do not know, I started using Koin with 2.2.2 and so cannot tell if any previous version has the same issue. My assumption is, any previous version depending on a custom Application class will face sooner or later some weired exceptions on 7.x and 8.x Android versions, sometimes this may even be vendor specific (saw it for some Samsung Android versions).

Actually I never received any user complaint about this, just a ton of crash reports on Google Play, never on Firebase. Seems to crash the app even before Crashlytics is ready. So at the end this could also just be some background app starting / house keeping of Android (read something about backup processes) that the user never notice. As said before I could never reproduce the issue, but with my code above the crash reports are gone.

brewin commented 3 years ago

Since this is a bug in Android that has been around for at least five years, I'm closing this issue. If you need a custom Application class, then the only workaround is to set allowBackup=false as described here. I also never received any user complaints about this, so maybe ignoring it is the best option.

grndvl1 commented 2 years ago

Mine crashes in CI builds occasionally on just tests. Wondering what would be the solution there where we use a testApplication class and Modules class to start all the injections using mocks for all the factories.

alexymumo commented 1 year ago

Seems that the error persists even on latest version 3.1.5

Process: com.codes.livescore, PID: 9852 java.lang.IncompatibleClassChangeError: Found interface kotlin.time.TimeMark, but class was expected (declaration of 'kotlin.time.TimeMark' appears in /data/app/com.codes.livescore-YrPXl18atpdMztTR_TLNGQ==/base.apk!classes6.dex) at org.koin.core.time.MeasureKt.measureDuration(Measure.kt:53) at org.koin.core.KoinApplication.modules(KoinApplication.kt:59) at org.koin.core.KoinApplication.modules(KoinApplication.kt:50) at com.codes.livescore.KoinStarter$start$1.invoke(LivescoreApplication.kt:30) at com.codes.livescore.KoinStarter$start$1.invoke(LivescoreApplication.kt:27) at org.koin.core.context.GlobalContext.startKoin(GlobalContext.kt:65) at org.koin.core.context.DefaultContextExtKt.startKoin(DefaultContextExt.kt:31) at com.codes.livescore.KoinStarter.start(LivescoreApplication.kt:27) at com.codes.livescore.LivescoreApplication.onCreate(LivescoreApplication.kt:19) at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1189) at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6696) at android.app.ActivityThread.access$1400(ActivityThread.java:237) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1933) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:264) at android.app.ActivityThread.main(ActivityThread.java:7663) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:980)