Android Core

Android Full App, Part 2: Using the HTTP API

This is the second part of the “Android Full Application Tutorial” series. The complete application aims to provide an easy way of performing movies/actors searching over the internet. In the first part of the series (“Main Activity UI”), we created the Eclipse project and set up a basic interface for the main activity of the application. In this part we are going to see how to consume an external HTTP API and how to integrate the API’s searching capabilities into our application.

For the movies and actor look-ups we will be using the TMDb API. From the official site:
“The TMDb API is a powerful resource for any developers that want to integrate movie & cast data along with posters or movie fanart. All of the API methods are available in XML, YAML and JSON.”

As with most available APIs, you are going to need a valid key in order to be able to use the API. The first step for that is to create a free account at TMDb’s Sign-Up page. After signing up, log into your account and find the link for generating an API key.

The list of the available API methods can be found at the TMDb API documentation page and the most important ones are the following:

  • Movie.search: Provides the easiest and quickest way to search for a movie.
  • Person.search: Is used to search for an actor, actress or production member.

For movies search, the example URL is the following:

http://api.themoviedb.org/2.1/Movie.search/en/xml/APIKEY/Transformers

For people search, the example URL is the following:

http://api.themoviedb.org/2.1/Person.search/en/xml/APIKEY/Brad+Pitt

(where APIKEY has to be replaced with a valid API key)

As you can see, the API is pretty easy and straightforward to use. It only involves performing HTTP GET requests at a specific URL and then retrieving the responses into a predefined format.

Next, we are going to see how to leverage Android’s networking capabilities in order to consume the API and provide a presentation of the provided data. Note that we will use the XML format for the responses, but this will be showcased in a next tutorial.

For manipulating HTTP requests/responses in an Android environment, the standard classes from the java.net package can be used. Thus, classes such as URL, URLConnection, HttpURLConnection etc., can all be used in the known way. However, you can avoid dealing with the low level details by using the Apache HTTP Client libraries. This library is based in the well known Apache Commons HTTP Client framework.

Let’s get started with the code. We will create a class named “HttpRetriever” which will be responsible for performing all the HTTP requests and will return the responses both in text format and as a stream (for image manipulation). The code for this class is the following:

package com.javacodegeeks.android.apps.moviesearchapp.services;

import java.io.IOException;
import java.io.InputStream;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

import com.javacodegeeks.android.apps.moviesearchapp.io.FlushedInputStream;
import com.javacodegeeks.android.apps.moviesearchapp.util.Utils;

public class HttpRetriever {
   
   private DefaultHttpClient client = new DefaultHttpClient();   
   
   public String retrieve(String url) {
      
        HttpGet getRequest = new HttpGet(url);
        
      try {
         
         HttpResponse getResponse = client.execute(getRequest);
         final int statusCode = getResponse.getStatusLine().getStatusCode();
         
         if (statusCode != HttpStatus.SC_OK) { 
            Log.w(getClass().getSimpleName(), "Error " + statusCode + " for URL " + url); 
            return null;
         }
         
         HttpEntity getResponseEntity = getResponse.getEntity();
         
         if (getResponseEntity != null) {
            return EntityUtils.toString(getResponseEntity);
         }
         
      } 
      catch (IOException e) {
         getRequest.abort();
         Log.w(getClass().getSimpleName(), "Error for URL " + url, e);
      }
      
      return null;
      
   }
   
   public InputStream retrieveStream(String url) {
      
      HttpGet getRequest = new HttpGet(url);
        
      try {
         
         HttpResponse getResponse = client.execute(getRequest);
         final int statusCode = getResponse.getStatusLine().getStatusCode();
         
         if (statusCode != HttpStatus.SC_OK) { 
            Log.w(getClass().getSimpleName(), "Error " + statusCode + " for URL " + url); 
            return null;
         }

         HttpEntity getResponseEntity = getResponse.getEntity();
         return getResponseEntity.getContent();
         
      } 
      catch (IOException e) {
         getRequest.abort();
         Log.w(getClass().getSimpleName(), "Error for URL " + url, e);
      }
      
      return null;
      
   }
   
   public Bitmap retrieveBitmap(String url) throws Exception {
      
      InputStream inputStream = null;
      try {
         inputStream = this.retrieveStream(url);
         final Bitmap bitmap = BitmapFactory.decodeStream(new FlushedInputStream(inputStream));
         return bitmap;
      } 
      finally {
         Utils.closeStreamQuietly(inputStream);
      }
      
   }

}

