0

I'm creating a movie list application that will use the TMDb api. I have created two spearate fragments that call separate api methods.

There is a java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference error. Here's my error code:

E/AndroidRuntime: FATAL EXCEPTION: main
              Process: sg.edu.tp.moviex, PID: 6404
              java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference
                  at sg.edu.tp.moviex.Adapter.MoviesAdapter.getItemCount(MoviesAdapter.java:56)
                  at android.support.v7.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:3722)
                  at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3527)
                  at android.support.v7.widget.RecyclerView.consumePendingUpdateOperations(RecyclerView.java:1737)
                  at android.support.v7.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:4928)
                  at android.view.Choreographer$CallbackRecord.run(Choreographer.java:911)
                  at android.view.Choreographer.doCallbacks(Choreographer.java:723)
                  at android.view.Choreographer.doFrame(Choreographer.java:655)
                  at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
                  at android.os.Handler.handleCallback(Handler.java:790)
                  at android.os.Handler.dispatchMessage(Handler.java:99)
                  at android.os.Looper.loop(Looper.java:164)
                  at android.app.ActivityThread.main(ActivityThread.java:6494)
                  at java.lang.reflect.Method.invoke(Native Method)
                  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
                  at com.android.internal.os.ZygoteInit.main    

Here's the fragment java code:

public class LatestBlockbustersFragment extends Fragment {

View view;
private RecyclerView recyclerView1;
private MoviesAdapter moviesAdapter;
private List<Movie> movieList;
private SwipeRefreshLayout swipeContainer1;
public static final String LOG_TAG = MoviesAdapter.class.getName();

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState){
    view = inflater.inflate(R.layout.fragment_home, container, false);
    initViews();
    return view;
}

@Override
public void onCreate(@Nullable Bundle SavedInstanceState){
    super.onCreate(SavedInstanceState);

    movieList = new ArrayList<>();
}

private void loadJSON(){
    try {
        if (BuildConfig.THE_MOVIE_DB_API_TOKEN.isEmpty()) {
            Toast.makeText(getActivity(), "Please obtain API key from themoviedb.org", Toast.LENGTH_SHORT).show();
            return;
        }
        Client Client = new Client();

        Service apiService = Client.getClient().create(Service.class);

        // API service: getLatestMovies ; change to other forms to get other types
        Call<MovieResponse> call = apiService.getLatestMovies(BuildConfig.THE_MOVIE_DB_API_TOKEN);
        call.enqueue(new Callback<MovieResponse>() {
            @Override
            public void onResponse(Call<MovieResponse> call, Response<MovieResponse> response) {
                List<Movie> movies = response.body().getResults();
                recyclerView1.setAdapter(new MoviesAdapter(getActivity(), movies));
                recyclerView1.smoothScrollToPosition(0);
                if (swipeContainer1.isRefreshing()){
                    swipeContainer1.setRefreshing(false);
                }

            }

            @Override
            public void onFailure(Call<MovieResponse> call, Throwable t) {
                Log.d("Error", t.getMessage());
                Toast.makeText(getActivity(), "Error Fetching Data!", Toast.LENGTH_SHORT).show();
            }
        });


    } catch (Exception e) {
        Log.d("Error", e.getMessage());
        Toast.makeText(getActivity(), e.toString(), Toast.LENGTH_SHORT).show();
    }
}


private void initViews(){

    recyclerView1 = view.findViewById(R.id.recycler_view);
    movieList = new ArrayList<>();
    moviesAdapter = new MoviesAdapter(getActivity(), movieList);

    recyclerView1.setLayoutManager(new GridLayoutManager(getActivity(),2));
    recyclerView1.setItemAnimator(new DefaultItemAnimator());
    recyclerView1.setAdapter(moviesAdapter);
    moviesAdapter.notifyDataSetChanged();

    loadJSON();

    //TODO Look at SwipeRefreshLayout and Lambda Expressions

    swipeContainer1 = view.findViewById(R.id.swipeContainer);
    swipeContainer1.setColorSchemeResources(android.R.color.holo_orange_dark);
    swipeContainer1.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            initViews();
        }
    });

}

