0

I'm working on udacity popular movies stage 1 project that will allow me to discover movies from themoviedb database.

I've created Movie, MovieAdapter, and MainActivity(with FetchMovieAsyncTask as inner class) classes. But I keep getting below error

java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference

These are my codes

Movie class:

public class Movie{

    private String mMovieTitle;
    private String mPosterPath;
    private String mOverview;
    private String mReleaseDate;
    private double mRating;

    public Movie(){
        mMovieTitle = null;
        mPosterPath = null;
        mOverview = null;
        mReleaseDate = null;
        mRating = -1.0;
    }

    public Movie(String title){
        mMovieTitle = title;
    }


    public Movie(String title, String posterPath, String overview, String releaseDate, double rating){
        mMovieTitle = title;
        mPosterPath = posterPath;
        mOverview = overview;
        mReleaseDate = releaseDate;
        mRating = rating;
    }

    public String getMovieTitle(){
        return mMovieTitle;
    }

    public String getPosterPath(){
        return mPosterPath;
    }

    public String getOverview(){
        return mOverview;
    }

    public String getReleaseData(){
        return mReleaseDate;
    }

    public double getRating(){
        return mRating;
    }


}

MovieAdapter class:

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import com.squareup.picasso.Picasso;

import java.util.ArrayList;

/**
 * Created by ibayp on 04/08/2017.
 */

public class MovieAdapter extends ArrayAdapter<Movie> {
    Context context;
    ArrayList<Movie> movies;
    public MovieAdapter(Context context, ArrayList<Movie> movies){
        super(context, 0, movies);
        this.context = context;
        this.movies = movies;
    }


    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        View view = convertView;

        if(view == null){
            view = LayoutInflater.from(getContext()).inflate(R.layout.poster_list, parent, false);
        }

        ImageView imageView = (ImageView)view.findViewById(R.id.movie_poster);

        Picasso.with(getContext())
                .load("https://image.tmdb.org/t/p/w500/kqjL17yufvn9OVLyXYpvtyrFfak.jpg")
                .into(imageView);

        return view;
    }
}

and MainActivity class:

package com.android.ibayp.popularmovies;

import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {
    GridView gridView;
    ArrayList<Movie> movies;

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

        new FetchMovieAsyncTask().execute("popularity.desc");
        MovieAdapter adapter = new MovieAdapter(this, movies);


        gridView = (GridView)findViewById(R.id.grid_view);
        gridView.setAdapter(adapter);
        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long l) {
                Movie movie = (Movie)parent.getItemAtPosition(position);
                Intent intent = new Intent(MainActivity.this, DetailActivity.class);
                intent.putExtra("title", movie.getMovieTitle());
                startActivity(intent);
            }
        });
    }

    private class FetchMovieAsyncTask extends AsyncTask<String, Void, ArrayList<Movie>> {
        private final String TAG = com.android.ibayp.popularmovies.FetchMovieAsyncTask.class.getSimpleName();

        private static final String API_KEY = "api key";
        private static final String BASE_URL = "https://api.themoviedb.org/3/discover/movie?";

        private String API_PARAM = "api_key";
        private String SORT_BY = "sort_by";

        public URL buildURL(String[] sortMethod){
            Uri builtUri = Uri.parse(BASE_URL).buildUpon()
                    .appendQueryParameter(API_PARAM, API_KEY)
                    .appendQueryParameter(SORT_BY, sortMethod[0])
                    .build();

            URL url = null;
            try{
                url = new URL(builtUri.toString());
            }catch (MalformedURLException e){
                e.printStackTrace();
            }

            Log.v(TAG, "BUILT URI: " +url);

            return url;
        }

        private String makeHttpRequst(URL url)throws IOException {
            String jsonResponse = "";
            HttpURLConnection urlConnection = null;
            InputStream inputStream = null;
            try{
                urlConnection = (HttpURLConnection)url.openConnection();
                urlConnection.setRequestMethod("GET");
                urlConnection.connect();
                inputStream = urlConnection.getInputStream();
                jsonResponse = readStream(inputStream);
            }catch (IOException e){
                e.printStackTrace();
            }finally {
                if(urlConnection!=null){
                    urlConnection.disconnect();
                }
                if (inputStream!=null){
                    inputStream.close();
                }
            }
            return jsonResponse;
        }

        private String readStream(InputStream inputStream) throws IOException{
            StringBuilder results = new StringBuilder();
            if(inputStream!=null){
                InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
                BufferedReader reader = new BufferedReader(inputStreamReader);
                String line = reader.readLine();
                while (line!=null){
                    results.append(line);
                    line = reader.readLine();
                }
            }

            return results.toString();
        }

        private ArrayList<Movie> getMovieFromJson(String movieJson){
            movies = new ArrayList<Movie>();
            try{
                JSONObject baseResponse = new JSONObject(movieJson);
                JSONArray resultsArray = baseResponse.getJSONArray("results");
                Log.v(TAG, "Array Results: "+resultsArray.length());
                if (resultsArray.length() > 0){
                    for (int i = 0; i<resultsArray.length(); i++ ){
                        JSONObject movieInformation = resultsArray.getJSONObject(i);
                        movies.add(new Movie(movieInformation.getString("title"),
                                movieInformation.getString("poster_path"),
                                movieInformation.getString("overview"),
                                movieInformation.getString("release_date"),
                                movieInformation.getDouble("vote_average")));

                    }
                }
            }catch (JSONException e){
                Log.v(TAG, "Problem parsing Json" + e);
            }

            return movies;
        }

        @Override
        protected ArrayList<Movie> doInBackground(String... strings) {
            URL url = buildURL(strings);

            String jsonResponse = "";
            try{
                jsonResponse = makeHttpRequst(url);
            }catch (IOException e){
                Log.v(TAG, "IO Exception error "+e);
            }

            movies = getMovieFromJson(jsonResponse);

            return movies;
        }

        @Override
        protected void onPostExecute(ArrayList<Movie> movies) {
            if (movies==null){
                return;
            }

            super.onPostExecute(movies);
        }
    }


}

I used logging and managed to get the correct json results, but I can't display it on the ui thread.

How can I solve this? Thanks

*note that I used dummy image for the poster

3 Answers 3

1

When you instantiate your adapter movies are null. Because asynctask is running in parallel thread. Instantiate your adapter in your asynctask's onPostExecute method. This way you will be sure asynctask is completed.

    @Override
    protected void onPostExecute(ArrayList<Movie> movies) {
        if (movies==null){
            return;
        }

        super.onPostExecute(movies);
        MovieAdapter adapter = new MovieAdapter(MainActivity.this, movies);
        gridView.setAdapter(adapter);
    }
Sign up to request clarification or add additional context in comments.

1 Comment

This works!! And also thank you for the explanation :D
0

Call adapter.notifyDataSetChanged() after movies.add(...) or in onPostExecute

1 Comment

I dont think that will remove the OPs null pointer exception. Also the movies.add part is in background thread, so calling notify from there doesn't make sense.
0

I finished the Android Nanodegree a while back and remember working on this project. When you instantiate an adapter it will call the getSize() method to determine how many rows to create for the views and then call getView() on each of them to bind the views.

Since your async Task may not have finished downloading the movies, you're passing an empty list to the adapter and thus getting a NullPointerException.

It's always a good approach to instantiate the adapter in the onPostExecute() method of AsyncTask. This method is called on the UI thread after the background thread has finished execution.

So put these two lines of code in onPostExecute() and it should work

MovieAdapter adapter = new MovieAdapter(MainActivity.this, movies);
gridView.setAdapter(adapter);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.