Android: JSON parse: null object reference when at

2020-05-10 09:10发布

问题:

This app is supposed to parse some JSON data (hard coded for now) from the Google Books API, and pass an ArrayList of Books to the adapter that will display it on a ListView. The problem I have is that the JSON parse is returning null instead of the parsed data.

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    ProgressBar pBar;
    List<MyTask> tasks;

    ArrayList<Book> bookList;

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

        pBar = (ProgressBar) findViewById(R.id.progressBar);
        pBar.setVisibility(View.INVISIBLE);

        Button sButton = (Button) findViewById(R.id.s_button);
        sButton.setOnClickListener(this);

        tasks = new ArrayList<>();

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.s_button: {
                if (isOnline()) {
                    new MyTask().execute("https://www.googleapis.com/books/v1/volumes?q=millionare"); //https://www.googleapis.com/books/v1/volumes?q=soft+skills

                } else {
                    Toast.makeText(this, "Connection failed", Toast.LENGTH_LONG).show();
                }
                break;
            }
        }
    }

    protected boolean isOnline() {

        ConnectivityManager connectManager = (ConnectivityManager)
                getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo netInfo = connectManager.getActiveNetworkInfo();

        if (netInfo != null && netInfo.isConnectedOrConnecting()) {
            return true;
        } else {
            return false;
        }
    }

    private class MyTask extends AsyncTask<String, Void, String> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected String doInBackground(String... urls) {

            // params comes from the execute() call: params[0] is the url.
            try {
                return HttpManager.getData(urls[0]);
            } catch (IOException e) {
                return "Unable to retrieve web page. URL may be invalid.";
            }
        }

        @Override
        protected void onPostExecute(String result) {
            bookList = BookJSONParser.parseFeed(result);
            updateDisplay();
        }

    }

    protected void updateDisplay() {

        BookAdapter adapter = new BookAdapter(this, bookList);

        ListView listView = (ListView) findViewById(R.id.list);

        listView.setAdapter(adapter);
    }
}

public class BookJSONParser {


    public static ArrayList<Book> parseFeed(String content) {

        try {
            JSONArray jsonArray = new JSONArray(content);
            ArrayList<Book> bookList = new ArrayList<>();

            for (int i = 0; i < jsonArray.length(); i++) {

                JSONObject object = jsonArray.getJSONObject(i);

                String name = object.getString("title").toString();

                Book book = new Book(name);
                bookList.add(book);

            }

            return bookList;
        } catch (JSONException e) {
            e.printStackTrace();
            return null;
        }

    }

}

public class BookAdapter extends ArrayAdapter<Book> {

    public BookAdapter(Context context, ArrayList<Book> bookList) {
        super(context, 0, bookList);

    }

    @Override
    public View getView(int position, View convertedView, ViewGroup parent) {

        View listItemView = convertedView;
        if (listItemView == null) {
            listItemView = LayoutInflater.from(getContext()).inflate(
                    R.layout.list_item, parent, false);
        }

        Book currentBook = getItem(position);

        TextView locationName = (TextView) listItemView.findViewById(R.id.book_title);

        locationName.setText(currentBook.getTittle());

        TextView locationAddress = (TextView) listItemView.findViewById(R.id.book_author);

        locationAddress.setText(currentBook.getAuthor());

        return listItemView;

    }
}

public class HttpManager {

    public static String getData(String myUrl) throws IOException {

        // BufferedReader reader = null;

        InputStream inputStream = null;

        int len = 10000;

        try {
            URL url = new URL(myUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setReadTimeout(10000 /* milliseconds */);
            connection.setConnectTimeout(15000 /* milliseconds */);
            connection.setRequestMethod("GET");
            connection.setDoInput(true);
            // Starts the query
            connection.connect();
            int response = connection.getResponseCode();

            inputStream = connection.getInputStream();

            // Convert the InputStream into a string
            String contentAsString = readIt(inputStream, len);
            return contentAsString;

            // Makes sure that the InputStream inputStream closed after the app inputStream
            // finished using it.
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    return null;
                }

            }
        }
    }

 // Reads an InputStream and converts it to a String.
    public static String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
        Reader reader = null;
        reader = new InputStreamReader(stream, "UTF-8");
        char[] buffer = new char[len];
        reader.read(buffer);
        return new String(buffer);
    }
}

public class Book {

    private String mTittle;

    /**
     * This is the constructor.
     * @param title is the book title being passed in.
     */
    public Book(String title) {
        mTittle = title;

    }

    public String getTittle() {
        return mTittle;
    }

    public void setTittle(String tittle) {
        mTittle = tittle;
    }

}

FATAL EXCEPTION: main
                                                                              Process: com.narvin.android.booklisting, PID: 3278
                                                                              java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference
                                                                                  at android.widget.ArrayAdapter.getCount(ArrayAdapter.java:330)
                                                                                  at android.widget.ListView.setAdapter(ListView.java:502)
                                                                                  at com.narvin.android.booklisting.MainActivity.updateDisplay(MainActivity.java:113)
                                                                                  at com.narvin.android.booklisting.MainActivity$MyTask.onPostExecute(MainActivity.java:100)
                                                                                  at com.narvin.android.booklisting.MainActivity$MyTask.onPostExecute(MainActivity.java:79)
                                                                                  at android.os.AsyncTask.finish(AsyncTask.java:632)
                                                                                  at android.os.AsyncTask.access$600(AsyncTask.java:177)
                                                                                  at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
                                                                                  at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                                  at android.os.Looper.loop(Looper.java:145)
                                                                                  at android.app.ActivityThread.main(ActivityThread.java:5942)
                                                                                  at java.lang.reflect.Method.invoke(Native Method)
                                                                                  at java.lang.reflect.Method.invoke(Method.java:372)
                                                                                  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
                                                                                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)

回答1:

The issue is that one of the arguments in BookAdapter adapter = new BookAdapter(this, bookList); is null for some reason. Try passing bookList as an argument to updateDisplay and checking whether it's not null.

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

ProgressBar pBar;
List<MyTask> tasks;

ArrayList<Book> bookList;

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

    pBar = (ProgressBar) findViewById(R.id.progressBar);
    pBar.setVisibility(View.INVISIBLE);

    Button sButton = (Button) findViewById(R.id.s_button);
    sButton.setOnClickListener(this);

    tasks = new ArrayList<>();

}

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.s_button: {
            if (isOnline()) {
                new MyTask().execute("https://www.googleapis.com/books/v1/volumes?q=millionare"); //https://www.googleapis.com/books/v1/volumes?q=soft+skills

            } else {
                Toast.makeText(this, "Connection failed", Toast.LENGTH_LONG).show();
            }
            break;
        }
    }
}

protected boolean isOnline() {

    ConnectivityManager connectManager = (ConnectivityManager)
            getSystemService(Context.CONNECTIVITY_SERVICE);

    NetworkInfo netInfo = connectManager.getActiveNetworkInfo();

    if (netInfo != null && netInfo.isConnectedOrConnecting()) {
        return true;
    } else {
        return false;
    }
}

private class MyTask extends AsyncTask<String, Void, String> {

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected String doInBackground(String... urls) {

        // params comes from the execute() call: params[0] is the url.
        try {
            return HttpManager.getData(urls[0]);
        } catch (IOException e) {
            return "Unable to retrieve web page. URL may be invalid.";
        }
    }

    @Override
    protected void onPostExecute(String result) {
        ArrayList<Book> bookList = BookJSONParser.parseFeed(result);
        updateDisplay(bookList);
    }

}

protected void updateDisplay(ArrayList<Book> bookList) {
    if (bookList != null){
        BookAdapter adapter = new BookAdapter(this, bookList);

        ListView listView = (ListView) findViewById(R.id.list);

        listView.setAdapter(adapter);
     }
}
}


回答2:

It would appear you are getting a JSONParseException... therefore causing a NullPointerExpcetion for the List into the Adapter

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

That is your error, here is how you get it

public static ArrayList<Book> parseFeed(String content) {

    try {
        JSONArray jsonArray = new JSONArray(content); // <-- Throws an error
        ArrayList<Book> bookList = new ArrayList<>();

        // Stuff...

        return bookList;
    } catch (JSONException e) {
        e.printStackTrace();
        return null; // <----- Null is returned
    }

And you use that null value here

    @Override
    protected void onPostExecute(String result) {
        bookList = BookJSONParser.parseFeed(result);
        updateDisplay();
    }

Followed by

protected void updateDisplay() {

    BookAdapter adapter = new BookAdapter(this, bookList); // <-- Null here

    ListView listView = (ListView) findViewById(R.id.list);

    listView.setAdapter(adapter);
}

So, the way to fix that NullPointerExpception is to always return an ArrayList

ArrayList<Book> bookList = new ArrayList<>();
try {
    JSONArray jsonArray = new JSONArray(content);

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

return bookList;


回答3:

to get json string from url you should do it like that

String content = new MyTask()
         .execute("https://www.googleapis.com/books/v1/volumes?q=millionare")
         .get();
//pass the content to BookJSONParser class
booklist = new BookJSONParser().parseFeed(content);
updateDisplay();

what you get from the url you provided is NOT jsonArray it's a jsonobject so I think this code will work "assuming that you did everything else correctly"

JSONObject o = new JSONObject(content);
JSONArray jsonArray = o.getJSONArray("items");

the you can do the for loop