Jjang Jjang - 5 months ago 25
Android Question

Importing a newer Apache HttpClient jar in Android

I'm trying to send an HTTP/HTTPS post request from my Android client.

Question



Why does my code fail?

So far



I created an apache class/HttpClient call. Everything worked fine:

HttpClient httpClient = new DefaultHttpClient();


I've read that this method has been deprecated, so I've switched to the new recommended method:

HttpClient httpClient = HttpClientBuilder.create().build();


Eclipse didn't have this class, so I had to download Apache HttpClient 4.3.3. I imported it to my project by copying it into the
libs
folder and adding them to my build path (imported httpclient, httpclient-cache, httpcore, httpmime, fluent-hc, commons-logging, commons-codec).

Error message



06-05 02:15:26.946: W/dalvikvm(29587): Link of class 'Lorg/apache/http/impl/conn/PoolingHttpClientConnectionManager;' failed
06-05 02:15:26.946: E/dalvikvm(29587): Could not find class 'org.apache.http.impl.conn.PoolingHttpClientConnectionManager', referenced from method org.apache.http.impl.client.HttpClientBuilder.build


Most recent code



static private String insertJson(String json,String url){
HttpClient httpClient = HttpClientBuilder.create().build();

String responseString = "";
try {
HttpPost request = new HttpPost(url);
StringEntity params =new StringEntity(json, "UTF-8");
request.addHeader("content-type", "application/json");
request.setEntity(params);
HttpResponse response = httpClient.execute(request);
HttpEntity entity = response.getEntity();
responseString = EntityUtils.toString(entity, "UTF-8");

}catch (Exception ex) {
ex.printStackTrace();
// handle exception here
} finally {
httpClient.getConnectionManager().shutdown();
}
return responseString;
}

Answer

The problem is that Android already includes an older version (it's unclear exactly which, but around 4.0beta2) of Apache HttpClient.

When you add the jars of the new version as libraries of your application, duplicate classes are ignored when the APK is loaded. Since some new classes in HttpClient depend on modifications made on these other classes, dalvik does what it can (e.g. removing references, &c) but unless they are used conditionally, this is likely to cause crashes.

For example, you can see messages like these in logcat:

06-05 00:46:39.083: I/dalvikvm(6286): Could not find method org.apache.http.client.protocol.RequestDefaultHeaders.<init>, referenced from method org.apache.http.impl.client.HttpClientBuilder.build
06-05 00:46:39.083: W/dalvikvm(6286): VFY: unable to resolve direct method 22794: Lorg/apache/http/client/protocol/RequestDefaultHeaders;.<init> (Ljava/util/Collection;)V
06-05 00:46:40.434: D/dalvikvm(6286): DexOpt: couldn't find static field Lorg/apache/http/impl/client/DefaultHttpRequestRetryHandler;.INSTANCE
06-05 00:46:40.434: W/dalvikvm(6286): VFY: unable to resolve static field 8420 (INSTANCE) in Lorg/apache/http/impl/client/DefaultHttpRequestRetryHandler;

This particular message is because DefaultHttpRequestRetryHandler has a new INSTANCE static field in 4.3, which it does not have in Android. There are many more.

Going back to your original question, the only way to use a newer httpclient would be to rename all classes so these name conflicts don't occur. This is what httpclientandroidlib does.

Going even further back: DefaultHttpClient has indeed been deprecated in 4.3, but it is not deprecated in Android (unless you consider the trend towards using HttpUrlConnection a stealth form of deprecation -- in which case a newer HttpClient wouldn't be the preferred alternative either). Why exactly would you want/need to change it?

Comments