Skip to the content.

Android extensions for Apache HttpClient 4.5.x

Dependency management

Add API or implementation dependency on com.github.ok2c.hc4.android:httpclient-android to Gradle dependencies. This will automatically introduce transitive dependency on Apache HttpClient.

dependencies {
    api 'com.github.ok2c.hc4.android:httpclient-android:0.1.0'
}

Configuration and application

HttpClient 4.5 is almost compatible with Android API 28 or later. The trouble-maker is the org.apache.http.conn.ssl.DefaultHostnameVerifier class that depends on javax.naming APIs unsupported by Android.

Android extensions for Apache HttpClient provide a replacement for the default HostnameVerifier implementation and provide a builder for PoolingHttpClientConnectionManager optimized for Android called AndroidHttpClientConnectionManagerBuilder. Users of Apache HttpClient 4.5 are strongly advised to use this builder to create connection managers in their Android applications.

Generally it is strongly advised to use a single instance of CloseableHttpClient per Android activity or several related activities. It may also be a good idea to close out idle connections on activity pause and evict expired connections upon activity resumption.

Activities that have a dedicated CloseableHttpClient instance should close it when destroyed in order to deallocate system resources held by persistent HTTP connections kept alive in the connection pool.

public class HttpClientActivity extends Activity {

    private static final String LOG_TAG = "HttpClientActivity";

    private final HttpClientConnectionManager connectionManager;
    private final CloseableHttpClient httpClient;

    public HttpClientActivity() {
        this.connectionManager = AndroidHttpClientConnectionManagerBuilder.create()
                .setConnectionTimeToLive(1, TimeUnit.MINUTES)
                .setDefaultSocketConfig(SocketConfig.custom()
                        .setSoTimeout(5000)
                        .build())
                .build();
        this.httpClient = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .setDefaultRequestConfig(RequestConfig.custom()
                        .setConnectTimeout(5000)
                        .setSocketTimeout(5000)
                        .setCookieSpec(CookieSpecs.STANDARD_STRICT)
                        .build())
                .build();
    }
    
    @Override
    protected void onPause() {
        connectionManager.closeIdleConnections(0, TimeUnit.MILLISECONDS);
        super.onPause();
    }

    @Override
    protected void onResume() {
        connectionManager.closeExpiredConnections();
        super.onResume();
    }

    @Override
    protected void onStop() {
        connectionManager.closeIdleConnections(0, TimeUnit.MILLISECONDS);
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        try {
            this.httpClient.close();
        } catch (IOException e) {
            Log.e(LOG_TAG, e.getMessage(), e);
        }
        super.onDestroy();
    }

}

Asynchronous HTTP message exchange support

The HttpExecAsyncTask class is an extension of Android AsyncTask that simplifies execution of HTTP requests and processing of HTTP responses. This class can execute a sequence of requests and process corresponding response data streams using a HttpExecAsyncTask.ResponseHandler handler provided by the caller asynchronously from the UI activity while pushing ExecUpdate messages with status updates from the execution thread to the UI activity thread. The UI components can be updated to reflect the progress of HTTP message exchange based on attributes of ExecUpdate message.

public class InternalHttpRequestAsyncTask extends HttpExecAsyncTask<HttpResponse> {

    private final WeakReference<Activity> activityRef;

    InternalHttpRequestAsyncTask(
            final CloseableHttpClient httpClient,
            final Activity activity,
            final HttpExecAsyncTask.ResponseHandler<HttpResponse> handler) {
        super(httpClient, handler);
        this.activityRef = new WeakReference<>(activity);
    }

    private Activity getActivity() {
        Activity activity = activityRef.get();
        if (activity != null && !activity.isFinishing()) {
            return activity;
        }
        return null;
    }

    @Override
    protected void onPreExecute() {
        Activity activity = getActivity();
        if (activity == null) {
            return;
        }
        ...
    }

    @Override
    protected void onProgressUpdate(final ExecUpdate... values) {
        Activity activity = getActivity();
        if (activity == null) {
            return;
        }
        ...
    }

    @Override
    protected void onCancelled(final List<HttpResponse> httpResponses) {
        Activity activity = getActivity();
        if (activity == null) {
            return;
        }
        ...
    }

    @Override
    protected void onPostExecute(final List<HttpResponse> responses) {
        Activity activity = getActivity();
        if (activity == null) {
            return;
        }
        ...
    }

}

Logging

Apache HttpClient 4.5 uses Commons Logging APIs to log messages. When running on Android Commons Logging redirects log messages to Java Logging backend (java.util.logging) backend at runtime. The default Java logging handler shipped with Android in its turn redirects log messages to Logcat - Android native logging backend. This three layer logging setup is quite cumbersome. Important Please note one must enable logging at Java level and at Android level in order to see HttpClient logs in the Android Logcat

The default java.util.logging log handler shipped with Android has to truncate original logging category names into so-called Android log tags. It usually takes the last period separated segment and truncates it to 23 characters.

'org.apache.http.impl.execchain.MainClientExec' -> 'MainClientExec'
'org.apache.http.impl.conn.DefaultHttpClientConnectionOperator' -> 'DefaultHttpClientConn'

This scheme may prove quite inconsistent and inconvenient when running HttpClient with context logging.

This library provides an alternative logging handler that groups HttpClient log messages into three categories

'org.apache.http.wire' -> 'HttpClientWire'
'org.apache.http.headers' -> 'HttpClientHeader'
'org.apache.http.*' -> 'HttpClient'

HttpClient Logging configuration

  1. Create logging.properties file in <MODULE>/src/main/res/raw folder and customize logging configuration as required by the application context.

     .level = WARN
        
     #handlers = com.android.internal.logging.AndroidHandler
     handlers = com.ok2c.hc.android.logging.HttpClientLogHandler
        
     org.apache.http.level = FINEST
     org.apache.http.wire.level = SEVERE
        
        
    
  2. Android is not going to read this configuration by default. It is necessary to load this configuration into the LogManager manually at application start-up.

     public class HttpClientApplication extends Application {
        
         private static final String LOG_TAG = "HttpClientApplication";
        
         @Override
         public void onCreate() {
             super.onCreate();
             try (InputStream inputStream = getResources().openRawResource(R.raw.logging)) {
                 LogManager logManager = LogManager.getLogManager();
                 logManager.readConfiguration(new BufferedInputStream(inputStream));
             } catch (IOException ex) {
                 Log.e(LOG_TAG, ex.getMessage(), ex);
             }
         }
     }
    

    Important please update AndroidManifest.xml to use this custom application class.

  3. Activate Logcat categories

     adb shell setprop log.tag.HttpClient VERBOSE
     adb shell setprop log.tag.HttpClientHeader VERBOSE
     adb shell setprop log.tag.HttpClientWire VERBOSE
        
    

    Skip HttpClientWire if wire logging is not required.