A basic HTTP client for Android (and more)
Posted by David Chandler on June 12, 2012
TL;DR http://code.google.com/p/basic-http-client/
Last year, the Android team recommended the use of java.net.HttpURLConnection instead of Apache HttpClient for Gingerbread and up. Unfortunately, HttpURLConnection is a lower-level API, so now everyone has to do their own URL encoding, set MIME type application/x-www-form-urlencoded, read InputStream and ErrorStream, wrap with try/catch, finally close streams, etc.
Rather than write all that code one-off for each request, I’ve created a basic HTTP client which lets you easily
- make GET, POST, PUT, and DELETE requests
- make asynchronous requests with automatic retries and exponential backoff
- customize requests using a RequestHandler
- automatically wrap requests in an AsyncTask (Android-specific)
It has a very simple API, and the jar weighs in at only 40kB (basic) or 45kb (Android), including source. It is Android-independent by design, although Android developers will find it especially useful.
Basic usage
// Example code to login to App Engine dev server
public void loginDev(String userEmail) {
BasicHttpClient httpClient = new BasicHttpClient("http://localhost:8888");
ParameterMap params = httpClient.newParams()
.add("continue", "/")
.add("email", userEmail)
.add("action", "Log In");
httpClient.addHeader("someheader", "value");
httpClient.setConnectionTimeout(2000); // 2s
HttpResponse httpResponse = httpClient.post("/_ah/login", params);
System.out.println(httpResponse.getBodyAsString());
}
// Example code to log in to App Engine production app with an auth token
// from Android's AccountManager
public void loginProd(String authToken) {
BasicHttpClient httpClient = new BasicHttpClient("http://localhost:8888");
ParameterMap params = httpClient.newParams()
.add("auth", authToken);
HttpResponse httpResponse = httpClient.get("/_ah/login", params);
}
Make an asynchronous request
AndroidHttpClient httpClient = new AndroidHttpClient("http://192.168.1.1:8888");
httpClient.setMaxRetries(5);
ParameterMap params = httpClient.newParams()
.add("continue", "/")
.add("email", "test@example.com")
.add("action", "Log In");
httpClient.post("/_ah/login", params, new AsyncCallback() {
@Override
public void onComplete(HttpResponse httpResponse) {
System.out.println(httpResponse.getBodyAsString());
}
@Override
public void onError(Exception e) {
e.printStackTrace();
}
});
To make an asynchronous request, you invoke the request methods (get, post, etc.) with an additional argument, an AsyncCallback, which can be easily implemented in an anonymous inner class as shown above. Here’s a quick tour of the various clients you can use.
BasicHttpClient
BasicHttpClient has no Android dependencies so it can be used on any platform which provides java.net.HttpURLConnection. It offers a synchronous interface only via get, post, put, and delete methods. Note that there are two post methods, one for posting form data (a ParameterMap) and one for uploading content of any type you specify. If you prefer, you can make a new HttpGetRequest and execute() it instead of using the get() method. In fact, all the request methods do this under the covers.
AndroidHttpClient
AndroidHttpClient extends both AbstractHttpClient and AsyncHttpClient so it can used synchronously or asynchronously. Remember that you should not invoke the immediate (non-async) methods on the UI thread. Android will throw an exception if you do. Besides automatically wrapping requests in an AsyncTask via the async methods, AndroidHttpClient automatically includes the workaround for a major bug in HttpURLConnection in earlier versions of Android as discussed in the Android Developers blog post above.
Cookie management
AbstractHttpClient (the base class for everything) registers a default java.net.CookieManager that is used by all HttpURLConnection requests in your VM (= app on Android). The default CookieManager acts just like a browser, remembering cookies it receives and sending them on subsequent requests. You shouldn’t need to set or read cookie headers manually at all. In fact, you can’t. When the CookieManager is in use, it consumes the cookie headers.
Under the covers
AbstractHttpClient is the base client from which all the others flow. It implements a synchronous API and can be instantiated using an anonymous inner class if you just want the base functionality: new AbstractHttpClient() {}.
AsyncHttpClient adds an asynchronous API where each request method takes an extra callback argument. It is intended to be extended like AndroidHttpClient. Subclasses should provide a platform-specific wrapper for async requests. AndroidHttpClient provides an AsyncTask wrapper that AsyncHttpClient uses to make requests in the background.
BasicHttpClient and AndroidHttpClient both delegate the actual I/O to a BasicRequestHandler, which implements a simple request lifecycle (open, prepare, write, read, error). You can construct any of the clients with a custom RequestHandler. BasicRequestHandler is abstract so you can easily override only one method using an anonymous inner class. This is shown on the project home page. A common use for this may be to provide your own onError() method which determines whether a given error condition is recoverable for your application.
Testing
There are no automated tests yet because I haven’t made the effort to simulate connections, timeouts, etc. I have done considerable manual testing, however, of the async capabilities with retries. The basic recipe is as follows:
- Try an async request to an unreachable IP address (say, 10.0.0.1 or 192.168.1.1). This lets you observe connection timeouts and retries.
- Copy the SleepServlet in the test package to a Web project and add the servlet mapping in web.xml. Start the server and try an async request. This lets you observe read timeouts and retries.
- Try an async request to a functioning URL and observe success.
Summary
The code is copiously commented with hopefully non-trivial observations about function and intended use. Planned enhancements include a MappingJsonClient which can take POJO arguments instead of raw bytes or Strings. Spring for Android already has a nice RestTemplate, but like Apache HttpClient, is fairly heavyweight on account of its configurability.
Enjoy!

