115 votes

Comment mettre à jour les LiveData d'un ViewModel à partir d'un service d'arrière-plan et d'une interface utilisateur de mise à jour ?

Récemment, j'ai exploré l'architecture Android, qui a été introduite récemment par Google. Depuis le Documentation J'ai trouvé ça :

public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<Users>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // do async operation to fetch users
    }
}

l'activité peut accéder à cette liste comme suit :

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

Ma question est la suivante : je vais le faire :

  1. dans le loadUsers() Je récupère les données de manière asynchrone et je vérifie d'abord la base de données (Room) pour ces données.

  2. Si je n'obtiens pas les données à cet endroit, je ferai un appel à l'API pour récupérer les données du serveur web.

  3. Je vais insérer les données récupérées dans la base de données (Room) et mettre à jour l'interface utilisateur en fonction de ces données.

Quelle est l'approche recommandée pour ce faire ?

Si je commence un Service pour appeler l'API à partir du loadUsers() comment puis-je mettre à jour la méthode MutableLiveData<List<User>> users de cette variable Service ?

11 votes

Tout d'abord, il vous manque un Repository. Votre ViewModel ne devrait pas faire de tâches de chargement de données. De plus, puisque vous utilisez Room, votre service n'a pas besoin de mettre à jour directement les LiveData dans le ViewModel. Le service peut se contenter d'insérer des données dans Room, tandis que votre ViewModelData doit être attaché uniquement à Room, et recevoir des mises à jour de Room (après que le service ait inséré des données). Mais pour obtenir la meilleure architecture possible, regardez l'implémentation de la classe NetworkBoundResource au bas de cette page : developer.Android.com/topic/bibliothèques/architecture/guide.html

0 votes

merci pour la suggestion :)

1 votes

La classe Repositor n'est pas mentionnée dans les documents offocials décrivant ROOM ou les composants de l'architecture Android.

103voto

Sharif Points 1387

Je suppose que vous utilisez Composants de l'architecture Android . En fait, l'endroit où vous appelez n'a aucune importance. service, asynctask or handler pour mettre à jour les données. Vous pouvez insérer les données à partir du service ou de la tâche asynchrone en utilisant la méthode suivante postValue(..) méthode. Votre classe ressemblerait à ceci :

private void loadUsers() {
    // do async operation to fetch users and use postValue() method
    users.postValue(listOfData)
}

Comme le users est LiveData , Room La base de données est chargée de fournir aux utilisateurs des données partout où elle est insérée.

Note: Dans une architecture de type MVVM, le référentiel est principalement responsable de la vérification et de l'extraction des données locales et des données distantes.

3 votes

J'obtiens "java.lang.IllegalStateException : Cannot access database on the main thread since it" lors de l'appel de mes méthodes db comme ci-dessus. Pouvez-vous me dire ce qui ne va pas ?

0 votes

J'utilise evernote-job qui met à jour la base de données en arrière-plan pendant que je suis sur l'interface utilisateur. Mais LiveData ne met pas à jour

0 votes

users.postValue(mUsers); -> Mais la méthode postValue de MutableLiveData est-elle capable d'accepter des LiveData ?

49voto

minhazur Points 3466

Vous pouvez utiliser MutableLiveData<T>.postValue(T value) à partir du fil d'arrière-plan.

private void loadUsers() {
    // do async operation to fetch users and use postValue() method
   users.postValue(listOfData)
}

21 votes

Pour rappel, postValue() est protégé dans LiveData mais public dans MutableLiveData.

1 votes

ce travail même à partir d'une tâche de fond et dans des situations où .setValue() ne serait pas autorisé

20voto

albert c braun Points 1593

... dans la fonction loadUsers(), je récupère les données de manière asynchrone ... Si je lance un Service pour appeler l'API à partir de la méthode loadUsers(), comment puis-je puis-je mettre à jour la variable MutableLiveData> users à partir de ce service ? service ?

Si l'application récupère des données utilisateur sur un fil d'arrière-plan, postValue (plutôt que setValue ) seront utiles.

Dans la méthode loadData, il y a une référence à l'objet MutableLiveData "users". La méthode loadData récupère également des données d'utilisateur fraîches depuis un endroit quelconque (par exemple, un référentiel).

Maintenant, si l'exécution se fait sur un thread d'arrière-plan, MutableLiveData.postValue() met à jour les observateurs extérieurs de l'objet MutableLiveData.

Peut-être quelque chose comme ça :

private MutableLiveData<List<User>> users;

.
.
.

private void loadUsers() {
    // do async operation to fetch users
    ExecutorService service =  Executors.newSingleThreadExecutor();
    service.submit(new Runnable() {
        @Override
        public void run() {
            // on background thread, obtain a fresh list of users
            List<String> freshUserList = aRepositorySomewhere.getUsers();

            // now that you have the fresh user data in freshUserList, 
            // make it available to outside observers of the "users" 
            // MutableLiveData object
            users.postValue(freshUserList);        
        }
    });

}

0 votes

du référentiel getUsers() peut appeler un api pour les données qui est asynchrone (peut démarrer un service ou une tâche asynchrone pour cela) dans ce cas, comment peut-elle retourner la liste à partir de son instruction de retour ?

0 votes

Peut-être pourrait-il prendre l'objet LiveData comme argument. (Quelque chose comme repository.getUsers(users)). Ensuite, la méthode repository appellerait users.postValue elle-même. Et, dans ce cas, la méthode loadUsers n'aurait même pas besoin d'un thread d'arrière-plan.

1 votes

Merci pour la réponse ..... Cependant, j'utilise une base de données Room pour le stockage et le DAO renvoie un LiveData<Object> ... comment puis-je convertir le LiveData en un MutableLiveData<Object> ?

4voto

user1978019 Points 222

Jetez un coup d'œil à la Guide de l'architecture Android qui accompagne les nouveaux modules d'architecture comme LiveData et ViewModel . Ils discutent de cette question en profondeur.

Dans leurs exemples, ils ne le mettent pas dans un service. Regardez comment ils le résolvent en utilisant un module "référentiel" et Retrofit. Les addendums en bas de page comprennent des exemples plus complets, notamment la communication de l'état du réseau, le signalement des erreurs, etc.

0voto

nag prakash Points 145

https://developer.Android.com/topic/libraries/architecture/guide.html

Il a de très bonnes réponses à votre problème. Faites défiler jusqu'en bas pour voir comment vous pouvez enregistrer la réponse dans votre base de données et mettre à jour l'interface utilisateur à l'aide de LiveData.

enter image description here

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X