For the actual execution of the HTTP requests, we are using an instance of the DefaultHttpClient class, which, as its name implies, is the default implementation of an HTTP client, i.e. the default implementation of the HttpClient interface. We also use the HttpGet class (in order to represent a GET request) and provide the target URL for its constructor argument. The HTTP client executes the request and provides an HttpResponse object which contains the actual server response along with any other information. For example, we can retrieve the response status code and compare it against the code for successful HTTP requests (HttpStatus.SC_OK). For successful requests, we take reference of the enclosed HttpEntity object and from that we have access to the actual response data. For textual responses we convert the entity to a String using the static toString method of the EntityUtils class. If we wish to retrieve the data as a byte stream (for example in order to handle binary downloads), we use the getContent method of the HttpEntity class, which creates a new InputStream object of the entity.

Note that there is also a third method for directly returning Bitmap objects. This will be helpful at the later parts of the tutorial series, where we will be downloading images from the internet. In that method, we execute the GET request and retrieve an InputStream as usual. Then, we use the decodeStream method of the BitmapFactory class to create a new Bitmap object. Note that we do not directly provide the downloaded InputStream, but we first wrap it with a FlushedInputStream class. As mentioned at the official Android developers blog post, there is a bug in the previous versions of the decodeStream method that may cause problems when downloading an image over a slow connection. The custom class FlushedInputStream, which extends FilterInputStream, is used instead in order to fix the problem. The code for that class is the following:

package com.javacodegeeks.android.apps.moviesearchapp.io;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

public class FlushedInputStream extends FilterInputStream {
 
    public FlushedInputStream(InputStream inputStream) {
        super(inputStream);
    }

    @Override
    public long skip(long n) throws IOException {
        long totalBytesSkipped = 0L;
        while (totalBytesSkipped < n) {
            long bytesSkipped = in.skip(n - totalBytesSkipped);
            if (bytesSkipped == 0L) {
                  int b = read();
                  if (b < 0) {
                      break;  // we reached EOF
                  } else {
                      bytesSkipped = 1; // we read one byte
                  }
           }
            totalBytesSkipped += bytesSkipped;
        }
        return totalBytesSkipped;
    }
    
}

This ensures that skip() actually skips the provided number of bytes, unless we reach the end of file. Finally, we use the closeStreamQuietly method of a custom Utils class in order to handle exceptions which might occur when closing an InputStream. The code is as follows:

package com.javacodegeeks.android.apps.moviesearchapp.util;

import java.io.IOException;
import java.io.InputStream;

public class Utils {
 
 public static void closeStreamQuietly(InputStream inputStream) {
   try {
   if (inputStream != null) {
        inputStream.close();  
    }
  } catch (IOException e) {
   // ignore exception
  }
 }

}

Finally, in order to be able to perform HTTP requests the corresponding permission has to be granted. Thus, add the android.permission.INTERNET to the project’s AndroidManifest.xml file which now is as follows:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.javacodegeeks.android.apps.moviesearchapp"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MovieSearchAppActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
    <uses-sdk android:minSdkVersion="3" />
 <uses-permission android:name="android.permission.INTERNET"></uses-permission>
</manifest> 

So, we have prepared the infrastructure for executing HTTP GET requests. At the following tutorials, we will use that in order to retrieve XML data and images for our application’s needs. You can download here the Eclipse project created so far.

Related Articles :

Ilias Tsagklis

Ilias is a software developer turned online entrepreneur. He is co-founder and Executive Editor at Java Code Geeks.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

6 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Mangomates
Mangomates
12 years ago

Im going over this now, lines 17 and 18 do not compile for me, and while i believe this is due to the name of the projects, renaming them to the same as my own doesnt work. Im also making a new class file, and see theres also  “services” at the top of this one, not on the first tutorial?

jalopyhead
jalopyhead
12 years ago

It seems like we’re missing a step or something. You say “Let’s get started with the code. We will create a class named “HttpRetriever”. But where do we do that? Is it a new package or what?

poonab
poonab
11 years ago

Hi, im getting “in” is an unknown entity on line 17 of flushedinputstream?

Any ideas?

Owen
Owen
11 years ago

Hi Ilias,

I, have been programming Android for the last 10 months, but I have never used Web or internet services until now, so I am new to this. I have only just stumbled on you great tutorial and find it excellent! well done. My current problem is the error message “.DefaultRequestDirector(11207): Authentication error: Unable to respond to any of these challenges: {}” I don’t understand the cause, ho how to fix it. Can you possible help?

HavranPírko Freya
10 years ago

Hey,
any updates on this part regarding their new API version?

I’d be very thankful if you could provide me with the changed URLs :)

cheedep
cheedep
9 years ago

Anyone still interested, I have the updated sample at https://github.com/cheedep/PhilmSearchApp

Back to top button