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>>() {
Il y a 3 problèmes dans votre code.
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;
}
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());
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.).
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