Async HTTP Connections on Android

Posted by on Apr 5, 2010 in Blog, Technical Articles | 25 comments

For a pleasant Android user experience it is vital for applications to actively provide feedback. When downloading images and data from the web, delays are expected. Developers should implement asynchronous connections and provide active feedback on their progress.

Throttling connections is another important technique. Android phones are mainly on 3G and Edge connections and are prone to slow connection speeds. Imagine downloading 100 images from the web simultaneously at 1 KB/second each. That certainly doesn’t make for the greatest user experience. Executing connections in smaller chunks is a much better option. For example, downloading those 100 images I mentioned earlier in chunks of 5 connections would allow for a comparable 20 KB/second rate.

Below is an example of my asynchronous HTTP connection implementation using the classes HttpConnection and ConnectionManager. It uses Apache’s HttpClient methods and allows for GET, POST, PUT and DELETE requests. A built in Bitmap decoder is also included.

To receive status updates from an HttpConnection a Handler is used (but not required). The HttpConnection will dispatch a message to the Handler at the occurrence of the following:

  • HttpConnection.DID_START – The connection is removed from the ConnectionManager’s queue and started
  • HttpConnection.DID_SUCCEED – The connection was successful and the response is stored in the Message instance obj field
  • HttpConnection.DID_ERROR – The connection failed and the exception is stored in the Message instance obj field


Here is an implementation of a Handler:

Handler handler = new Handler() {
  public void handleMessage(Message message) {
    switch (message.what) {
    case HttpConnection.DID_START:
      text.setText("Starting connection...");
      break;
    case HttpConnection.DID_SUCCEED:
      String response = (String) message.obj;
      text.setText(response);
      break;
    case HttpConnection.DID_ERROR:
      Exception e = (Exception) message.obj;
      e.printStackTrace();
        text.setText("Connection failed.");
    break;
    }
  }
};

Now that we have a Handler we can create an HttpConnection. Here is the one line of code doing so:

new HttpConnection(handler).get("http://twitter.com/statuses/user_timeline/69177017.rss");

Download this sample project

Sample Implementation

public void downloadTwitterIcon() {
  Handler handler = new Handler() {
    public void handleMessage(Message message) {
      switch (message.what) {
      case HttpConnection.DID_START: {
        Log.d("Twitter Icon", "Starting Connection");
        break;
      }
      case HttpConnection.DID_SUCCEED: {
        Bitmap response = (Bitmap) message.obj;
       icon.setImageBitmap(response);
       break;
      }
      case HttpConnection.DID_ERROR: {
        Exception e = (Exception) message.obj;
        e.printStackTrace();
        break;
      }
    }
  }
 };
 new HttpConnection(handler)
    .bitmap("http://a1.twimg.com/profile_images/398455304/gvsu-cis-avatar_bigger.jpg");
}

public void downloadTwitterStream() {
  Handler handler = new Handler() {
      public void handleMessage(Message message) {
        switch (message.what) {
        case HttpConnection.DID_START: {
          text.setText("Starting connection...");
          break;
        }
        case HttpConnection.DID_SUCCEED: {
          String response = (String) message.obj;
          text.setText(response);
          break;
        }
        case HttpConnection.DID_ERROR: {
          Exception e = (Exception) message.obj;
          e.printStackTrace();
          text.setText("Connection failed.");
          break;
        }
      }
    }
  };
  new HttpConnection(handler)
    .get("http://twitter.com/statuses/user_timeline/69177017.rss");
}

HttpConnection.java [download]

package edu.gvsu.masl.asynchttp;

import java.io.*;

import org.apache.http.*;
import org.apache.http.client.*;
import org.apache.http.client.methods.*;
import org.apache.http.entity.*;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;

import android.graphics.*;
import android.os.*;

/**
 * Asynchronous HTTP connections
 * 
 * @author Greg Zavitz & Joseph Roth
 */
public class HttpConnection implements Runnable {

