Ilias Tsagklis

About Ilias Tsagklis

Ilias Tsagklis is a senior software engineer working in the telecom domain. He is an applications developer in a wide variety of applications/services. Ilias is co-founder and Executive Editor at Java Code Geeks.

Android Full App, Part 4: Performing the API request asynchronously from the main activity

This is the fourth 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 the second part (“Using the HTTP API”), we used the Apache HTTP client library in order to consume an external HTTP API and integrate the API’s searching capabilities into our application. In the third part (“Parsing the XML response”) we saw how to parse the XML response using Android’s built-in XML parsing capabilities. In this part, we will tie together the HTTP retriever and XML parser services in order to perform the API search request from our application’s main activity. The request will be executed asynchronously in a background thread in order to avoid blocking the main UI thread.

On mobile applications development, one very important aspect of the application’s behavior is the smooth execution. The application’s response to user input should be quick and the whole experience should be smooth and snappy. Application responsiveness is very significant especially for the Android platform and Google has published some design guidelines for that. This is the reason that we will have the application perform the searching operations in the background, meaning that those will execute in a thread other than the main UI thread.

Despite the fact that internet connection rates on mobile devices have been tremendously improved over the last years, it still remains a fact that downloading data from the internet can be a time consuming operation. Thus, we don’t want to pause the main thread while the HTTP client waits for the data to be downloaded. Also note that stalling the main thread UI for more than five seconds will cause an “Application Not Responding” (ANR) dialog to kick in and the user will be given the opportunity to kill your application. That is definitely not a feature to have.

For that purpose, we are going to leverage the Android API and use the built in class named AsyncTask. I have explained its usage in a previous post of mine (“Android Reverse Geocoding with Yahoo API – PlaceFinder”) but in short, this class allows us to properly and easily use the UI thread. From the official documentation page: “AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers”.

Before we begin writing the asynchronous code, we will first introduce some service classes which will be responsible for performing the HTTP requests, parsing the XML responses, create the corresponding model objects and returning those to the calling Activity. Those classes will extend the abstract base class named GenericSeeker with the following source code:

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

import java.net.URLEncoder;
import java.util.ArrayList;

public abstract class GenericSeeker<E> {
    
    protected static final String BASE_URL = "http://api.themoviedb.org/2.1/";    
    protected static final String LANGUAGE_PATH = "en/";
    protected static final String XML_FORMAT = "xml/";
    protected static final String API_KEY = "<YOUR_API_KEY_HERE>";
    protected static final String SLASH = "/";
    
    protected HttpRetriever httpRetriever = new HttpRetriever();
    protected XmlParser xmlParser = new XmlParser();
    
    public abstract ArrayList<E> find(String query);
    public abstract ArrayList<E> find(String query, int maxResults);

    public abstract String retrieveSearchMethodPath();
    
    protected String constructSearchUrl(String query) {
        StringBuffer sb = new StringBuffer();
        sb.append(BASE_URL);
        sb.append(retrieveSearchMethodPath());
        sb.append(LANGUAGE_PATH);
        sb.append(XML_FORMAT);
        sb.append(API_KEY);
        sb.append(SLASH);
        sb.append(URLEncoder.encode(query));
        return sb.toString();
    }
    
    public ArrayList<E> retrieveFirstResults(ArrayList<E> list, int maxResults) {
        ArrayList<E> newList = new ArrayList<E>();
        int count = Math.min(list.size(), maxResults);
        for (int i=0; i<count; i++) {
            newList.add(list.get(i));
        }
        return newList;
    }

}

The GenericSeeker class denotes that it is able to find results of a particular class and the extending classes will have to provide concrete implementations for the appropriate classes. The HttpRetriever and XmlParser objects from our previous tutorials will be used. Remember that the TMDb API uses similar URLs for movies and person searching:

Thus, we are using a common base URL and the extending classes have to provide the additional path by implementing the “retrieveSearchMethodPath” method. Two more methods have to be implemented, find(String) and find(String, int), which both return an ArrayList of objects with the appropriate class. The second one can be used in order to narrow down the total number of results. This could be helpful because the API typically return results which are not very relevant to the search query and could be discarded in order to have some performance gain. Finally, do not forget to replace the value of the API_KEY variable with a valid key from the TMDb site.

Next, we have the code for the two child classes, MovieSeeker and PersonSeeker. The classes are very similar, so I only present one here for reasons of brevity:

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

import java.util.ArrayList;

import android.util.Log;

import com.javacodegeeks.android.apps.moviesearchapp.model.Movie;

public class MovieSeeker extends GenericSeeker<Movie> {
        
    private static final String MOVIE_SEARCH_PATH = "Movie.search/";
    
    public ArrayList<Movie> find(String query) {
        ArrayList<Movie> moviesList = retrieveMoviesList(query);
        return moviesList;
    }
    
    public ArrayList<Movie> find(String query, int maxResults) {
        ArrayList<Movie> moviesList = retrieveMoviesList(query);
        return retrieveFirstResults(moviesList, maxResults);
    }
    
    private ArrayList<Movie> retrieveMoviesList(String query) {
        String url = constructSearchUrl(query);
        String response = httpRetriever.retrieve(url);
        Log.d(getClass().getSimpleName(), response);
        return xmlParser.parseMoviesResponse(response);
    }

    @Override
    public String retrieveSearchMethodPath() {
        return MOVIE_SEARCH_PATH;
    }

}

