Sto cercando di aggiungere Dagger 2 al mio progetto. Sono stato in grado di iniettare ViewModels (componente AndroidX Architecture) per i miei frammenti.
Ho un ViewPager che ha 2 istanze dello stesso frammento (solo una piccola modifica per ciascuna scheda) e in ciascuna scheda, sto osservando un LiveData
aggiornamento per la modifica dei dati (dall'API).
Il problema è che quando arriva la risposta api e la aggiorna LiveData
, gli stessi dati nel frammento attualmente visibile vengono inviati agli osservatori in tutte le schede. (Penso che questo sia probabilmente a causa della portata del ViewModel
).
Ecco come sto osservando i miei dati:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
activityViewModel.expenseList.observe(this, Observer {
swipeToRefreshLayout.isRefreshing = false
viewAdapter.setData(it)
})
....
}
Sto usando questa classe per fornire ViewModel
:
class ViewModelProviderFactory @Inject constructor(creators: MutableMap<Class<out ViewModel?>?, Provider<ViewModel?>?>?) :
ViewModelProvider.Factory {
private val creators: MutableMap<Class<out ViewModel?>?, Provider<ViewModel?>?>? = creators
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
var creator: Provider<out ViewModel?>? = creators!![modelClass]
if (creator == null) { // if the viewmodel has not been created
// loop through the allowable keys (aka allowed classes with the @ViewModelKey)
for (entry in creators.entries) { // if it's allowed, set the Provider<ViewModel>
if (modelClass.isAssignableFrom(entry.key!!)) {
creator = entry.value
break
}
}
}
// if this is not one of the allowed keys, throw exception
requireNotNull(creator) { "unknown model class $modelClass" }
// return the Provider
return try {
creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
companion object {
private val TAG: String? = "ViewModelProviderFactor"
}
}
Sto vincolando il mio in ViewModel
questo modo:
@Module
abstract class ActivityViewModelModule {
@MainScope
@Binds
@IntoMap
@ViewModelKey(ActivityViewModel::class)
abstract fun bindActivityViewModel(viewModel: ActivityViewModel): ViewModel
}
Sto usando @ContributesAndroidInjector
per il mio frammento in questo modo:
@Module
abstract class MainFragmentBuildersModule {
@ContributesAndroidInjector
abstract fun contributeActivityFragment(): ActivityFragment
}
E sto aggiungendo questi moduli al mio MainActivity
sottocomponente in questo modo:
@Module
abstract class ActivityBuilderModule {
...
@ContributesAndroidInjector(
modules = [MainViewModelModule::class, ActivityViewModelModule::class,
AuthModule::class, MainFragmentBuildersModule::class]
)
abstract fun contributeMainActivity(): MainActivity
}
Ecco il mio AppComponent
:
@Singleton
@Component(
modules =
[AndroidSupportInjectionModule::class,
ActivityBuilderModule::class,
ViewModelFactoryModule::class,
AppModule::class]
)
interface AppComponent : AndroidInjector<SpenmoApplication> {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
}
Sto estendendo DaggerFragment
e iniettando in ViewModelProviderFactory
questo modo:
@Inject
lateinit var viewModelFactory: ViewModelProviderFactory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
....
activityViewModel =
ViewModelProviders.of(this, viewModelFactory).get(key, ActivityViewModel::class.java)
activityViewModel.restartFetch(hasReceipt)
}
il key
sarà diverso per entrambi i frammenti.
Come posso assicurarmi che solo l'osservatore del frammento attuale venga aggiornato.
MODIFICA 1 ->
Ho aggiunto un progetto di esempio con l'errore. Sembra che il problema si stia verificando solo quando viene aggiunto un ambito personalizzato. Controlla il progetto di esempio qui: link Github
master
branch ha l'app con il problema. Se aggiorni una scheda (scorri per aggiornare), il valore aggiornato viene riflesso in entrambe le schede. Questo succede solo quando aggiungo un ambito personalizzato ( @MainScope
).
working_fine
branch ha la stessa app senza ambito personalizzato e funziona bene.
Per favore fatemi sapere se la domanda non è chiara.
working_fine
branch? Perché hai bisogno dell'ambito?