	public static final int DID_START = 0;
	public static final int DID_ERROR = 1;
	public static final int DID_SUCCEED = 2;

	private static final int GET = 0;
	private static final int POST = 1;
	private static final int PUT = 2;
	private static final int DELETE = 3;
	private static final int BITMAP = 4;

	private String url;
	private int method;
	private Handler handler;
	private String data;

	private HttpClient httpClient;

	public HttpConnection() {
		this(new Handler());
	}

	public HttpConnection(Handler _handler) {
		handler = _handler;
	}

	public void create(int method, String url, String data) {
		this.method = method;
		this.url = url;
		this.data = data;
		ConnectionManager.getInstance().push(this);
	}

	public void get(String url) {
		create(GET, url, null);
	}

	public void post(String url, String data) {
		create(POST, url, data);
	}

	public void put(String url, String data) {
		create(PUT, url, data);
	}

	public void delete(String url) {
		create(DELETE, url, null);
	}

	public void bitmap(String url) {
		create(BITMAP, url, null);
	}

	public void run() {
		handler.sendMessage(Message.obtain(handler, HttpConnection.DID_START));
		httpClient = new DefaultHttpClient();
		HttpConnectionParams.setSoTimeout(httpClient.getParams(), 25000);
		try {
			HttpResponse response = null;
			switch (method) {
			case GET:
				response = httpClient.execute(new HttpGet(url));
				break;
			case POST:
				HttpPost httpPost = new HttpPost(url);
				httpPost.setEntity(new StringEntity(data));
				response = httpClient.execute(httpPost);
				break;
			case PUT:
				HttpPut httpPut = new HttpPut(url);
				httpPut.setEntity(new StringEntity(data));
				response = httpClient.execute(httpPut);
				break;
			case DELETE:
				response = httpClient.execute(new HttpDelete(url));
				break;
			case BITMAP:
				response = httpClient.execute(new HttpGet(url));
				processBitmapEntity(response.getEntity());
				break;
			}
			if (method < BITMAP)
				processEntity(response.getEntity());
		} catch (Exception e) {
			handler.sendMessage(Message.obtain(handler,
					HttpConnection.DID_ERROR, e));
		}
		ConnectionManager.getInstance().didComplete(this);
	}

	private void processEntity(HttpEntity entity) throws IllegalStateException,
			IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(entity
				.getContent()));
		String line, result = "";
		while ((line = br.readLine()) != null)
			result += line;
		Message message = Message.obtain(handler, DID_SUCCEED, result);
		handler.sendMessage(message);
	}

	private void processBitmapEntity(HttpEntity entity) throws IOException {
		BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity);
		Bitmap bm = BitmapFactory.decodeStream(bufHttpEntity.getContent());
		handler.sendMessage(Message.obtain(handler, DID_SUCCEED, bm));
	}

}

ConnectionManager.java [download]

package edu.gvsu.masl.asynchttp;

import java.util.ArrayList;

/**
 * Simple connection manager to throttle connections
 * 
 * @author Greg Zavitz
 */
public class ConnectionManager {
	
	public static final int MAX_CONNECTIONS = 5;

	private ArrayList<Runnable> active = new ArrayList<Runnable>();
	private ArrayList<Runnable> queue = new ArrayList<Runnable>();

	private static ConnectionManager instance;

	public static ConnectionManager getInstance() {
		if (instance == null)
			instance = new ConnectionManager();
		return instance;
	}

	public void push(Runnable runnable) {
		queue.add(runnable);
		if (active.size() < MAX_CONNECTIONS)
			startNext();
	}

	private void startNext() {
		if (!queue.isEmpty()) {
			Runnable next = queue.get(0);
			queue.remove(0);
			active.add(next);

			Thread thread = new Thread(next);
			thread.start();
		}
	}

	public void didComplete(Runnable runnable) {
		active.remove(runnable);
		startNext();
	}

}

  

