改造库提供空Json响应

时间:2017-04-23 17:25:09

标签: java android json nullpointerexception retrofit2

我正在使用Retrofit库来解析现在播放电影类别的TMDB API的Json响应,并希望在RecyclerView中显示结果。

我从API获得的原始Json是

{
  "page": 1,
  "results": [
    {
      "poster_path": "\/tWqifoYuwLETmmasnGHO7xBjEtt.jpg",
      "adult": false,
      "overview": "A live-action adaptation of Disney's version of the classic 'Beauty and the Beast' tale of a cursed prince and a beautiful young woman who helps him break the spell.",
      "release_date": "2017-03-16",
      "genre_ids": [
        14,
        10749
      ],
      "id": 321612,
      "original_title": "Beauty and the Beast",
      "original_language": "en",
      "title": "Beauty and the Beast",
      "backdrop_path": "\/6aUWe0GSl69wMTSWWexsorMIvwU.jpg",
      "popularity": 147.254531,
      "vote_count": 1938,
      "video": false,
      "vote_average": 6.9
    },
    {
      "poster_path": "\/unPB1iyEeTBcKiLg8W083rlViFH.jpg",
      "adult": false,
      "overview": "A story about how a new baby's arrival impacts a family, told from the point of view of a delightfully unreliable narrator, a wildly imaginative 7 year old named Tim.",
      "release_date": "2017-03-23",
      "genre_ids": [
        16,
        35,
        10751
      ],
      "id": 295693,
      "original_title": "The Boss Baby",
      "original_language": "en",
      "title": "The Boss Baby",
      "backdrop_path": "\/bTFeSwh07oX99ofpDI4O2WkiFJ.jpg",
      "popularity": 138.856227,
      "vote_count": 649,
      "video": false,
      "vote_average": 5.7
    },

我创建的模型类是Movie.java

package com.example.vikas.movie.model;

/**
 * Created by vikas on 22/4/17
 */
import com.google.gson.annotations.SerializedName;

import java.util.ArrayList;
import java.util.List;
public class Movie
{
    @SerializedName("poster_path")
    private String posterPath;
    @SerializedName("adult")
    private boolean adult;
    @SerializedName("overview")
    private String overview;
    @SerializedName("realease_date")
    private String releaseDate;
    @SerializedName("genre_ids")
    private List<Integer> genreIds=new ArrayList<Integer>();
    @SerializedName("id")
    private int id;
    @SerializedName("original_title")
    private String originalTitle;
    @SerializedName("original_language")
    private String original_language;
    @SerializedName("title")
    private String title;
    @SerializedName("backdrop_path")
    private String backdrop_path;
    @SerializedName("popularity")
    private Double popularity;
    @SerializedName("vote_count")
    private int vote_count;
    @SerializedName("vedeo")
    private boolean vedeo;
    @SerializedName("vote_average")
    private Double vote_average;


    public Movie(String posterPath, boolean adult, String overview, String releaseDate, List<Integer> genreIds, Integer id,
                 String originalTitle, String original_language, String title, String backdrop_path, Double popularity,
                 Integer vote_count, Boolean vedeo, Double vote_average) {
        this.posterPath = posterPath;
        this.adult = adult;
        this.overview = overview;
        this.releaseDate = releaseDate;
        this.genreIds = genreIds;
        this.id = id;
        this.originalTitle = originalTitle;
        this.original_language = original_language;
        this.title = title;
        this.backdrop_path = backdrop_path;
        this.popularity = popularity;
        this.vote_count = vote_count;
        this.vedeo = vedeo;
        this.vote_average = vote_average;
    }

    public String getPosterPath() {
        return posterPath;
    }

    public void setPosterPath(String posterPath) {
        this.posterPath = posterPath;
    }

    public boolean isAdult() {
        return adult;
    }

    public void setAdult(boolean adult) {
        this.adult = adult;
    }

    public String getOverview() {
        return overview;
    }

    public void setOverview(String overview) {
        this.overview = overview;
    }

    public String getReleaseDate() {
        return releaseDate;
    }

    public void setReleaseDate(String releaseDate) {
        this.releaseDate = releaseDate;
    }

    public List<Integer> getGenreIds() {
        return genreIds;
    }

    public void setGenreIds(List<Integer> genreIds) {
        this.genreIds = genreIds;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getOriginalTitle() {
        return originalTitle;
    }

    public void setOriginalTitle(String originalTitle) {
        this.originalTitle = originalTitle;
    }

    public String getOriginalLanguage() {
        return original_language;
    }

    public void setOriginalLanguage(String original_language) {
        this.original_language = original_language;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getBackdropPath() {
        return backdrop_path;
    }

    public void setBackdropPath(String backdropPath) {
        this.backdrop_path = backdropPath;
    }

    public Double getPopularity() {
        return popularity;
    }

    public void setPopularity(Double popularity) {
        this.popularity = popularity;
    }

    public Integer getvote_count() {
        return vote_count;
    }

    public void setVoteCount(Integer voteCount) {
        this.vote_count = voteCount;
    }

    public Boolean getVedeo() {
        return vedeo;
    }

    public void setVedeo(Boolean vedeo) {
        this.vedeo = vedeo;
    }

    public Double getvote_average() {
        return vote_average;
    }

    public void setVoteAverage(Double vote_average) {
        this.vote_average = vote_average;
    }
}

MovieResponse.java

package com.example.vikas.movie.model;

/**
 * Created by vikas on 22/4/17.
 */
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class MoviesResponse
{
    @SerializedName("page")
    private int page;
    @SerializedName("results")
    private List<Movie>results;
    @SerializedName("total_results")
    private int totalResults;
    @SerializedName("total_pages")
    private int totalPages;
    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public List<Movie> getResults() {
        return results;
    }

    public void setResults(List<Movie> results) {
        this.results = results;
    }

    public int getTotalResults() {
        return totalResults;
    }

    public void setTotalResults(int totalResults) {
        this.totalResults = totalResults;
    }

    public int getTotalPages() {
        return totalPages;
    }

    public void setTotalPages(int totalPages) {
        this.totalPages = totalPages;
    }




}

现在进行如下API调用是我编写的用于生成URL和创建改造实例的类。我用这些类来生成URL并调用API。

ApiClient.java     包com.example.vikas.movi​​e.rest;

/**
 * Created by vikas on 22/4/17.
 */
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ApiClient
{
    public static final String URL="http://api.themoviedb.org/3/";
    private static Retrofit retrofit=null;

    public static Retrofit getClient()
    {
        if(retrofit==null)
        {
            retrofit= new Retrofit.Builder()//netsed class of Retrofit used to create a new instance of retrofit class
                    .baseUrl(URL)//loaded the base URL which will be used later for all api calls
                    .addConverterFactory(GsonConverterFactory.create())//added converter factory for
                    // serialization and desserialization of objects

                    .build();//created the retrofit instance with configured values

        }
        return retrofit;
    }

}

ApiInterface.java

package com.example.vikas.movie.rest;

/**
 * Created by vikas on 23/4/17.
 */
import com.example.vikas.movie.model.MoviesResponse;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.Query;
// to define endpoints of a URL we will use special retrofit annotations. we have used Get annotation in
// to get information from the API and the parameters os this method are @Query,@Path,
public interface ApiInterface
{
    @GET("movie/now-playing")
    Call<MoviesResponse> getNowPlayingMovies(@Query("api_key") String apikey);
    //return value is alwayays a parameterized call<T> oject whose paremeter is generic so we can have
    @GET("movie/{id}")
    Call<MoviesResponse> getMovieDetails(@Path("id") int id, @Query("api_key") String apiKey);
}


// so using this we will generate the following URL
//http://api.themoviedb.org/3/movie/now-playing?api_key=Xxxx

现在我在RecyclerView中显示结果,所以Dapter类就是

MovieAdapter.java

package com.example.vikas.movie.adapter;

/**
 * Created by vikas on 23/4/17.
 */
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.example.vikas.movie.R;
import com.example.vikas.movie.helper.CircleTransformation;
import com.example.vikas.movie.model.Movie;

import java.util.List;
public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MovieViewHolder> {

    private List<Movie> movies;
    private int rowLayout;
    private Context context;


    public static class MovieViewHolder extends RecyclerView.ViewHolder {
       RelativeLayout moviesLayout;
        TextView movieTitle;
        TextView data;
        TextView movieDescription;
        TextView rating;


        public MovieViewHolder(View v) {
            super(v);
            moviesLayout = (RelativeLayout) v.findViewById(R.id.movie_layout);
            movieTitle = (TextView) v.findViewById(R.id.title);
            data = (TextView) v.findViewById(R.id.subtitle);
            movieDescription = (TextView) v.findViewById(R.id.description);
            rating = (TextView) v.findViewById(R.id.rating);
        }
    }

    public MovieAdapter(List<Movie> movies, int rowLayout, Context context) {
        this.movies = movies;
        this.rowLayout = rowLayout;
        this.context = context;
    }

    @Override
    public MovieAdapter.MovieViewHolder onCreateViewHolder(ViewGroup parent,
                                                            int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(rowLayout, parent, false);
        return new MovieViewHolder(view);
    }


    @Override
    public void onBindViewHolder(MovieViewHolder holder, final int position) {
        holder.movieTitle.setText(movies.get(position).getTitle());
        holder.data.setText(movies.get(position).getReleaseDate());
        holder.movieDescription.setText(movies.get(position).getOverview());
        holder.rating.setText(movies.get(position).getvote_average().toString());
    }

    @Override
    public int getItemCount() {
        return movies.size();
    }
}

现在是MainActivity类,我在其中调用API并获取结果并将结果提供给适配器

MainActivity.java

package com.example.vikas.movie;

import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;


import com.example.vikas.movie.adapter.MovieAdapter;
import com.example.vikas.movie.model.Movie;
import com.example.vikas.movie.model.MoviesResponse;
import com.example.vikas.movie.rest.ApiClient;
import com.example.vikas.movie.rest.ApiInterface;

import java.util.ArrayList;
import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class MainActivity extends AppCompatActivity
{

    private static final String TAG=MainActivity.class.getSimpleName();
    //get which got from web API
    private final static String API_KEY="Xxxxxx";
    private CoordinatorLayout coordinatorLayout;
    private MovieAdapter movieAdapter;
    private RecyclerView recyclerView;
    private List<Movie> movieList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        coordinatorLayout=(CoordinatorLayout)findViewById(R.id.coordinate_layout);

        if(API_KEY.isEmpty())
        {
            Snackbar snackbar = Snackbar
                    .make(coordinatorLayout, "Please Enter The key", Snackbar.LENGTH_LONG);

            snackbar.show();
            return;
        }
        recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.setAdapter(new MovieAdapter(movieList, R.layout.recyclerview_item_movie, getApplicationContext()));


        ApiInterface apiInterface= ApiClient.getClient().create(ApiInterface.class);
        Call<MoviesResponse> call= apiInterface.getNowPlayingMovies(API_KEY);
        call.enqueue(new Callback<MoviesResponse>()
        {
            @Override
            public void onResponse(Call<MoviesResponse> call, Response<MoviesResponse> response)
            {
                Log.d(TAG, "Number of movies received: " + movieList.size());
                int statusCode=response.code();
                 movieList= response.body().getResults();
                 movieAdapter.notifyDataSetChanged();


            }

            @Override
            public void onFailure(Call<MoviesResponse> call, Throwable t)
            {
                Snackbar snackbar = Snackbar
                        .make(coordinatorLayout, "Please Check your Internet Connection", Snackbar.LENGTH_LONG);

                snackbar.show();
                Log.e(TAG, t.toString());

            }
        });

    }
}

但是当我运行此代码时,我收到此错误logcat

04-23 23:32:32.529 30506-30506/com.example.vikas.movie E/AndroidRuntime: FATAL EXCEPTION: main
                                                                     Process: com.example.vikas.movie, PID: 30506
                                                                     java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.vikas.movie.adapter.MovieAdapter.notifyDataSetChanged()' on a null object reference
                                                                         at com.example.vikas.movie.MainActivity$1.onResponse(MainActivity.java:69)
                                                                         at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:68)
                                                                         at android.os.Handler.handleCallback(Handler.java:739)
                                                                         at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                         at android.os.Looper.loop(Looper.java:148)
                                                                         at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                         at java.lang.reflect.Method.invoke(Native Method)
                                                                         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

那么我现在该怎么做才能运行这段代码并获取结果以成功显示结果,以便它不会返回null

这个问题不同,首先我无法提供正确的网址

2 个答案:

答案 0 :(得分:0)

首先你的网址是错误的。它应该是

 http://api.themoviedb.org/3/movie/now_playing?api_key=ddfffa28b4ace50cacf67274370469a1 

注意结束点 movie / now_playing 而不是电影/正在播放

这在https://developers.themoviedb.org/3/movies/get-now-playing

中有记录

此外,您可以在获得响应并初始化列表

后获取列表大小

首先这个

movieList= response.body().getResults();

然后

Log.d(TAG, "Number of movies received: " + movieList.size());

Edit1:

你有这个

 recyclerView.setAdapter(new MovieAdapter(movieList, R.layout.recyclerview_item_movie, getApplicationContext()));

您永远不会初始化movieAdapter,当您致电notifyDataSetChanged()时,movieAdapter为空。

将其更改为

 movieAdapter = new MovieAdapter(movieList, R.layout.recyclerview_item_movie, getApplicationContext());

然后

recyclerView.setAdapter(movieAdapter);

注意在大多数与ui相关的情况下,获取getApplicationContext()可能没用。喜欢活动背景。

编辑2:您还需要将项目添加到同一列表中,然后按照以下评论中的建议调用notifyDataSetChanged()

答案 1 :(得分:0)

替换

recyclerView.setAdapter(new MovieAdapter(movieList, R.layout.recyclerview_item_movie, getApplicationContext()));

使用

movieAdapter = new MovieAdapter(movieList, R.layout.recyclerview_item_movie, this);
recyclerView.setAdapter(movieAdapter);

并替换

movieList= response.body().getResults();
movieAdapter.notifyDataSetChanged();

有了这个。否则notifyDataSetChanged什么都不做,因为你有一个完全不同的列表,需要重新创建适配器

movieList.clear();
movieList.addAll(response.body().getResults()); 
movieAdapter.notifyDataSetChanged();