web-dev-qa-db-fra.com

Comment effectuer un appel API de modification à l'aide de ViewModel et LiveData

c'est la première fois que j'essaie d'implémenter une architecture MVVM et je suis un peu perplexe quant à la manière correcte de passer un appel API.

Actuellement, j'essaie simplement de faire une requête simple à partir de l'API IGDB et de générer le nom du premier élément dans un journal.

Mon activité est configurée comme suit:

public class PopularGamesActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_popular_games);


        PopularGamesViewModel popViewModel = ViewModelProviders.of(this).get(PopularGamesViewModel.class);
        popViewModel.getGameList().observe(this, new Observer<List<Game>>() {
            @Override
            public void onChanged(@Nullable List<Game> gameList) {
                String firstName = gameList.get(0).getName();
                Timber.d(firstName);
            }
        });
    }
}

Mon modèle de vue est configuré comme suit:

public class PopularGamesViewModel extends AndroidViewModel {

    private static final String igdbBaseUrl = "https://api-endpoint.igdb.com/";
    private static final String FIELDS = "id,name,genres,cover,popularity";
    private static final String ORDER = "popularity:desc";
    private static final int LIMIT = 30;

    private LiveData<List<Game>> mGameList;

    public PopularGamesViewModel(@NonNull Application application) {
        super(application);


        // Create the retrofit builder
        Retrofit.Builder builder = new Retrofit.Builder()
                .baseUrl(igdbBaseUrl)
                .addConverterFactory(GsonConverterFactory.create());

        // Build retrofit
        Retrofit retrofit = builder.build();

        // Create the retrofit client
        RetrofitClient client = retrofit.create(RetrofitClient.class);
        Call<LiveData<List<Game>>> call = client.getGame(FIELDS,
                ORDER,
                LIMIT);

        call.enqueue(new Callback<LiveData<List<Game>>>() {
            @Override
            public void onResponse(Call<LiveData<List<Game>>> call, Response<LiveData<List<Game>>> response) {
                if (response.body() != null) {
                    Timber.d("Call response body not null");
                    mGameList = response.body();

                } else {
                    Timber.d("Call response body is null");
                }
            }

            @Override
            public void onFailure(Call<LiveData<List<Game>>> call, Throwable t) {
                Timber.d("Retrofit call failed");
            }
        });

    }

    public LiveData<List<Game>> getGameList() {
        return mGameList;
    }

Le problème vient maintenant du fait qu’il s’agit d’un appel d’API, la valeur initiale de mGameList sera nulle, jusqu’à ce que call.enqueue retourne avec une valeur. Cela provoquera une exception de pointeur null avec

popViewModel.getGameList().observe(this, new Observer<List<Game>>() {
  1. Alors, quelle est la bonne façon de gérer l'observation d'un LiveData pendant l'appel de l'API?
  2. Ai-je effectué l'appel API Retrofit au bon endroit?
16
Chao Li

Il y a 3 problèmes dans votre code.

  1. Vous devez créer un objet MutableLiveData car vous avez une réponse vide avant l'appel de l'API, votre objet LiveData sera rempli d'une manière ou d'une autre par le biais de la réponse IGDB.
private MutableLiveData<List<Game>> mGameList = new MutableLiveData();
//...
public LiveData<List<Game>> getGameList() {
    return mGameList;
}
  1. Une autre erreur est de changer la référence de mGameList au lieu de définir sa valeur, essayez donc de changer:
Timber.d("Call response body not null");
mGameList = response.body();

à

mGameList.setValue(response.body());
  1. Appeler la modification dans votre classe ViewModel revient à éviter la séparation des problèmes. Il est préférable de créer un module de référentiel et d'obtenir votre réponse via une interface. Lisez ceci article pour plus de détails.

Les modules de référentiel sont responsables du traitement des opérations de données. Ils fournissent une API propre au reste de l'application. Ils savent où obtenir les données et quelle API appeler lors de la mise à jour des données. Vous pouvez les considérer comme des médiateurs entre différentes sources de données (modèle persistant, service Web, cache, etc.).

15
Saeed Masoumi

Je viens de m'inspirer de la démo de Google et de créer une bibliothèque pouvant ajouter le support LiveData pour Retrofit. L'utilisation est simple:

Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(LiveDataCallAdapterFactory())
            .build()
            .create(GithubService::class.Java)
            .getUser("shawnlinboy").observe(this,
                Observer { response ->
                    when (response) {
                        is ApiSuccessResponse -> {
                          //success response
                        }
                        else -> {
                            //failed response
                        }
                    }
                })

Le site de la bibliothèque: https://github.com/shawnlinboy/retrofit-livedata-adapter

0
Shawn Lin