1

I'm trying to do a pretty simple update to the URL that is being queried. I change the URL in onOptionsItemSelected based on which item in the menu is selected. If I choose the first item the app works great. If I choose the second item the app crashes. Why? Both URLs have been verified to be correct. I believe selecting the second item (highestRated) causes a NetworkOnMainThreadException. Any debugging help would be appreciated.

public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == R.id.mostPopular) {
        FILM_REQUEST_URL = "http://api.themoviedb.org/3/movie/popular?api_key=[MyNetworkKey]";
    } else if (item.getItemId() == R.id.highestRated) {
        FILM_REQUEST_URL = "http://api.themoviedb.org/3/movie/top_rated?api_key=[MyNetworkKey]";
    }

    // Clear the adapter of previous film data
    mAdapter.clear();

    // Get the revised film list
    List<Film> films = QueryUtils.fetchFilmData(FILM_REQUEST_URL);

    // Populate the adapters data set
    if (films != null && !films.isEmpty()) {
        mAdapter.addAll(films);
    }

    return super.onOptionsItemSelected(item);
}

Logcat Error: Logcat Error Trace

QueryUtils.java:

public class QueryUtils {

private QueryUtils() {
}

/** Tag for log messages */
private static final String LOG_TAG = QueryUtils.class.getSimpleName();

/**
 * Query TMDb and return a list of {@link Film} objects
 */
public static List<Film> fetchFilmData(String requestUrl) {
    // Create a URL object
    URL url = createUrl(requestUrl);

    //Call HTTP request to URL and get JSON response
    String jsonResponse = null;
    try {
        jsonResponse = makeHttpRequest(url);
    } catch (IOException e) {
        Log.e(LOG_TAG, "Problem making the HTTP request ", e);
    }

    // Extract relevant fields from the JSON response and return the list of films
    return extractFeatureFromJson(jsonResponse);
}

/**
 * Returns new URL object from the given string URL
 */
private static URL createUrl(String stringUrl) {
    URL url = null;
    try {
        url = new URL(stringUrl);
    } catch (MalformedURLException e) {
        Log.e(LOG_TAG, "Problem building the URL ", e);
    }
    return url;
}

/**
 * Make HTTP request to the URL and return a String as the response
 */
private static String makeHttpRequest(URL url) throws IOException {
    String jsonResponse = "";

    // If the URL is null, then return
    if (url == null) {
        return jsonResponse;
    }

    HttpURLConnection urlConnection = null;
    InputStream inputStream = null;
    try {
        urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setReadTimeout(10000 /* milliseconds */);
        urlConnection.setConnectTimeout(15000 /* milliseconds */);
        urlConnection.setRequestMethod("GET");
        urlConnection.connect();

        // If the request was successful (response code 200),
        // then read the input stream and parse the response.
        if (urlConnection.getResponseCode() == 200) {
            inputStream = urlConnection.getInputStream();
            jsonResponse = readFromStream(inputStream);
        } else {
            Log.e(LOG_TAG, "Error response code: " + urlConnection.getResponseCode());
        }
    } catch (IOException e) {
        Log.e(LOG_TAG, "Problem retrieving the movie JSON results ", e);
    } finally {
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
        if (inputStream != null) {
            // Closing the input stream could throw an IOException, which is why
            // the makeHttpRequest(URL url) method signature specifies than an IOException
            // could be thrown
            inputStream.close();
        }
    }
    return jsonResponse;
}

/**
 * Convert the {@link InputStream} into a String that contains JSON
 * response from the server
 */
private static String readFromStream(InputStream inputStream) throws IOException {
    StringBuilder output = 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) {
            output.append(line);
            line = reader.readLine();
        }
    }
    return output.toString();
}

/**
 * Return a list of {@link Film} objects that has been built up from
 * parsing the given JSON response
 */
private static List<Film> extractFeatureFromJson(String filmJSON) {

    // If the JSON string is empty or null, then return
    if (TextUtils.isEmpty(filmJSON)) {
        return null;
    }

    // Create an empty ArrayList that we can start adding films to
    List<Film> films = new ArrayList<>();

    // Try to parse the JSON response string. If there's a problem with the way the JSON
    // is formatted, a JSONException exception object will be thrown
    try {

        // Create a JSONObject from the JSON response string
        JSONObject baseJsonResponse = new JSONObject(filmJSON);

        // Extract the JSONArray associated with the key called "results",
        // which represents a list of films
        JSONArray filmArray = baseJsonResponse.getJSONArray("results");

        // For each movie in the movieArray, create an {@link Film} object
        for (int i = 0; i < filmArray.length(); i++) {

            // Get a single movie at position i within the list of movies
            JSONObject currentFilm = filmArray.getJSONObject(i);

            // Extract the value for individual keys from JSONObject results
            int voteCount = currentFilm.getInt("vote_count");
            long voteAverage = currentFilm.getLong("vote_average");
            String title = currentFilm.getString("title");
            long popularity = currentFilm.getLong("popularity");
            String posterUrl = "http://image.tmdb.org/t/p/w185" + currentFilm.getString("poster_path");
            String overview = currentFilm.getString("overview");
            String releaseDate = currentFilm.getString("release_date");

            // Create a new {@link Film} object with the vote count, vote average, title,
            // popularity, poster path, overview, and release date from the JSON response
            Film film = new Film(voteCount, voteAverage, title, popularity, posterUrl,
                    overview, releaseDate);

            // Add the new {@link Film} to the list of movies
            films.add(film);
        }

    } catch (JSONException e) {
        // If an error is thrown when executing any of the above statements in the "try" block,
        // catch the exception here, so the app doesn't crash. Print a log message
        // with the message from the exception
        Log.e("QueryUtils", "Problem parsing the TMDb JSON results", e);
    }

    // Return the list of films
    return films;
}

}

Indy
  • 209
  • 1
  • 2
  • 8
  • 1
    This has nothing to do with onOptionsItemSelected. The crash is probably caused by your fetchFilmData() method. Post the stack trace of the crash (from Logcat) and the code from fetchFilmData() please. – Ridcully Apr 27 '18 at 18:48
  • @Ridcully I've updated the original question to include the logcat error messages and the QueryUtils code. I agree with you that it's not an issue with onOptionsItemSelected but I'm just really confused why I can hit the first menu item (mostPopular) as many times as I want and the app works fine but as soon as I hit the second menu item it crashes. It doesn't matter if I interchange the URLs I still get the same issue. – Indy Apr 27 '18 at 20:02

1 Answers1

0

From: https://developer.android.com/reference/android/os/NetworkOnMainThreadException

NetworkOnMainThreadException:

The exception that is thrown when an application attempts to perform a networking operation on its main thread.

In order to avoid this exception you will need to make your HTTP request on a separate thread. One possible way to accomplish this is to use an AsyncTask.

Here is a stackoverflow post going over how to use an AsyncTask to make a HTTP GET request.

Community
  • 1
  • 1
  • Shoot I thought I was using AsyncTask but clearly not. Reworking the code now but this should probably fix it. Thanks! – Indy Apr 27 '18 at 21:29