Nous en avons discuté, mais nous ne connaissons pas la raison de la création d'une fabrique de viewmodels pour créer un viewmodel au lieu d'instancier directement le viewmodel. Quel est le gain de créer une usine qui ne fait que créer le viewmodel?
Je viens de mettre un exemple simple de la façon dont je l'ai fait sans Factory
voici le module kodein:
val heroesRepositoryModel = Kodein {
bind<HeroesRepository>() with singleton {
HeroesRepository()
}
bind<ApiDataSource>() with singleton {
DataModule.create()
}
bind<MainViewModel>() with provider {
MainViewModel()
}
}
Le morceau de l'activité où j'instancie le modèle de vue sans utiliser l'usine
class MainActivity : AppCompatActivity() {
private lateinit var heroesAdapter: HeroAdapter
private lateinit var viewModel: MainViewModel
private val heroesList = mutableListOf<Heroes.MapHero>()
private var page = 0
private var progressBarUpdated = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProviders.of(this)
.get(MainViewModel::class.Java)
initAdapter()
initObserver()
findHeroes()
}
Le ViewModel où j'instancie directement le cas d'utilisation sans l'avoir dans le constructeur
class MainViewModel : ViewModel(), CoroutineScope {
private val heroesRepository: HeroesRepository = heroesRepositoryModel.instance()
val data = MutableLiveData<List<Heroes.MapHero>>()
private var job: Job = Job()
override val coroutineContext: CoroutineContext
get() = uiContext + job
fun getHeroesFromRepository(page: Int) {
launch {
try {
val response = heroesRepository.getHeroes(page).await()
data.value = response.data.results.map { it.convertToMapHero() }
} catch (e: HttpException) {
data.value = null
} catch (e: Throwable) {
data.value = null
}
}
}
override fun onCleared() {
super.onCleared()
job.cancel()
}
}
Voici donc un exemple utilisant l'usine
class ListFragment : Fragment(), KodeinAware, ContactsAdapter.OnContactListener {
override val kodein by closestKodein()
private lateinit var adapterContacts: ContactsAdapter
private val mainViewModelFactory: MainViewModelFactory by instance()
private val mainViewModel: MainViewModel by lazy {
activity?.run {
ViewModelProviders.of(this, mainViewModelFactory)
.get(MainViewModel::class.Java)
} ?: throw Exception("Invalid Activity")
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_list, container, false)
}
Le viewmodelfactory:
class MainViewModelFactory (private val getContacts: GetContacts) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(MainViewModel::class.Java)) {
return MainViewModel(getContacts) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
Et le viewmodel:
class MainViewModel(private val getContacts: GetContacts) : BaseViewModel() {
lateinit var gamesList: LiveData<PagedList<Contact>>
var contactsSelectedData: MutableLiveData<List<Contact>> = MutableLiveData()
var contactsSelected: ArrayList<Contact> = ArrayList()
private val pagedListConfig by lazy {
PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setInitialLoadSizeHint(PAGES_CONTACTS_SIZE)
.setPageSize(PAGES_CONTACTS_SIZE)
.setPrefetchDistance(PAGES_CONTACTS_SIZE*2)
.build()
}
Voici le premier exemple complet:
https://github.com/ibanarriolaIT/Marvel/tree/mvvm
Et le deuxième exemple complet:
Nous ne pouvons pas créer ViewModel par nous-mêmes. Nous avons besoin de l'utilitaire ViewModelProviders fourni par Android pour créer ViewModels.
Mais ViewModelProviders ne peut instancier que ViewModels sans constructeur arg.
Donc, si j'ai un ViewModel avec plusieurs arguments, je dois utiliser une fabrique que je peux transmettre à ViewModelProviders pour utiliser lorsqu'une instance de MyViewModel est requise.
Par exemple -
public class MyViewModel extends ViewModel {
private final MyRepo myrepo;
public MyViewModel(MyRepo myrepo) {
this.myrepo = myrepo;
}
}
Pour instancier ce ViewModel, j'ai besoin d'avoir une usine que ViewModelProviders peut utiliser pour créer son instance.
L'utilitaire ViewModelProviders ne peut pas créer d'instance d'un ViewModel avec un constructeur d'arguments car il ne sait pas comment et quels objets passer dans le constructeur.
Nous en avons discuté, mais nous ne connaissons pas la raison de la création d'une fabrique de viewmodels pour créer un viewmodel au lieu d'instancier directement le viewmodel. Quel est le gain de créer une usine qui ne fait que créer le modèle de vue?
Parce que Android ne vous donnera une nouvelle instance que si elle n'est pas encore créée pour ce LifecycleOwner donné.
N'oublions pas non plus que les ViewModels sont maintenus en vie malgré les changements de configuration, donc si vous faites pivoter le téléphone, vous n'êtes pas censé créer un nouveau ViewModel.
Si vous revenez à une activité précédente et que vous rouvrez cette activité, le ViewModel précédent devrait recevoir onCleared()
et la nouvelle activité devrait avoir un nouveau ViewModel.
À moins que vous ne le fassiez vous-même, vous devriez probablement faire confiance au ViewModelProviders.Factory
pour faire son travail.
(Et vous avez besoin de l'usine parce que vous n'avez généralement pas seulement un no-arg
constructeur, votre ViewModel a des arguments constructeur, et le ViewModelProvider
doit savoir comment remplir les arguments constructeur lorsque vous utilisez un constructeur non par défaut).
En bref,
si nous devons passer certains input data
au constructor
du viewModel
, nous devons créer un factory class
pour viewModel.
Comme exemple: -
class MyViewModelFactory constructor(private val repository: DataRepository): ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return if (modelClass.isAssignableFrom(MyViewModel::class.Java!!)) {
MyViewModel(this.repository) as T
} else {
throw IllegalArgumentException("ViewModel Not Found")
}
}
}
Raison
Nous ne pouvons pas créer directement l'objet du ViewModel car il ne serait pas être au courant du lifecyclerOwner
. Nous utilisons donc: -
ViewModelProviders.of(this, MyViewModelFactory(repository)).get(MyViewModel::class.Java)