Reputation: 222
Im trying to retrive json with retrofit and gson from the API http://api.themoviedb.org/3/movie/. Im my main activity it is working fine, but when i call some additional information in my DetailsActivity i figure out that the models in my array are emty. This is weird because i am recieving even the right number of objects in the array from the api. But when i Log a value from the model (for example Log.d(TAG, movieTrailers.get(0).getName()) the app crashes. Whit an if statement i figure out that this value is null.
This is the API http://api.themoviedb.org/3/movie/297802/videos?api_key= for example. (Unfortunatly i am not allowed to post the api key) It contains in a jsonarray of videos about the movie.
This is my Code:
The Model:
public class MovieTrailer {
@SerializedName("key")
private String key;
@SerializedName("name")
private String name;
@SerializedName("type")
private String type;
public MovieTrailer(String key, String name, String type){
this.key = key;
this.name = name;
this.type = type;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
The Model for the List:
public class TrailersList {
@SerializedName("results")
@Expose
private List<MovieTrailer> trailers = null;
public List<MovieTrailer> getTrailers() {
return trailers;
}
public void setTrailers(List<MovieTrailer> trailers) {
this.trailers = trailers;
}
}
The Adapter:
public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.VideoAdapterViewHolder> {
private List<MovieTrailer> movieTrailers;
private final VideoOnClickHandler videoOnClickHandler;
public interface VideoOnClickHandler{
void onClick(MovieTrailer movieTrailer);
}
public VideoAdapter(VideoOnClickHandler onClickHandler) {videoOnClickHandler = onClickHandler;}
public class VideoAdapterViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public final TextView videoTypeText;
public final TextView videoNameText;
public VideoAdapterViewHolder(View view){
super(view);
videoTypeText = (TextView) view.findViewById(R.id.tv_video_type);
videoNameText = (TextView) view.findViewById(R.id.tv_video_name);
view.setOnClickListener(this);
}
@Override
public void onClick(View view) {
int adapterPosition = getAdapterPosition();
MovieTrailer movieTrailer = movieTrailers.get(adapterPosition);
videoOnClickHandler.onClick(movieTrailer);
}
}
@NonNull
@Override
public VideoAdapterViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
Context context = viewGroup.getContext();
int gridItem = R.layout.movie_trailer;
LayoutInflater inflater = LayoutInflater.from(context);
boolean shouldAttachToParentImmediately = false;
View view = inflater.inflate(gridItem, viewGroup,shouldAttachToParentImmediately);
return new VideoAdapter.VideoAdapterViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull VideoAdapterViewHolder videoAdapterViewHolder, int i) {
MovieTrailer movieTrailer = movieTrailers.get(i);
videoAdapterViewHolder.videoTypeText.setText(movieTrailer.getType());
videoAdapterViewHolder.videoNameText.setText(movieTrailer.getName());
}
@Override
public int getItemCount() {
if (null == movieTrailers) return 0;
return movieTrailers.size();
}
//Function to set movieTrailers
public void setMovieTrailerArray(List<MovieTrailer> trailerArrayToSet){
movieTrailers = trailerArrayToSet;
notifyDataSetChanged();
}
}
The Interface:
public interface GetDataService {
@GET("{path}?api_key=" + BuildConfig.API_KEY)
Call<MoviesList> getAllMovies(@Path("path") String path);
@GET("{movieId}/reviews?api_key=" + BuildConfig.API_KEY)
Call<TrailersList> getAllTrailers(@Path("movieId") String movieId);
@GET("{movieId}/videos?api_key=" + BuildConfig.API_KEY)
Call<ReviewsList> getAllReviews(@Path("movieId") String movieId);
}
The Retrofit Instance:
public class RetrofitClientInstance {
//Base URL for API request
private static final String MOVIE_DATABASE_URL_POPULAR =
"http://api.themoviedb.org/3/movie/";
/**
* Get Retrofit Instance
*/
private static Retrofit getRetrofitInstance() {
return new Retrofit.Builder()
.baseUrl(MOVIE_DATABASE_URL_POPULAR)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
/**
* Get API Service
*
* @return API Service
*/
public static GetDataService getApiService() {
return getRetrofitInstance().create(GetDataService.class);
}
}
And the method from the DetailsActivity:
private void loadMovieTrailers(String movieId){
GetDataService api = RetrofitClientInstance.getApiService();
Call<TrailersList> call = api.getAllTrailers(movieId);
call.enqueue(new Callback<TrailersList>() {
@Override
public void onResponse(Call<TrailersList> call, Response<TrailersList> response) {
if(response.isSuccessful()){
movieTrailers = response.body().getTrailers();
if(movieTrailers.get(0).getName() == null){
Log.d("MODEL", "Null");
}else {
Log.d("MODEL", "Not Null");
}
videoAdapter.setMovieTrailerArray(movieTrailers);
}
}
@Override
public void onFailure(Call<TrailersList> call, Throwable t) {
}
});
}
Someone has an idea where the mistake is?
Upvotes: 4
Views: 1244
Reputation: 4442
In my case, my debug apk was working fine, but when release apk was created, all data of model was showing empty except the data was annotated with Expose
and SerializedName
. Although my key and variable name was same, but I still had to add SerializedName
, I am not sure why.
So, if you haven't added those, please add these two annotation.
In short convert private int id;
to this @Expose @SerializedName("id") private int id;
Upvotes: 0
Reputation: 23665
In your MovieTrailer class, the @Expose
annotations are missing for the fields. Thus, Gson creates the objects, but doesn't find any fields to put the actual data in.
Btw. The @SerializedFieldName is only needed if the field name in the JSON is different than the field name in your model.
Upvotes: 1
Reputation: 5190
I think the problem is in your interface class with these methods,
@GET("{movieId}/reviews?api_key=" + BuildConfig.API_KEY)
Call<TrailersList> getAllTrailers(@Path("movieId") String movieId);
@GET("{movieId}/videos?api_key=" + BuildConfig.API_KEY)
Call<ReviewsList> getAllReviews(@Path("movieId") String movieId);
From {movieId}/reviews
you'll get the following JSON response,
{
"id": 297761,
"page": 1,
"results": [
{
"id": "57a814dc9251415cfb00309a",
"author": "Frank Ochieng",
"content": "Summertime 2016 has not been very kind to DC Comics-based personalities looking to shine consistently like their big screen Marvel Comics counterparts.",
"url": "https://www.themoviedb.org/review/57a814dc9251415cfb00309a"
}
],
"total_pages": 1,
"total_results": 1
}
and {movieId}/videos
returns,
{
"id": 550,
"results": [
{
"id": "533ec654c3a36854480003eb",
"iso_639_1": "en",
"iso_3166_1": "US",
"key": "SUXWAEX2jlg",
"name": "Trailer 1",
"site": "YouTube",
"size": 720,
"type": "Trailer"
}
]
}
So, both of them returns almost same result with JSONArray (results
) enclosed withing JSONObject. JSON response is proper there's no problem at all.
While parsing JSON response, Gson converter factory fails to find the equivalent POJO class for the response. You don't even get any errors because both are returning same type response.
A simple fix is to change (inter change) the URLs of the following methods.
@GET("{movieId}/videos?api_key=" + BuildConfig.API_KEY) // change from reviews to videos
Call<TrailersList> getAllTrailers(@Path("movieId") String movieId);
@GET("{movieId}/reviews?api_key=" + BuildConfig.API_KEY) // change from videos to reviews
Call<ReviewsList> getAllReviews(@Path("movieId") String movieId);
That's it!
Upvotes: 0