=========================[ Readings ]========================= If you haven't read through the Android training example https://developer.android.com/training/basics/network-ops/connecting.html, please catch up. The important things are understand are 1) why the AsyncTask is wrapped in a "headless" Fragment that calls setRetainInstance(true); 2) how status and progress are handled (via the interface DownloadCallback); and 3) how the AsyncTask uses that interface and its DownloadTask.Result to pass "exception or result" back See notes in the previous lecture. =================[ Volley & error processing ]========================= Volley hides away all that complexity, especially error-handling complexity. With Volley, you create Request objects and put them on a queue to execute (where they run on worker threads), and supply two callback objects (which define functions to run on the UI thread): Response.Listener<..> and Response.ErrorListener. ErrorListener takes an argument of base type VolleyError, but the actual type is one of AuthFailureError.java NetworkError.java NoConnectionError.java ParseError.java ServerError.java TimeoutError.java and you can select the right response by using instanceof . See https://stackoverflow.com/questions/24700582/handle-volley-error =================[ Setting HTTP headers with Volley ]================= So controlling behaviors of an HTTP connection is a bit harder, as the connection object is further abstracted. In the end, of course, it has to come back to HTTP headers and underlying Unix network sockets, but getting at them is a bit harder. Namely, you get to @Override a function in the Request class that returns a Map of strings that become "Header: Value" pairs. The syntax is a bit heavy: (see also // Cf. https://stackoverflow.com/questions/17049473/how-to-set-custom-header-in-volley-request StringRequest stringRequest = new StringRequest( // this is a call to StringRequest's constructor // to build an anonymous class off StringRequest Request.Method.GET, // 1st argument url, // 2nd argument // this is the 3rd argument, a glorified lambda function as a callback new Response.Listener() { // JSONObject @Override public void onResponse(String response) { mTextView.setText(response); } }, // 4th argument, also a glorified lambda function new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { mTextView.setText("Error: " + error.toString() + "\n" + error.networkResponse.statusCode + "\n" + error.networkResponse.headers.toString() + "\n" + error.networkResponse.data.toString()); } } ) // arguments to the constructor end, but body of the new anonymous class begins: { // @Override public Map getHeaders() throws AuthFailureError { Map params = new HashMap(); // otherwise causes status 500 server error params.put("Accept", "application/json"); //params.put("User-Agent", "MSIE"); //params.put("Accept-Language", "en"); return params; } }; // finally, the assignment to stringRequest is done The latter example, as I discovered, is a minimal fix to prevent the National Weather Service API from crashing with a "500 Server Error" on asking for Hanover's weather, https://api.weather.gov/points/43.708366,-72.277187/forecast (Hanover coordinates from http://citylatitudelongitude.com/NH/Hanover.htm). ===============[ Getting parsed JSON objects back instead of String ]=============== Strings are good to get back for debugging, but we really want objects most of the time on the modern web. Rolling your own ad-hoc code to parse strings into objects should be avoided at all costs, because this is how bugs and vulnerabilities creep in. (Does your ad-hoc parser cover *all* corner cases of hostile input? Are you really really sure?). So one big strength of Volley is that it parses JSON responses for you automatically. You just need to use JsonObjectRequest: JsonObjectRequest jsObjRequest = new JsonObjectRequest( Request.Method.GET, url, null, // JSON body of the request, null for GETs new Response.Listener() { @Override public void onResponse(JSONObject response) { try { // JASON is auto-parsed, but we must match its structure exactly, // or else face JSON access exceptions. This is very error-prone, // so see next section on how to do it better. JSONObject i = (JSONObject) response.getJSONObject("properties"). getJSONArray("periods").get(0); mTextView.setText( i.getString("detailedForecast")); } catch( Exception e){ Log.d("JSON", e.toString()); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { mTextView.setText("Error: " + error.toString()); } }) { //.. manipulate HTTP headers as above, to add "Accept: application/json" }; ===============[ GSON and the magic of Java Reflection ]=============== Getting your incoming JSON automatically parsed is good, but accessing the right member of a nested JSON object still requires a series of calls of just the right method depending on the nested structure: if you forget at which step you had an array and at which step a single object, you still get an exception. This is too much like parsing. What you want to do is specify the expected object structure only once, in the most concise declarative form, and then generate any code for actual member access from it, automatically. That way you avoid having to co-ordinate this structure and multiple places in you code that must obey this structure, i.e., also implicitly describe/define it, but in much less readable form. Worse, all of these descriptions, implicit or explicit if any, must agree. So there is a much better way for parsing and accessing JSON: the GSON library (and the Retrofit library that goes further). It works as follows: - You create a _model_ of the data you expect, in the form of Java classes that have the data elements and types you are interested in. You need not cover all the data in the response. E.g., for the weather forecasts, the following works to get at the detailedForecast and the shortForecast (remember that each class goes into its own file, with the package name that matches your project! I omit the package declarations here.): ----[ WeatherResponse.java ]---- public class WeatherResponse { ForecastList properties; } ------[ ForecastList.java ]------ import java.util.ArrayList; import java.util.List; public class ForecastList { List periods; public Forecast get(int i){ return periods.get(i); } // public constructor is necessary for collections public ForecastList() { periods = new ArrayList(); } } --------[ Forecast.java ]-------- public class Forecast { // Sample content: int number; // 1 String name; // Tonight, String startTime; // 2017-10-03T19:00:00-05:00, String endTime; // 2017-10-04T06:00:00-05:00, boolean isDaytime; // false, int temperature; // 66, String temperatureUnit; // F, String temperatureTrend; // null, String windSpeed; // 6 mph, String windDirection; // WSW, String icon; // https://api.weather.gov/icons/land/night/tsra,40/tsra,60?size=medium, String shortForecast; // Chance Showers And Thunderstorms, String detailedForecast; // ... } - Then the code that parses the response and converts it into Java classes with members called "properties", "periods", and, at the leaf level of the overall JSON nesting structure, "detailedForecast", looks very simple: // Assume Response is a JSON string to be parsed Gson gson = new GsonBuilder().create(); WeatherResponse wr = gson.fromJson(response, WeatherResponse.class ); ForecastList forecastList = wr.properties; Forecast f = forecastList.get(0); mTextView.setText( f.shortForecast + "\n\n" + f.detailedForecast ); As you can see, the GSON parser guides itself solely by the structure of WeatherResponse.class , recursively creating the kind of objects needed, and going by the names of the class fields to extract the next chunk of JSON and convert it into object(s) of whatever type is expected at that point. See http://guides.codepath.com/android/leveraging-the-gson-library . The code example of this is in examples/WeatherVolley/ ------------[ Java Reflection ]------------ The fundamental mechanism that powers GSON is Java Reflection. With Reflection API, you can obtain objects that represent classes, and any such object can be asked what methods and fields the class has, and what their names and types are. So class objects (e.g., WeatherResponse.class) can be recursively processed, down to their leaf elements such as individual class fields of primitive types, to read off their structure and to construct parsers that create it from JSON input strings. More about Reflection API: http://tutorials.jenkov.com/java-reflection/fields.html http://tutorials.jenkov.com/java-reflection/methods.html You can scan GSON code (https://github.com/google/gson) for uses of the reflection API. Guess which one is used most often. Retrofit goes further and leverages Java 8 annotations to make automatic code generation even more flexible.