Warum wird in Android eine Viewmodel-Factory benötigt?

Lesezeit: 7 Minuten

Warum wird in Android eine Viewmodel Factory benotigt
Iban Arriola

Wir haben darüber diskutiert, kennen aber den Grund für die Erstellung einer Ansichtsmodell-Factory nicht, um ein Ansichtsmodell zu erstellen, anstatt das Ansichtsmodell direkt zu instanziieren. Was bringt es, eine Factory zu erstellen, die nur das Ansichtsmodell erstellt?

Ich habe nur ein einfaches Beispiel dafür gegeben, wie ich es ohne Factory gemacht habe

Hier ist das Codein-Modul:

val heroesRepositoryModel = Kodein {
    bind<HeroesRepository>() with singleton {
        HeroesRepository()
    }

    bind<ApiDataSource>() with singleton {
        DataModule.create()
    }

    bind<MainViewModel>() with provider {
        MainViewModel()
    }
}

Der Teil der Aktivität, in dem ich das Ansichtsmodell instanziiere, ohne die Factory zu verwenden

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()
    }

Das ViewModel, in dem ich den Anwendungsfall direkt instanziiere, ohne ihn im Konstruktor zu haben

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()
    }
}

Hier also ein Beispiel mit Factory

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)
    }

Die Ansichtsmodellfabrik:

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")
    }
}

Und das Ansichtsmodell:

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()
    }

Hier ist das vollständige erste Beispiel:

https://github.com/ibanarriolaIT/Marvel/tree/mvvm

Und das komplette zweite Beispiel:

https://github.com/AdrianMeizoso/Payment-App

  • sehen Das und Das – Fabriken werden sowieso verwendet

    – Pskink

    29. Januar 2019 um 11:04 Uhr


  • Warum also selbst erstellen, wenn es sowieso verwendet wird?

    – Iban Arriola

    29. Januar 2019 um 11:16 Uhr

Wir können ViewModel nicht selbst erstellen. Wir benötigen das von Android bereitgestellte ViewModelProviders-Dienstprogramm, um ViewModels zu erstellen.

Aber ViewModelProviders können ViewModels nur ohne arg-Konstruktor instanziieren.

Wenn ich also ein ViewModel mit mehreren Argumenten habe, muss ich eine Factory verwenden, die ich an ViewModelProviders übergeben kann, um sie zu verwenden, wenn eine Instanz von MyViewModel erforderlich ist.

Zum Beispiel –

public class MyViewModel extends ViewModel {
    private final MyRepo myrepo;
    public MyViewModel(MyRepo myrepo) {
         this.myrepo = myrepo;
    }
}

Um dieses ViewModel zu instanziieren, benötige ich eine Factory, mit der ViewModelProviders seine Instanz erstellen kann.

ViewModelProviders Utility kann keine Instanz eines ViewModel mit Argumentkonstruktor erstellen, da es nicht weiß, wie und welche Objekte an den Konstruktor übergeben werden sollen.

  • Aber ich instanziiere das Repository im Viewmodel selbst, sodass der Konstruktor keine Parameter hat. Dann stellt sich hier die Frage, ob es einen Vorteil gibt, das Repository in der Aktivität zu instanziieren, die Factory zu erstellen, um das Repository an das Ansichtsmodell zu übergeben, oder einfach das Repository im Ansichtsmodell zu instanziieren und ViewModelProviders zu verwenden, ohne eine eigene Factory zu erstellen.

    – Iban Arriola

    29. Januar 2019 um 11:49 Uhr

  • I am instantiating the repository in the viewmodel itself dann verstoßen Sie gegen DI-Prinzipien und ich frage mich, warum Sie Kodein verwenden.

    – EpicPandaForce

    29. Januar 2019 um 11:51 Uhr

  • Für die Abhängigkeitsumkehr sollte eine Klasse nicht die Abhängigkeiten erstellen, die sie benötigt. Diese Abhängigkeiten sollten ihm zugeführt werden. Hier haben wir ein Beispiel für eine Konstruktorinjektion, bei der wir unser Repo über den Objektkonstruktor injizieren.

    – Vishal Arora

    29. Januar 2019 um 11:51 Uhr

  • Lesen Sie dies für mehr Referenz und Klarheit – en.wikipedia.org/wiki/Dependency_inversion_principle

    – Vishal Arora

    29. Januar 2019 um 11:53 Uhr

  • “ViewModelProviders Utility kann keine Instanz eines ViewModel mit Argumentkonstruktor erstellen, da es nicht weiß, wie und welche Objekte an den Konstruktor übergeben werden sollen.” Können Sie das erklären? Warum ist ViewModelProviders speziell so entworfen, dass nur Fabriken und keine einfachen Konstruktoren für die ViewModel-Erstellung verwendet werden?

    – getsadzeg

    12. Mai 2019 um 11:51 Uhr

