web-dev-qa-db-fra.com

Meilleure pratique: filtres d'exécution avec Room et LiveData

Je travaille sur un écran qui affiche le contenu d'une base de données enveloppée d'une pièce à l'aide d'un recycleur. L'adaptateur obtient les données LiveData d'un ViewModel qui masque l'appel de requête sur l'objet Room DAO. Ainsi, l'objet LiveData est en réalité un objet ComputableLiveData qui est informé des modifications apportées à la base de données de la pièce.

Maintenant, je veux ajouter des options de filtrage à l'écran. Où et comment pourrais-je implémenter cela dans cette configuration de Room-LiveData-ViewModel?

Est-ce que l'adaptateur ou ViewModel "postfilter" les résultats dans LiveData? Devrais-je extraire les données de la pièce à chaque changement de filtre? Puis-je réutiliser les données LiveData (calculables) sous-jacentes pour cela? Si non, devrais-je vraiment créer de nouveaux LiveData pour chaque changement de filtre?

Une question similaire est abordée ici: Reload RecyclerView après modification des données avec Room, ViewModel et LiveData

7
Oderik

Alors, j'ai fini par le faire comme ça:

  • Le fragment transmet l'état du filtre au ViewModel. Effet secondaire: l’état du filtre peut être utilisé par plusieurs instances de fragment (c’est-à-dire en raison de changements de configuration). Peut-être que vous voulez ça, peut-être pas. Je fais.
  • ViewModel contient une instance de MediatorLiveData. Son source est unique: l’objet LiveData Room DB. La source prévient simplement des modifications apportées au médiateur. Si le filtre est modifié par le fragment, la source est remplacée par une interrogation.

Répondre à mes questions détaillées:

  • Pas de post-filtrage
  • Oui, réessayer lors du changement de filtre
  • Je ne réutilise pas ComputableLiveData (je ne suis pas sûr que ce soit possible)

En ce qui concerne la discussion dans les commentaires:

  • Je n'applique pas la pagination

Note finale sur Room: Est-ce que je me trompe ou dois-je écrire des méthodes DAO distinctes pour chaque combinaison de filtres que je souhaite appliquer? Ok, je pourrais insérer des parties facultatives de l'instruction select via une chaîne, mais je perdrais alors les avantages de Room. Une sorte de générateur d’instruction qui rend les instructions composables serait bien.

EDIT: S'il vous plaît noter le commentaire de Ridcully ci-dessous. Il mentionne SupportSQLiteQueryBuilder avec @RawQuery pour répondre à la dernière partie, je suppose. Cependant, je n'ai pas encore vérifié.

Merci à CommonsWare et pskink pour votre aide!

2
Oderik

Je travaille dans un problème similaire. Au départ, j'avais RxJava mais maintenant je le convertis en LiveData.

Voici comment je vais dans mon ViewModel:

// Inside ViewModel
MutableLiveData<FilterState> modelFilter = new MutableLiveData<>();
LiveData<PagedList<Model>> modelLiveData;

Ce modelLivedata est construit de la manière suivante dans le constructeur du modèle de vue:

        // In ViewModel constructor
        modelLiveData = Transformations.switchMap(modelFilter,
                    new Android.Arch.core.util.Function<FilterState, LiveData<PagedList<Model>>>() {
                        @Override
                        public LiveData<PagedList<Model>> apply(FilterState filterState) {
                            return modelRepository.getModelLiveData(getQueryFromFilter(filterState));
                        }
                    });

Lorsque le modèle de vue reçoit un autre filtre à appliquer, il:

// In ViewModel. This method receives the filtering data and sets the modelFilter 
// mutablelivedata with this new filter. This will be "transformed" in new modelLiveData value.
public void filterModel(FilterState filterState) {

    modelFilter.postValue(filterState);
}

Ensuite, ce nouveau filtre sera "transformé" en une nouvelle valeur de liveata qui sera envoyée à l'observateur (un fragment).

Le fragment obtient les éléments de données à observer via un appel dans le modèle de vue:

// In ViewModel
public LiveData<PagedList<Model>> getModelLiveData() {

    return modelLiveData;

}

Et à l'intérieur de mon fragment j'ai:

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    ViewModel viewModel = ViewModelProviders.of(this.getActivity()).get(ViewModel.class);

    viewModel.getModelLiveData().observe(this.getViewLifecycleOwner(), new Observer<PagedList<Model>>() {
        @Override
        public void onChanged(@Nullable PagedList<Model> model) {
            modelListViewAdapter.submitList(model);
        }
    });

}

J'espère que ça aide.

7
Francisco Junior