google / dagger

A fast dependency injector for Android and Java.
https://dagger.dev
Apache License 2.0
17.41k stars 2.01k forks source link

Singletons are initilized several times when injected into application #3361

Closed yaugenka closed 2 years ago

yaugenka commented 2 years ago

Using dagger hilt 2.40.5

I have a singleton classe which needs to be initialized at app start and is accessed in a non-main activity. So I had it injected into Application and the activity.


@Singleton
class SubsManager() {
    init {
        log("init")
    }
}

@HiltAndroidApp
class App : Application() {
    @Inject lateinit var subsManager: SubsManager
}

@AndroidEntryPoint
class FilterActivity : AppCompatActivity() {
    @Inject lateinit var subsManager: SubsManager
}

The problem is that the sington is initialized twice. I've tried injecting into MainActivity instead of the Application and works as expected then.

bcorso commented 2 years ago

Hi @yaugenka, I could not reproduce this.

One guess is that you have a module that is also providing SubsManager which is not scoped.

Also, if your actual SubsManager class matches the one in your snippet, the @Singleton scope is effectively useless since the class does not have an @Inject-annotated constructor. However, even if your actual SubsManager class does have an @Inject-annotated constructor you should still verify it is not provided via a module since bindings provided via a module can implicitly override bindings provided via an @Inject-annotated constructor.

yaugenka commented 2 years ago

@bcorso I've tried it on a brand new project and could not reproduce it either. But then I digged futher and noticed the following. The project this issue is reproduced in, has both Google and Yandex ads SDKs initialized in the SubsManager init method, and the issue is accompanied with google's sdk throwing RuntimeException: Using WebView from more than one process at once with the same data directory is not supported. The important thing is that if the SubsManager is injected into the MainActivity instead of the Application then there is no exception. You can find a playground project here (you will need to define an admob_app_id string and put google-services.json file into ./app to make it run). The project does not demonstrate the double initialization, but reproduces the exception which I guess should drive to proper issue.

bcorso commented 2 years ago

The project this issue is reproduced in, has both Google and Yandex ads SDKs initialized in the SubsManager init method, and the issue is accompanied with google's sdk throwing RuntimeException: Using WebView from more than one process at once with the same data directory is not supported. The important thing is that if the SubsManager is injected into the MainActivity instead of the Application then there is no exception.

If you're dealing with multiple processes then it's not surprising that you will be getting multiple instances of a singleton object. Dagger/Hilt guarantees one instance of a @Singleton scoped binding per SingletonComponent instance, but if you are accessing bindings from multiple processes each process must have its own SingletonComponent instance.

It sounds like you need to avoid creating SubsManager on one of the processes.

yaugenka commented 2 years ago

That's the point of it, that there is no intention to create multiple processes. It looks like the injection into Application under these circumstancies does that. Did you check the example project?

danysantiago commented 2 years ago

You need to take a look at the SDKs being used, specifically the Yandex SDK seems to be starting a service in a secondary process called :Metrica (see the merged manifest in the sample app), they seem to have some instructions about working around initialization of multiple Application classes since in Android every process will use the same Application class but they'll be different instances.

bcorso commented 2 years ago

@yaugenka I'm going to close this because I don't think there's anything we can do on our side.

If you're forced to create the application in multiple processes I think you'll have to add checks to your own code that determines which process you're in before initializing certain things that should only be initialized on the main process.