Fetching JSON from Flickr

JSON stands for JavaScript Object Notation, a format that has become popular in recent years, particularly for web services. Android includes the standard org.json package, which has classes that provide simple access to creating and parsing JSON text. The Android developer documentation has information about org.json, and you can get more information about JSON as a format at json.org.

Flickr offers a fine JSON API. All the details you need are available in the documentation at www.flickr.com/​services/​api/. Pull it up in your favorite web browser and find the list of Request Formats. You will be using the simplest – REST. This tells you that the API endpoint is https://​api.flickr.com/​services/​rest/. You can invoke the methods Flickr provides on this endpoint.

Back on the main page of the API documentation, find the list of API Methods. Scroll down to the photos section, then locate and click on flickr.photos.getRecent. The documentation will report that this method Returns a list of the latest public photos uploaded to flickr. That is exactly what you need for PhotoGallery.

The only required parameter for the getRecent method is an API key. To get an API key, return to www.flickr.com/​services/​api/ and follow the link for API keys. You will need a Yahoo ID to log in. Once you are logged in, request a new, noncommercial API key. This usually only takes a moment. Your API key will look something like 4f721bgafa75bf6d2cb9af54f937bb70. (You do not need the “Secret,” which is only used when an app will access user-specific information or images.)

Once you have a key, you have all you need to make a request to the Flickr web service. Your GET request URL will look something like this:

https://​api.flickr.com/​services/​rest/​?method=flickr.photos.getRecent&api_key=xxx&format=json&nojsoncallback=1.

The Flickr response is in XML format by default. To get a valid JSON response, you need to specify values for both the format and nojsoncallback parameters. Setting nojsoncallback to 1 tells Flickr to exclude the enclosing method name and parentheses from the response it sends back. This is necessary so that your Java code can more easily parse the response.

Copy the example URL into your browser, replacing the xxx value provided for the api_key with your actual API key. This will allow you to see an example of what the response data will look like, as shown in Figure 25.8.

Figure 25.8  Example JSON output

Screenshot shows Photogallery app screen in Android. The screen shows a list of photos.

Time to start coding. First, add some constants to FlickrFetchr.

Listing 25.7  Adding constants (FlickrFetchr.java)

  public class FlickrFetchr {

      private static final String TAG = "FlickrFetchr";

      private static final String API_KEY = "yourApiKeyHere";
      ...
  }

Make sure to replace yourApiKeyHere with the API key you generated earlier.

Now use the constants to write a method that builds an appropriate request URL and fetches its contents.

Listing 25.8  Adding fetchItems() method (FlickrFetchr.java)

public class FlickrFetchr {
    ...
    public String getUrlString(String urlSpec) throws IOException {
        return new String(getUrlBytes(urlSpec));
    }

    public void fetchItems() {
        try {
            String url = Uri.parse("https://api.flickr.com/services/rest/")
                    .buildUpon()
                    .appendQueryParameter("method", "flickr.photos.getRecent")
                    .appendQueryParameter("api_key", API_KEY)
                    .appendQueryParameter("format", "json")
                    .appendQueryParameter("nojsoncallback", "1")
                    .appendQueryParameter("extras", "url_s")
                    .build().toString();
            String jsonString = getUrlString(url);
            Log.i(TAG, "Received JSON: " + jsonString);
        } catch (IOException ioe) {
            Log.e(TAG, "Failed to fetch items", ioe);
        }
    }
}

Here you use a Uri.Builder to build the complete URL for your Flickr API request. Uri.Builder is a convenience class for creating properly escaped parameterized URLs. Uri.Builder.appendQueryParameter(String, String) will automatically escape query strings for you.

Notice you added values for the method, api_key, format, and nojsoncallback parameters. You also specified one extra parameter called extras, with a value of url_s. Specifying the url_s extra tells Flickr to include the URL for the small version of the picture if it is available.

Finally, modify the AsyncTask in PhotoGalleryFragment to call the new fetchItems() method.

Listing 25.9  Calling fetchItems() (PhotoGalleryFragment.java)

public class PhotoGalleryFragment extends Fragment {
    ...
    private class FetchItemsTask extends AsyncTask<Void,Void,Void> {
          @Override
          protected Void doInBackground(Void... params) {
              try {
                  String result = new FlickrFetchr()
                          .getUrlString("https://www.bignerdranch.com");
                  Log.i(TAG, "Fetched contents of URL: " + result);
              } catch (IOException ioe) {
                  Log.e(TAG, "Failed to fetch URL: ", ioe);
              }
              new FlickrFetchr().fetchItems();
              return null;
        }
    }
}

Run PhotoGallery and you should see rich, fertile Flickr JSON in Logcat, like Figure 25.9. (It will help to search for FlickrFetchr in the Logcat search box.)

Figure 25.9  Flickr JSON in Logcat

Screenshot shows Flickr JSON in Logcat.  The LogCat tab is selected on the top left corner of the screen. Below is the LogCat detail.

Unfortunately the Android Studio Logcat window does not wrap the output nicely as of this writing. Scroll to the right to see more of the extremely long JSON response string. (Logcat can be finicky. Do not panic if you do not get results like ours. Sometimes the connection to the emulator is not quite right and the log messages do not get printed out. Usually it clears up over time, but sometimes you have to rerun your application or even restart your emulator.)

Now that you have such fine JSON from Flickr, what should you do with it? You do what you do with all data – put it in one or more model objects. The model class you are going to create for PhotoGallery is called GalleryItem. Figure 25.10 shows an object diagram of PhotoGallery.

Figure 25.10  Object diagram of PhotoGallery

Figure shows object diagram for PhotoGallery.

Note that Figure 25.10 does not show the hosting activity so that it can focus on the fragment and the networking code.

Create the GalleryItem class and add the following code:

Listing 25.10  Creating model object class (GalleryItem.java)

public class GalleryItem {
    private String mCaption;
    private String mId;
    private String mUrl;

    @Override
    public String toString() {
        return mCaption;
    }
}

Have Android Studio generate getters and setters for mCaption, mId, and mUrl.

Now that you have made model objects, it is time to fill them with data from the JSON you got from Flickr.

Parsing JSON text

The JSON response displayed in your browser and Logcat window is hard to read. If you pretty print (format with white space) the response, it looks something like Figure 25.11.

Figure 25.11  JSON hierarchy

Figure shows JSON hierarchy.

A JSON object is a set of name-value pairs enclosed between curly braces, { }. A JSON array is a comma-separated list of JSON objects enclosed in square brackets, [ ]. You can have objects nested within each other, resulting in a hierarchy.

The org.json API provides Java objects corresponding to JSON text, such as JSONObject and JSONArray. You can easily parse JSON text into corresponding Java objects using the JSONObject(String) constructor. Update fetchItems() to do just that.

Listing 25.11  Reading JSON string into JSONObject (FlickrFetchr.java)

public class FlickrFetchr {

    private static final String TAG = "FlickrFetchr";
    ...
    public void fetchItems() {
        try {
            ...
            Log.i(TAG, "Received JSON: " + jsonString);
            JSONObject jsonBody = new JSONObject(jsonString);
        } catch (IOException ioe) {
            Log.e(TAG, "Failed to fetch items", ioe);
        } catch (JSONException je){
            Log.e(TAG, "Failed to parse JSON", je);
        }
    }
}

The JSONObject constructor parses the JSON string you passed it, resulting in an object hierarchy that maps to the original JSON text. The object hierarchy for the JSON returned from Flickr is shown in Figure 25.11.

Here you have a top-level JSONObject that maps to the outermost curly braces in the original JSON text. This top-level object contains a nested JSONObject named photos. Within this nested JSONObject is a JSONArray named photo. This array contains a collection of JSONObjects, each representing metadata for a single photo.

Write a method that pulls out information for each photo. Make a GalleryItem for each photo and add it to a List.

Listing 25.12  Parsing Flickr photos (FlickrFetchr.java)

public class FlickrFetchr {

    private static final String TAG = "FlickrFetchr";
    ...
    public void fetchItems() {
        ...
    }

    private void parseItems(List<GalleryItem> items, JSONObject jsonBody)
            throws IOException, JSONException {

        JSONObject photosJsonObject = jsonBody.getJSONObject("photos");
        JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");

        for (int i = 0; i < photoJsonArray.length(); i++) {
            JSONObject photoJsonObject = photoJsonArray.getJSONObject(i);

            GalleryItem item = new GalleryItem();
            item.setId(photoJsonObject.getString("id"));
            item.setCaption(photoJsonObject.getString("title"));

            if (!photoJsonObject.has("url_s")) {
                continue;
            }

            item.setUrl(photoJsonObject.getString("url_s"));
            items.add(item);
        }
    }
}

This code uses convenience methods such as getJSONObject(String name) and getJSONArray(String name) to navigate the JSONObject hierarchy. (These methods are also annotated in Figure 25.11.)

Flickr does not always return a url_s component for each image. You add a check here to ignore images that do not have an image URL.

The parseItems(…) method needs a List and JSONObject. Update fetchItems() to call parseItems(…) and return a List of GalleryItems.

Listing 25.13  Calling parseItems(…) (FlickrFetchr.java)

public void List<GalleryItem> fetchItems() {

    List<GalleryItem> items = new ArrayList<>();

    try {
        String url = ...;
        String jsonString = getUrlString(url);
        Log.i(TAG, "Received JSON: " + jsonString);
        JSONObject jsonBody = new JSONObject(jsonString);
        parseItems(items, jsonBody);
    } catch (JSONException je) {
            Log.e(TAG, "Failed to parse JSON", je);
    } catch (IOException ioe) {
        Log.e(TAG, "Failed to fetch items", ioe);
    }

    return items;
}

Run PhotoGallery to test your JSON parsing code. PhotoGallery has no way of reporting the contents of your List right now, so you will need to set a breakpoint and use the debugger if you want to make sure everything worked correctly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.91.176.3