jessedavidbrucewilson said
I’m the author of the Android Developers’ Blog post. This is nice work!
Does it enable the HTTP response cache on Android 4.0? Craig Andrews did a backport here:
https://github.com/candrews/HttpResponseCache
Also take a look at MockWebServer, which makes unit testing this kind of code easy.
http://code.google.com/p/mockwebserver/
David Chandler said
Thanks very much, Jesse. No response caching yet. I’ll look into it. Thanks for the pointers.
Anatoly said
what if someone develops an application for froyo and up, so according to the blog you mentioned in text, are we supposed to switch on algorithm depending on platform version?
or we just have to deside what is more suitable for the app ( HttpURLConnection feautures or some kind of unknown, or known bugs of httpclient) ? What do you think?
jessedavidbrucewilson said
If you’re writing code for Froyo and above, your best bet is HttpURLConnection. Just use the blog posts’ disableConnectionReuseIfNecessary() workaround to avoid Froyo-only bugs.
David Chandler said
Hmmm, the blog post I referenced is conflicted. It recommends HttpURLConnection for Gingerbread and up, but elsewhere says “Prior to Froyo, HttpURLConnection had some frustrating bugs.” The worst bug pre-Froyo was presumably the one mentioned in that article, and I’ve included the workaround for it in my code, so you should be covered there. From peeking around StackOverflow, it looks like a lot of the older bugs in HttpURLConnection have to do with things like chunked input streaming that basic-http-client doesn’t use, anyway. But… I’ve only tested on ICS.
Let me add that I think Apache HttpClient remains a solid all-around choice. I wrote basic-http-client mainly because I wanted something really lightweight with a really simple API. It’s not intended to be a one-size-fits-all HTTP client. I haven’t used buffered readers or writers or chunked input streaming and haven’t tested with large data at all. But it beats writing the same 20 lines of code over and over to use HttpURLConnection directly, especially with the AsyncTask wrapper.
Should you wish to switch between HttpURLConnection and Apache based on Android version, the Spring for Android project does this automatically. The Spring RestTemplate is super easy.
David Chandler said
Besides Apache HttpClient, a couple other libraries of interest:
Google HTTP Java Client is a solid, full-featured client written by some of my favorite Java programmers at Google and is Android-compatible.
JAX-RS 2.0 Client API looks promising.
I probably should have called this minimal-http-client because that’s really my intent. I do think the async calling mechanism is really cool (and haven’t seen it elsewhere). That’s no doubt the GWT programmer coming out in me.
Weedlord Bonerhitler (@WeedBonerHitler) said
Spring has a really nice rest client for android. I haven’t used it yet, but I trust pretty much anything put out by springsource. those guys really seem to know how to build an api.
rowntreerob said
http://code.google.com/p/httpclientandroidlib/
IMO the above link is more up-to-date than your ‘apache httpclient’ link
geezenslaw said
So which one should be used and for what reasons?
Tutorial: Requesting RESTful Web Services with JSON in your Android App « Ingo Hofmann's Blog said
[...] to have a look at their tutorial and how to set up a more stable client in your App. Also check David Chandler’s BasicHttpClient which he considers as simpler to use because it already implements all basic operations such as URL [...]
geezenslaw said
Hi David, I created a new Android project using your LoginDev example in my Intellij and dropped in the jar. No unresolved references. All looked good except httpresponse returns NULL! No exceptions or logs to examine. I used my public blog url that I have in the past succesfully logged into using Apache HttpClient. Any ideas as how to debug this? Pleas advise, David.
David Chandler said
Nothing in logcat? By default, you should see the request at INFO level.
geezenslaw said
Hi David, pls excuse the top post but I insert a couple of Log.d’s:
Log.d(TAG, params.get(“j_username”)); Log.d(TAG, params.get(“j_password”));
The params seem to be satisfied correctly but yet don’t show up in the request URL.
A mystery.
-David.
geezenslaw said
Hi David, I have tried the AndroidHttpClient example instead and it is working with the same params.
I suspect the issue w/ the Basic client is running it on the main UI thread.
Thanks for the nice API!
geezenslaw said
Hi David, I am not able to use ArrayAdapter for a ListView object inside of onComplete().
The compile time complaint is: Cannot resolve constructor ‘ArrayAdapter’(com.turbomanage.httpclient.AsyncCallback, int, int, String[]) using for the context.
If I try to use ArrayAdapter and ListView outside of the onComplete() the String[] array data is not available.
Does it make sense to get onComplete() to return a String[] array?
David Chandler said
David,
It looks you’re trying to construct an ArrayAdapter with an AsyncCallback. Instead, onComplete() should call a method in your Activity and pass it the HttpResponse or response body. The Activity method can then create or update an ArrayAdapter.
HTH, /dmc
geezenslaw said
Hi David, thanks for the speedy reply and pls excuse the noise.
U r rite! I created a local private method and all is well. (doh!).
lindo said
I use and contribute to https://github.com/loopj/android-async-http for all my http needs
Kevin Galligan (@kpgalligan) said
Hey Dave. We met at a meetup in NY in Sep. I help run the meetup group. You mentioned this project, which I finally tracked down. Just the kind of thing I’ve been looking for. I’ve had an open source project in a state of partial completion for a year or so. It facilitates offline editing and syncing for Android. One of the big problems was distinguishing between “hard” exceptions, and simple temporary network issues. I added an extension of the BasicHttpClient to better handle that. Result: problem (basically) solved. All future projects will include your http project. Seems crazy not to.
If you get some free time, take a look at the offline sync bus and give feedback:
http://touchlabblog.tumblr.com/post/33710233787/offline-sync-queue-aka-superbus
https://github.com/touchlab/Superbus
The docs in the blog have horrible formatting. It was a copy/paste from confluence. Will edit given time. Had a 12 hour day at new client, so my tolerance for doc editing is at an all time low
Short intro. To enable offline data editing and sync, the project provides a “command bus”. Commands are abstract classes. The implementation is the “work” needed to be done, basically. The bus provides the serialization and proper ordering and management. Most sync we’ve done prior was “state based”. Set an “updated” flag on an entity. Simple, but problematic for a number of reasons. This project attempts to solve that problem.
Its a hard “sell”. Its one of those things you don’t “get” until you need it, but its been in several of our projects to date, although the current state is far more robust than older ones.
Anyway, dig the http project!
Jannie Theunissen said
Thanks a lot for this, David.
When I try to implement your client, my app fails with:
“Could not find class ‘java.net.CookieManager’, referenced from method com.turbomanage.httpclient.AbstractHttpClient.ensureCookieManager”
What am I missing?
Jannie Theunissen said
Oops! Sorted. I was building on Android 2.2 (API 8)
David Chandler said
Thought so
If you don’t need the CookieManager, you can just comment it out and it should otherwise work on API 8.
humsuplow said
I am not sure on this, but I am having some trouble with https secure request. How do we handle secure request?
David Chandler said
I haven’t tested specifically with https. What happens if you put https://in the URL? Any clues in logcat?
zgzong said
12-19 14:11:57.291: W/System.err(10303): java.net.SocketTimeoutException: Read timed out
12-19 14:11:57.296: W/System.err(10303): at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_read(Native Method)
12-19 14:11:57.296: W/System.err(10303): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLInputStream.read(OpenSSLSocketImpl.java:671)
12-19 14:11:57.296: W/System.err(10303): at libcore.io.Streams.readSingleByte(Streams.java:41)
12-19 14:11:57.301: W/System.err(10303): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLInputStream.read(OpenSSLSocketImpl.java:655)
12-19 14:11:57.301: W/System.err(10303): at libcore.io.Streams.readAsciiLine(Streams.java:201)
12-19 14:11:57.301: W/System.err(10303): at libcore.net.http.HttpEngine.readResponseHeaders(HttpEngine.java:544)
12-19 14:11:57.301: W/System.err(10303): at libcore.net.http.HttpEngine.readResponse(HttpEngine.java:784)
12-19 14:11:57.306: W/System.err(10303): at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:274)
12-19 14:11:57.306: W/System.err(10303): at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:479)
12-19 14:11:57.311: W/System.err(10303): at libcore.net.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:133)
12-19 14:11:57.311: W/System.err(10303): at com.turbomanage.httpclient.AbstractHttpClient.writeOutputStream(AbstractHttpClient.java:311)
12-19 14:11:57.311: W/System.err(10303): at com.turbomanage.httpclient.AbstractHttpClient.doHttpMethod(AbstractHttpClient.java:224)
12-19 14:11:57.311: W/System.err(10303): at com.turbomanage.httpclient.AsyncHttpClient.tryMany(AsyncHttpClient.java:185)
12-19 14:11:57.316: W/System.err(10303): at com.turbomanage.httpclient.android.DoHttpRequestTask.doInBackground(DoHttpRequestTask.java:40)
12-19 14:11:57.316: W/System.err(10303): at com.turbomanage.httpclient.android.DoHttpRequestTask.doInBackground(DoHttpRequestTask.java:1)
12-19 14:11:57.316: W/System.err(10303): at android.os.AsyncTask$2.call(AsyncTask.java:264)
12-19 14:11:57.321: W/System.err(10303): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
12-19 14:11:57.321: W/System.err(10303): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
12-19 14:11:57.321: W/System.err(10303): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:208)
12-19 14:11:57.321: W/System.err(10303): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
12-19 14:11:57.326: W/System.err(10303): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
12-19 14:11:57.326: W/System.err(10303): at java.lang.Thread.run(Thread.java:856)
David Chandler said
Timeouts are not uncommon on the Web. If you think it’s a bug in basic-http-client, please note the version and file in the issue tracker.
zgzong said
It seems doesn’t support post json raw data. “application/json”
David Chandler said
You can use post(String path, String contentType, byte[] data) to post raw content of any type.
zgzong said
thanks for your quick reply. It works fine and read time out issue seems caused by slow internet connection. I will keep you informed when I found out what caused that timeout issue.
Janna said
AnroidHttpClient is missing a ‘d’.
David Chandler said
fixed, thx
Benny Khoo said
Hi David,
This is a beautifully designed framework for using HttpUrlConnection. I am looking forward to incorporate it in my app. I still find that there are quite some coding to post upload file request. I am wondering how this can be done using this framework.
David Chandler said
Thanks, Benny. Can you use AbstractHttpClient.post(String path, String contentType, byte[] data)?
Mohd said
Hi David..
I am new to android development and implementing your framework for my final year project. I am trying to post json object and calling AsyncTask on the same time but cant figure up which post class should i use.
David Chandler said
I think you’ll want to use AsyncHttpClient.post(String path, String contentType, byte[] data, AsyncCallback callback).
Federico said
Hello Mr. David Chandler,
I have some problem using this library. When I use the post method – in particular, the post method with the async callback – of an AndroidHttpClient object, it seems that the method startActivity(new Intent(FromActivity.this, ToActivity.class)) doesn’t work. The connection works perfectly, only the method startActivity within the onComplete method doesn’t work. This is my code snippet
AndroidHttpClient request = new AndroidHttpClient(
stringURL);
request.setMaxRetries(5);
ParameterMap paramMap = new ParameterMap();
paramMap.add(“DATA”, json_req);
request.post(“”, paramMap, new AsyncCallback() {
@Override
public void onComplete(HttpResponse httpResponse) {
try {
JSONObject resp = JsonCR2.readRequest(response.getBodyAsString());
if (resp.get(“response”).toString().equalsIgnoreCase(“success”)) {
startActivity(new Intent(this, IndexActivity.class));
} else {
//some error
}
} catch (ParseException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
});
Can you explain me where I wrong? Thanks.
David Chandler said
Hi Federico, it should be possible to start an Activity from the callback since the callback’s onComplete() method is invoked within the AsyncTask’s onPostExecute() method, which runs on the UI thread. I suspect the issue has to do with passing the correct Context. In your example, is this code located in an Activity? Is any exception being thrown?
Federico said
Yes, the code is within the method loginService from the main activity of the project. This method runs when a button is clicked. It seems that no exceptions are thrown
David Chandler said
You might try getApplicationContext().startActivity(). Failing that, you could try using a Handler to communicate back to the parent Activity instead of invoking startActivity() directly. stORM is just using an AsyncTask internally, so anything you can do ordinarily within onPostExecute() should be possible, but the stORM AsyncTask does not have a reference to the Context, and I wonder if that’s the issue. There are a number of questions on SO about starting an Activity within an AsyncTask, and it seems one solution is to provide the application context to the AsyncTask in a constructor. To do that with stORM, you could subclass DoHttpRequestTask and call it the same way that the post() method does.
On Tue, Jan 15, 2013 at 11:07 AM, TurboManage
Federico said
Hi Mr. David Chandler, my problem is finally resolved. There were several problems related to other classes that I use; once solved these problems, the application works as expected. Thank you for the help.
tosken1337Sebastian said
Hi,
i have a question on uploading images (byte arrays) with the BasicHttpClient.
Before i used the DefaultHttpClient with multipart like this:
final HttpClient httpClient = new DefaultHttpClient();
final HttpPost httpPostReq = new HttpPost(ApplicationConfig.SCREENSHOT_UPLOAD_URL);
final MultipartEntity mimeEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
mimeEntity.addPart(“image”, new ByteArrayBody(imageData, params[0].getName()));
httpPostReq.setEntity(mimeEntity);
final HttpResponse response = httpClient.execute(httpPostReq);
How can i achieve this kind of multipart upload with yout very excellent BasicHttpCLlent ?
Thanks and best regards
Sebastian
David Chandler said
Hi Sebastian,
You can use post(String path, String contentType, byte[] data) to post raw bytes of any content type you specify; however, basic-http-client doesn’t have any abstraction specifically for multipart entities. I suspect that you could extend BasicHttpClient’s HttpPost and copy MultipartEntity from another HTTP client library like Apache. Then you could invoke BasicHttpClient.execute(YourCustomPost post).
michel said
Hi, tnk for the android client
but i have a problem :
Could not find method com.turbomanage.httpclient.android.AndroidHttpClient.ensureCookieManager, referenced from method com.turbomanage.httpclient.android.AndroidHttpClient.
VFY: unable to resolve static method 3169: Lcom/turbomanage/httpclient/android/AndroidHttpClient;.ensureCookieManager ()V
why this?
i was building on Android 2.2 (API 8)
David Chandler said
Hi Michel, see https://code.google.com/p/basic-http-client/issues/detail?id=8. This has been fixed, but be aware that there is no CookieManager prior to Gingerbread (2.3) so it will compile but won’t automatically manage cookies.
michel said
Hi, yes i read iusse 8 and infact i use the 0.88 version, and with android 2.2 work (the output is ok) even if I get the warning, but with android 2.3+ i have this error:
03-23 11:31:59.344: E/AndroidRuntime(780): FATAL EXCEPTION: main
03-23 11:31:59.344: E/AndroidRuntime(780): java.lang.ExceptionInInitializerError
03-23 11:31:59.344: E/AndroidRuntime(780): at com.example.helloworld.MainActivity.onCreate(MainActivity.java:32)
03-23 11:31:59.344: E/AndroidRuntime(780): at android.app.Activity.performCreate(Activity.java:5104)
03-23 11:31:59.344: E/AndroidRuntime(780): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
03-23 11:31:59.344: E/AndroidRuntime(780): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
03-23 11:31:59.344: E/AndroidRuntime(780): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
03-23 11:31:59.344: E/AndroidRuntime(780): at android.app.ActivityThread.access$600(ActivityThread.java:141)
03-23 11:31:59.344: E/AndroidRuntime(780): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
03-23 11:31:59.344: E/AndroidRuntime(780): at android.os.Handler.dispatchMessage(Handler.java:99)
03-23 11:31:59.344: E/AndroidRuntime(780): at android.os.Looper.loop(Looper.java:137)
03-23 11:31:59.344: E/AndroidRuntime(780): at android.app.ActivityThread.main(ActivityThread.java:5041)
03-23 11:31:59.344: E/AndroidRuntime(780): at java.lang.reflect.Method.invokeNative(Native Method)
03-23 11:31:59.344: E/AndroidRuntime(780): at java.lang.reflect.Method.invoke(Method.java:511)
03-23 11:31:59.344: E/AndroidRuntime(780): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
03-23 11:31:59.344: E/AndroidRuntime(780): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
03-23 11:31:59.344: E/AndroidRuntime(780): at dalvik.system.NativeStart.main(Native Method)
03-23 11:31:59.344: E/AndroidRuntime(780): Caused by: java.lang.NoSuchMethodError: com.turbomanage.httpclient.android.AndroidHttpClient.ensureCookieManager
03-23 11:31:59.344: E/AndroidRuntime(780): at com.turbomanage.httpclient.android.AndroidHttpClient.(AndroidHttpClient.java:49)
03-23 11:31:59.344: E/AndroidRuntime(780): … 15 more
java.lang.NoSuchMethodError and not class not found.
AndroidHttpClient.java:49
static {
disableConnectionReuseIfNecessary();
// See http://code.google.com/p/basic-http-client/issues/detail?id=8
if (Build.VERSION.SDK_INT > 8)
ensureCookieManager();
}
I do not understand, it would find the method ensureCookieManager() in com.turbomanage.httpclient.android.AndroidHttpClient class?
tnk
David Chandler said
Please comment on issue 8 and I’ll take a look.
Juan Jose Alonso (@JJoseAlonso) said
Hi ! fantastic library, but im having troubles trying to put to a URL.
I need to send a multipart form data to the server, (usually strings values and keys). But the data type is byte[]… How i can send this fields key-value in byte[] type? What is the way?
Thanks.
sexe said
This piece of writing is actually a good one it assists new net
visitors, who are wishing for blogging.