Here's the MovieAdapter code, where the error is:

import android.content.Context;
import android.content.Intent;
import android.support.v7.widget.RecyclerView;
import android.telecom.Call;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.bumptech.glide.Glide;

import java.util.List;

import sg.edu.tp.moviex.Activities.MovieDetailActivity;
import sg.edu.tp.moviex.Model.Movie;
import sg.edu.tp.moviex.R;

public class MoviesAdapter extends       RecyclerView.Adapter<MoviesAdapter.MyViewHolder> {

private Context mContext;
private List<Movie> movieList;

public MoviesAdapter(Context mContext, List<Movie> movieList){
    this.mContext = mContext;
    this.movieList = movieList;
}

// int i refers to the count/position

@Override
public MoviesAdapter.MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i){
    View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.movie_card, viewGroup, false);

    return new MyViewHolder(view);
}


@Override
public void onBindViewHolder(final MoviesAdapter.MyViewHolder viewHolder, int i){
    // Calls getOriginalTitle from movie.java and sets as movie title
    viewHolder.title.setText(movieList.get(i).getOriginalTitle());
    // Calls getVoteAverage from movie.java and sets to userrating
    String vote = Double.toString(movieList.get(i).getVoteAverage());
    viewHolder.userrating.setText(vote);
    // Calls getPosterPath from movie.java and sets poster as thumbnail
    Glide.with(mContext).load(movieList.get(i).getPosterPath())
            .placeholder(R.drawable.load).into(viewHolder.thumbnail);
}


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

public class MyViewHolder extends RecyclerView.ViewHolder{

    public TextView title, userrating;
    public ImageView thumbnail;

    public MyViewHolder(View view){
        super(view);
        title = view.findViewById(R.id.title);
        userrating = view.findViewById(R.id.userrating);
        thumbnail = view.findViewById(R.id.thumbnail);

        view.setOnClickListener(v -> {
            int position = getAdapterPosition();
            if (position != RecyclerView.NO_POSITION){
                Movie clickedDataItem = movieList.get(position);
                Intent intent = new Intent(mContext, MovieDetailActivity.class);
                intent.putExtra("original_title", movieList.get(position).getOriginalTitle());
                intent.putExtra("poster_path", movieList.get(position).getPosterPath());
                intent.putExtra("overview", movieList.get(position).getOverview());
                intent.putExtra("vote_average",Double.toString(movieList.get(position).getVoteAverage()));
                intent.putExtra("release_date", movieList.get(position).getReleaseDate());
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                mContext.startActivity(intent);
                Toast.makeText(v.getContext(), "You clicked" + clickedDataItem.getOriginalTitle(), Toast.LENGTH_SHORT).show();
            }
        });
    }
}

Please help. Thanks in advance.

Kenneth
  • 1
  • 1

1 Answers1

0

In your fragment you have to add the movies to your moviesList

movieList.add(movies);

I would suggest you to do in the API response as below:

  try {
                    JSONObject job = new JSONObject(response);
                    if (movieList != null) {
                        movieList.clear();
                    }
                        JSONArray jarray = job.getJSONArray("whatever array name");
                        if (jarray.length() > 0) {
                            for (int i = 0; i < jarray.length(); i++) {
                         JSONObject json_obj = jarray.getJSONObject(i);
                         Movies type = new Movies();
                         type.setOriginalTitle(json_obj.getString("parameter name"));
                         type.setVoteAverage(json_obj.getString("parameter name"));
                         type.setPosterPath(json_obj.getString("parameter name"));

                         movieList.add(type);
                            }

                      if (movieList != null) {
                      Log.d("asher","movie list "+movieList);
                      MoviesAdapter adapter = new MoviesAdapter(activity, movieList);
                      recyclerView1.setAdapter(adapter);
                            }

                        }
                } catch (JSONException e) {
                    e.printStackTrace();
                }

And in the adapter class now you can get it with the position in the onBindViewHolder(). Put Log in your onBindViewHolder method and check if all the data is coming. And do the same in the fragment class , put log for the movieList before you set it to the adapter.