25 Responses to “Async HTTP Connections on Android”

  1. After searching all over the net for an ‘easy to understand’ implementation of HtppClient I finally found this. This is really easy to understand and works like a charm. Hats Off!! Thanks a million.

  2. thank you sooooo much for this. really helpful

  3. Thanks mate, I was crazy looking for an easy way of doing it and this is exactly what I needed!!

  4. really awesome

  5. Thank you. This is what I was looking for, or at least pretty close to it :)

  6. Greetings,

    Great piece of code. How do you send parameters with a .post(url,data)?

    I’ve tried this:

    new HttpConnection(handler).post(“https://mydomain.com/login.php,”uname=”+uname+”&pword=”+pword);

    But it doesn’t seem to work?

    Many thanks,

  7. James Murran says:

    Greetings, I’m also having the same problems with the POST.
    How do you specify post parameters?

  8. Indeed a very good test application. This clearly makes me understand how to do it. :)
    Thanks a ton.

  9. Such A nice example.. Thanks dude.

  10. Hi… @all

    help me

    I first met with android application
    I can a problem when creating client server
    can you help me please

    can you share sample project
    example : application android Login in Server (php and mysql) and then view data in list view

    thanks alot

  11. Is this code example available for free use?

  12. I see nothing in the code that indicates any true asynchronous downloading. All I see are messages for the start and end. If you’re downloading a file and you want to indicate the progress of download (10%, 20%, etc.) there should be a callback that gets called. This is missing from the code sample.

  13. Hi, This helped me lot for my project. Great and simple..

  14. James Smith says:

    Check out the awesome Android Asynchronous Http Client library, which does all of this for you automatically!

  15. Thank you SO much for this. I would have spent days writing something that isn’t even half as good. Again, thank you!

  16. Thank you!

  17. Wonderful, glad i found this :)
    The reason i was brought here, i was using Asynchttp for android to read xml and i had some issues handling UTF content and there was no proper support page for asynchttp. Your method worked perfectly well even for UTF xml

    Thanks

  18. Jeffrey says:

    We could also use android’s own AsyncTask

  19. Nice work, thanks for sharing this. But I have one question: Will the threads be closed if the reqeust is completed or are the threads are only removed from the active list?

    Best regards
    Andy

  20. Andy Davies says:

    Same for me .post(url, “data=test”);

    Doesn’t appear to work

  21. Thx, GREAT!

    @Andy: to post data try this:

    case POST:
    List nameValuePairs = new ArrayList(2);
    nameValuePairs.add(new BasicNameValuePair("id", data));
    nameValuePairs.add(new BasicNameValuePair("data", moredata));
    UrlEncodedFormEntity entity = new UrlEncodedFormEntity(nameValuePairs);
    entity.setContentEncoding(HTTP.ISO_8859_1);
    entity.setContentType("application/x-www-form-urlencoded");

    HttpPost httpPost = new HttpPost(url);
    //httpPost.setEntity(new StringEntity(data));
    httpPost.setEntity(entity);
    response = httpClient.execute(httpPost);
    break;

  22. I changed the post case to this:


    case POST:
    HttpPost httpPost = new HttpPost(url);

    // Add your data
    //Split the data
    String[] aData = data.split("\\&");

    List nameValuePairs = new ArrayList(aData.length);
    for (int x = 0; x < aData.length; x++){
    String[] aVal = aData[x].split("\\=");
    String name = aVal[0];
    String value = aVal[1];
    nameValuePairs.add(new BasicNameValuePair(name, value));
    }
    httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

    //httpPost.setEntity(new StringEntity(data));
    response = httpClient.execute(httpPost);
    break;

  23. Man great code! but after I download the images bufHttpEntity.getContent() and decode into a file I don’t have permission to read the file any ideas?

  24. Hi ,

    I have used the same code to send http request tomy server in PC . It is working fine with Wifi, but if I connect to mobile internet request is not going . Can anyone suggest me a solution for this on Android.

Leave a Reply