The private method “retrieveMoviesList” is the core of that class. It first constructs the URL for the API call and the executes the HTTP request using the HttpRetriever class instance. If the request is successful, the XML response is feeded to the XmlParser service which is responsible for mapping the response to the Movie model object. The find(String) and find(String, int) methods are actually wrappers to the private method.

We are now ready to use the search services from our main Activity. First we create an instance of those services as follows:

...
private GenericSeeker<Movie> movieSeeker = new MovieSeeker();
private  GenericSeeker<Person> personSeeker = new PersonSeeker();
...

As mentioned in the beginning of the article, the call to the find methods of those classes should be performed in a thread other than the UI thread. It is now time to create our AsyncTask implementation, which is the following for Movie searching:

...
private class PerformMovieSearchTask extends AsyncTask<String, Void, List<Movie>> {

   @Override
   protected List<Movie> doInBackground(String... params) {
      String query = params[0];
      return movieSeeker.find(query);
   }
   
   @Override
   protected void onPostExecute(final List<Movie> result) {         
      runOnUiThread(new Runnable() {
      @Override
      public void run() {
         if (progressDialog!=null) {
            progressDialog.dismiss();
            progressDialog = null;
         }
         if (result!=null) {
               for (Movie movie : result) {
                  longToast(movie.name + " - " + movie.rating);
               }
            }
      }
       });
   }
      
}
...

First we declare that our implementation extends the class AsyncTask>. This signature means that the type of the parameters sent to the task upon execution are of type String, that no progress units will be published during the background computation (denoted by Void) and that the result of the background computation is of type List which holds Movie objects. In the doInBackground method, we perform the actual HTTP data retrieval and then, on the onPostExecute method we present the results in the form of Toast notifications. Note that another task class is named “PerformPersonSearchTask” is also created and is used for performing person searching.

Note that a ProgressDialog widget is used in order to let the user know that data retrieval takes place and that he should be patient. The progress dialog is declared cancelable in its factory method so that the user can cancel the task upon request. The relevant code is the following:

...
private void performSearch(String query) {
        
        progressDialog = ProgressDialog.show(MovieSearchAppActivity.this,
                "Please wait...", "Retrieving data...", true, true);
        
        if (moviesSearchRadioButton.isChecked()) {
            PerformMovieSearchTask task = new PerformMovieSearchTask();
            task.execute(query);
            progressDialog.setOnCancelListener(new CancelTaskOnCancelListener(task));
        }
        else if (peopleSearchRadioButton.isChecked()) {
            PerformPersonSearchTask task = new PerformPersonSearchTask();
            task.execute(query);
            progressDialog.setOnCancelListener(new CancelTaskOnCancelListener(task));
        }
        
    }
...

We also provide an implementation for the OnCancelListener of the progress dialog with the sole purpose of canceling the relevant task. The code is the following:

...
private class CancelTaskOnCancelListener implements OnCancelListener {
        private AsyncTask<?, ?, ?> task;
        public CancelTaskOnCancelListener(AsyncTask<?, ?, ?> task) {
            this.task = task;
        }
        @Override
        public void onCancel(DialogInterface dialog) {
            if (task!=null) {
                task.cancel(true);
            }
        }
    }
...

Let’s see the results of our code so far. Use Eclipse to run the project’s configuration and launch the application. Provide a query string in the edit text and hit the button to perform the search. The progress dialog appears notifying for the request operation as in the following image:

The user is able to cancel the task at any time by hitting the “Back” button. If the operation is successful, the callback method will get triggered and a toast for each result will be presented to the user as in the following image:

This concludes the fourth part of the tutorial series. You can download here the Eclipse project created so far.

Related Articles :
Related Whitepaper:

Rapid Android Development: Build Rich, Sensor-Based Applications with Processing

Create mobile apps for Android phones and tablets faster and more easily than you ever imagined

Use 'Processing', the free, award-winning, graphics-savvy language and development environment, to work with the touchscreens, hardware sensors, cameras, network transceivers, and other devices and software in the latest Android phones and tablets.

Get it Now!  

6 Responses to "Android Full App, Part 4: Performing the API request asynchronously from the main activity"

  1. mohirl says:

    Thanks for the tutorial, really helpful so far.
    But unless I missed a step (quite possible!) there’s one small step missing – the OnClickListener for the searchButton in the main Activity needs to be changed to call performSearch(query); 
    At the moment, in my project, it still just displays the search query with a toast, but doesn’t actually do anything.

  2. Mayukh says:

    I am getting this below error

    01-08 12:39:45.685: W/HttpRetriever(302): Error for URL http://api.themoviedb.org/2.1/Movie.search/en/xml/09018f62a712fb9286a26450752e8162/Inception

    Though when the same url is hit from browser , able to connect and see the xml.
    Any idea?

  3. Raj says:

    I followed this tutorial keenly upto this part, but now have a problem that themoviedb.org api has moved to version 3 of their api. Would your tutorial undergo any changes to accommodate for this api change. The new thing appears to be a handshake for authentication.

  4. Adarsh says:

    Just like Raj i followed till here n found out the TMDB api have changed drastically they dont support XML anymore they have Json and Jsonp as response format.

  5. Asad says:

    will grateful if i get full source code

  6. cheedep says:

    For anyone interested, I have the sample with the latest API at
    https://github.com/cheedep/PhilmSearchApp

Leave a Reply


× 2 = twelve



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close