Using Dagger 2 singletons in View Models on Android
Using Dagger 2 singletons in View Models on Android
I want my application to have the following architecture :
-di // dependancy injection package
-model
--datasources // implementations of repositories, using dao to return data
--dao // access to the database (room)
--repositories // interfaces
-ui // activities, fragments...
-viewmodels // viewmodels of each fragment / activity which will return data to the views in the ui package
On Android, people seem to usually have the dependancy injection in the activities, but I want to inject my repositories in view models.
I have one particular viewmodel that uses a manually created LiveData
class NavigationViewModel(application: Application) : AndroidViewModel(application)
init
DaggerAppComponent.builder()
.appModule(AppModule(getApplication()))
.roomModule(RoomModule(getApplication()))
.build()
.injectNavigationViewModel(this)
@Inject
lateinit var displayScreenRepository: DisplayScreenRepository
fun setScreenToDisplay(screen: DisplayScreen)
displayScreenRepository.setScreenToDisplay(screen)
In my RoomModule, I have this provider :
@Singleton
@Provides
internal fun displayScreenRepository(): DisplayScreenRepository
return DisplayScreenDataSource()
The class DisplayScreenDataSource is very simple :
class DisplayScreenDataSource : DisplayScreenRepository
private val screenToDisplay = MutableLiveData<DisplayScreen>()
override fun getScreenToDisplay(): LiveData<DisplayScreen>
return screenToDisplay
override fun setScreenToDisplay(screen: DisplayScreen)
if (Looper.myLooper() == Looper.getMainLooper())
screenToDisplay.value = screen
else
screenToDisplay.postValue(screen)
I want this singleton to be available to an other viewmodel, MainActivityViewModel, so I did this :
class MainActivityViewModel(application: Application) : AndroidViewModel(application)
@Inject
lateinit var displayScreenRepository: DisplayScreenRepository
init
DaggerAppComponent.builder()
.appModule(AppModule(application))
.roomModule(RoomModule(application))
.build()
.injectMainViewModel(this)
But when I run my application, the repositories instantiated don't have the same reference and when I update the value of one LiveData in one of my ViewModel, if I observe the LiveData of an other ViewModel, it is not the same LiveData so is is not updated.
My guess is that I am not correctly instantiating the DaggerAppComponent : it is supposed to be created only once and eventually injected several times.
Is that right?
Where should I be supposed to store the instance of the DaggerAppComponent? In the Application class?
I could have something like that :
class MainActivityViewModel(application: Application) : AndroidViewModel(application)
@Inject
lateinit var displayScreenRepository: DisplayScreenRepository
init
(application as MyApplication).daggerAppComponent.injectMainViewModel(this)
Is this the recommended way?
You should only have a single instance of
AppComponent
, otherwise you will create a new instance of everything you use each time– EpicPandaForce
Aug 23 at 22:05
AppComponent
That's what I did in the end yes. I wondered where to put it, if there are some best practices, I have it in the application class at the moment.
– leb1755
Aug 24 at 6:53
I store it in the component it scopes / where I create it. ActivityComponents in activities, FragmentComponents in fragments, ServiceComponents in services. That's usually a safe bet
– David Medenjak
Aug 24 at 7:31
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Please don't recreate your scoped components everywhere you use them. If you create 2x AppComponent then you have 2x your singletons, each component with their own instance. Create it once, then reuse it.
– David Medenjak
Aug 23 at 15:23