Warum wird in Android eine Viewmodel Factory benotigt
EpicPandaForce

Wir haben darüber diskutiert, kennen aber den Grund für die Erstellung einer Ansichtsmodell-Factory nicht, um ein Ansichtsmodell zu erstellen, anstatt das Ansichtsmodell direkt zu instanziieren. Was bringt es, eine Factory zu erstellen, die nur das Ansichtsmodell erstellt?

Denn Android gibt Ihnen nur dann eine neue Instanz, wenn diese noch nicht erstellt ist für diesen bestimmten gegebenen ViewModelStoreOwner.

Vergessen wir auch nicht, dass ViewModels über Konfigurationsänderungen hinweg am Leben erhalten werden. Wenn Sie also das Telefon drehen, sollten Sie kein neues ViewModel erstellen.

Wenn Sie zu einer vorherigen Aktivität zurückkehren und diese Aktivität erneut öffnen, sollte das vorherige ViewModel empfangen werden onCleared() und die neue Aktivität sollte ein neues ViewModel haben.

Wenn Sie das nicht selbst tun, sollten Sie dem wahrscheinlich einfach vertrauen ViewModelProviders.Factory um seinen Job zu machen.

(Und Sie brauchen die Fabrik, weil Sie normalerweise nicht nur eine haben no-arg Konstruktor, Ihr ViewModel hat Konstruktorargumente und die ViewModelProvider müssen wissen, wie die Konstruktorargumente ausgefüllt werden, wenn Sie einen nicht standardmäßigen Konstruktor verwenden).

  • Die Sache ist, dass ich das benötigte Repository in das Ansichtsmodell einfüge, sodass ich keinen Konstruktor mit Argumenten erstellen muss, und es respektiert den Lebenszyklus, da es nicht jedes Mal erneut geladen wird, wenn ich das Telefon drehe. Es funktioniert also so, als ob ich eine Fabrik hätte, aber ohne sie erstellen zu müssen.

    – Iban Arriola

    29. Januar 2019 um 11:55 Uhr

  • Das ist keine Injektion, Sie suchen nach einer globalen Variablen. Sie sollten das Konstruktorargument verwenden. Im Falle von Kodein würde ich denken, dass Sie haben sollten provider { MainViewModel(instance()) } und rufen Sie dann den Anbieter innerhalb der auf ViewModelProviders.Factory.

    – EpicPandaForce

    29. Januar 2019 um 12:26 Uhr


  • Dies ist die einzige Antwort, die hervorhebt, wie wichtig es ist, den “Anfangszustand” nur einmal und nicht bei jeder Drehung des Geräts einzustellen. Aus diesem Grund ist es wichtig, eine kundenspezifische Fabrik zu schaffen.

    – Richard Le Mesurier

    21. April 2021 um 13:56 Uhr

Android Studio Gradle Synchronisierung fehlgeschlagen HEAD konnte nicht ausgefuhrt werden Statuscode
Santanu Sur

Zusamenfassend,

wenn wir müssen passieren etwas input data zum constructor des viewModel müssen wir eine erstellen factory class für viewModel.

Wie Beispiel :-

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")
        }
    }
}

Grund

Wir können das Objekt von nicht direkt erstellen ViewModel als es würde nicht sich dessen bewusst sein lifecyclerOwner. Also verwenden wir: –

ViewModelProviders.of(this, MyViewModelFactory(repository)).get(MyViewModel::class.java)

Wenn wir einfach verwenden ViewModelkönnen wir keine Argumente an dieses ViewModel übergeben

class GameViewModel() : ViewModel() {

    init {
        Log.d(TAG, "GameViewModel created")
    }
}

In einigen Fällen müssen wir jedoch unsere eigenen Argumente an ViewModel übergeben. Dies kann mit erfolgen ViewModelFactory.

class ScoreViewModel(finalScore: Int) : ViewModel() {

    val score = finalScore

    init {
        Log.d(TAG, "Final score: $finalScore")
    }
}

Und um dieses ViewModel zu instanziieren, brauchen wir a ViewModelProvider.Factory da einfaches ViewModel es nicht instanziieren kann.

class ScoreViewModelFactory(private val finalScore: Int) : ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(ScoreViewModel::class.java)) {
            return ScoreViewModel(finalScore) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

Wenn es darum geht, ein Objekt dieses ViewModel zu instanziieren, dh mit ViewModelProvider, übergeben wir ViewModelFactory als Argument, das Informationen über unsere benutzerdefinierten Argumente enthält, die wir übergeben möchten. Es geht so:

viewModelFactory = ScoreViewModelFactory(score)
viewModel = ViewModelProvider(this,viewModelFactory).get(ScoreViewModel::class.java)

Deshalb gibt es Fabrikmethoden.

1005390cookie-checkWarum wird in Android eine Viewmodel-Factory benötigt?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy