web-dev-qa-db-fra.com

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

Récemment, j'explore Android Architecture, qui a été introduite récemment par Google. De la Documentation j'ai trouvé ceci:

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, je vais faire ceci:

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

  2. Si je ne reçois pas les données, je vais effectuer un appel API pour récupérer les données sur le 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 des données.

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

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

68
CodeCameo

Je suppose que vous utilisez composants d'architecture Android . En fait, peu importe où vous appelez service, asynctask or handler pour mettre à jour les données. Vous pouvez insérer les données du service ou de l'asynctask à l'aide de la méthode postValue(..). Votre classe ressemblerait à ceci:

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

Comme la users est LiveData, la base de données Room est chargée de fournir les données des utilisateurs où qu'elles soient insérées.

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.

68

Veuillez utiliser la méthode postValue (..) de Live data à partir du thread d'arrière-plan.

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

... 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 mettre à jour la variable MutableLiveData> users à partir de ce service?

Si l'application récupère les données utilisateur sur un thread d'arrière-plan, postValue (plutôt que setValue ) sera utile.

Dans la méthode loadData, il existe une référence à l'objet "utilisateurs" de MutableLiveData. La méthode loadData extrait également de nouvelles données utilisateur (par exemple, un référentiel).

Désormais, si l'exécution se fait sur un thread en arrière-plan, MutableLiveData.postValue () est mis à jour en dehors des observateurs 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);        
        }
    });

}
15
albert c braun

Jetez un coup d’œil au guide d’architecture Android qui accompagne les nouveaux modules d’architecture tels que 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 au bas incluent des exemples plus complets, notamment l’état du réseau de communication, les erreurs de rapport, etc.

4
user1978019

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

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

enter image description here

2
Prakash

Si vous appelez votre api dans le référentiel alors,

Dans Repository:

public MutableLiveData<LoginResponseModel> checkLogin(LoginRequestModel loginRequestModel) {
    final MutableLiveData<LoginResponseModel> data = new MutableLiveData<>();
    apiService.checkLogin(loginRequestModel)
            .enqueue(new Callback<LoginResponseModel>() {
                @Override
                public void onResponse(@NonNull Call<LoginResponseModel> call, @Nullable Response<LoginResponseModel> response) {
                    if (response != null && response.isSuccessful()) {
                        data.postValue(response.body());
                        Log.i("Response ", response.body().getMessage());
                    }
                }

                @Override
                public void onFailure(@NonNull Call<LoginResponseModel> call, Throwable t) {
                    data.postValue(null);
                }
            });
    return data;
}

Dans ViewModel

public LiveData<LoginResponseModel> getUser() {
    loginResponseModelMutableLiveData = repository.checkLogin(loginRequestModel);
    return loginResponseModelMutableLiveData;
}

Dans Activité/Fragment

loginViewModel.getUser().observe(LoginActivity.this, loginResponseModel -> {
        if (loginResponseModel != null) {
            Toast.makeText(LoginActivity.this, loginResponseModel.getUser().getType(), Toast.LENGTH_SHORT).show();
        }
    });

Remarque: En utilisant Java_1.8 lambda ici, vous pouvez utiliser sans elle

1
Shubham Agrawal