From 969170f0e802d7b93bac3cda167f672d582868fe Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Tue, 11 Feb 2014 23:45:35 +0100 Subject: [PATCH] Create only one HttpClient instance instead of one per download --- .../antennapod/gpoddernet/GpodnetClient.java | 35 ------- .../antennapod/gpoddernet/GpodnetService.java | 19 +--- .../download/AntennapodHttpClient.java | 95 +++++++++++++++++++ .../service/download/HttpDownloader.java | 55 +++-------- 4 files changed, 111 insertions(+), 93 deletions(-) delete mode 100644 src/de/danoeh/antennapod/gpoddernet/GpodnetClient.java create mode 100644 src/de/danoeh/antennapod/service/download/AntennapodHttpClient.java diff --git a/src/de/danoeh/antennapod/gpoddernet/GpodnetClient.java b/src/de/danoeh/antennapod/gpoddernet/GpodnetClient.java deleted file mode 100644 index 845a23823..000000000 --- a/src/de/danoeh/antennapod/gpoddernet/GpodnetClient.java +++ /dev/null @@ -1,35 +0,0 @@ -package de.danoeh.antennapod.gpoddernet; - -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.scheme.PlainSocketFactory; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.ssl.SSLSocketFactory; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; -import org.apache.http.params.BasicHttpParams; - -/** - * HTTP client for the gpodder.net service. - */ -public class GpodnetClient extends DefaultHttpClient { - - private static SchemeRegistry prepareSchemeRegistry() { - SchemeRegistry sr = new SchemeRegistry(); - - Scheme http = new Scheme("http", - PlainSocketFactory.getSocketFactory(), 80); - sr.register(http); - Scheme https = new Scheme("https", - SSLSocketFactory.getSocketFactory(), 443); - sr.register(https); - - return sr; - } - - @Override - protected ClientConnectionManager createClientConnectionManager() { - return new ThreadSafeClientConnManager(new BasicHttpParams(), prepareSchemeRegistry()); - } - -} diff --git a/src/de/danoeh/antennapod/gpoddernet/GpodnetService.java b/src/de/danoeh/antennapod/gpoddernet/GpodnetService.java index 6e819f570..a0c5b534c 100644 --- a/src/de/danoeh/antennapod/gpoddernet/GpodnetService.java +++ b/src/de/danoeh/antennapod/gpoddernet/GpodnetService.java @@ -1,9 +1,8 @@ package de.danoeh.antennapod.gpoddernet; -import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.gpoddernet.model.*; import de.danoeh.antennapod.preferences.GpodnetPreferences; -import de.danoeh.antennapod.preferences.UserPreferences; +import de.danoeh.antennapod.service.download.AntennapodHttpClient; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; @@ -11,15 +10,13 @@ import org.apache.http.HttpStatus; import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.entity.StringEntity; import org.apache.http.impl.auth.BasicScheme; -import org.apache.http.params.CoreProtocolPNames; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -45,16 +42,10 @@ public class GpodnetService { public static final String DEFAULT_BASE_HOST = "gpodder.net"; private final String BASE_HOST; - private static final int TIMEOUT_MILLIS = 20000; - - private final GpodnetClient httpClient; + private final HttpClient httpClient; public GpodnetService() { - httpClient = new GpodnetClient(); - final HttpParams params = httpClient.getParams(); - params.setParameter(CoreProtocolPNames.USER_AGENT, AppConfig.USER_AGENT); - HttpConnectionParams.setConnectionTimeout(params, TIMEOUT_MILLIS); - HttpConnectionParams.setSoTimeout(params, TIMEOUT_MILLIS); + httpClient = AntennapodHttpClient.getHttpClient(); BASE_HOST = GpodnetPreferences.getHostname(); } @@ -519,7 +510,7 @@ public class GpodnetService { new Thread() { @Override public void run() { - httpClient.getConnectionManager().shutdown(); + AntennapodHttpClient.cleanup(); } }.start(); } diff --git a/src/de/danoeh/antennapod/service/download/AntennapodHttpClient.java b/src/de/danoeh/antennapod/service/download/AntennapodHttpClient.java new file mode 100644 index 000000000..7e1c9178a --- /dev/null +++ b/src/de/danoeh/antennapod/service/download/AntennapodHttpClient.java @@ -0,0 +1,95 @@ +package de.danoeh.antennapod.service.download; + +import android.util.Log; +import de.danoeh.antennapod.AppConfig; +import org.apache.http.client.HttpClient; +import org.apache.http.client.params.HttpClientParams; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.params.ConnManagerPNames; +import org.apache.http.conn.scheme.PlainSocketFactory; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.impl.client.AbstractHttpClient; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.CoreProtocolPNames; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; + +import java.util.concurrent.TimeUnit; + +/** + * Provides access to a HttpClient singleton. + */ +public class AntennapodHttpClient { + private static final String TAG = "AntennapodHttpClient"; + + public static final long EXPIRED_CONN_TIMEOUT_SEC = 30; + + public static final int MAX_REDIRECTS = 5; + public static final int CONNECTION_TIMEOUT = 30000; + public static final int SOCKET_TIMEOUT = 30000; + + public static final int MAX_CONNECTIONS = 6; + + + private static volatile HttpClient httpClient = null; + + /** + * Returns the HttpClient singleton. + */ + public static synchronized HttpClient getHttpClient() { + if (httpClient == null) { + if (AppConfig.DEBUG) Log.d(TAG, "Creating new instance of HTTP client"); + + HttpParams params = new BasicHttpParams(); + params.setParameter(CoreProtocolPNames.USER_AGENT, AppConfig.USER_AGENT); + params.setIntParameter("http.protocol.max-redirects", MAX_REDIRECTS); + params.setBooleanParameter("http.protocol.reject-relative-redirect", + false); + HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT); + HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT); + HttpClientParams.setRedirecting(params, true); + + httpClient = new DefaultHttpClient(createClientConnectionManager(), params); + // Workaround for broken URLs in redirection + ((AbstractHttpClient) httpClient) + .setRedirectHandler(new APRedirectHandler()); + } + return httpClient; + } + + /** + * Closes expired connections. This method should be called by the using class once has finished its work with + * the HTTP client. + */ + public static synchronized void cleanup() { + if (httpClient != null) { + httpClient.getConnectionManager().closeExpiredConnections(); + httpClient.getConnectionManager().closeIdleConnections(EXPIRED_CONN_TIMEOUT_SEC, TimeUnit.SECONDS); + } + } + + + private static ClientConnectionManager createClientConnectionManager() { + HttpParams params = new BasicHttpParams(); + params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, MAX_CONNECTIONS); + return new ThreadSafeClientConnManager(params, prepareSchemeRegistry()); + } + + private static SchemeRegistry prepareSchemeRegistry() { + SchemeRegistry sr = new SchemeRegistry(); + + Scheme http = new Scheme("http", + PlainSocketFactory.getSocketFactory(), 80); + sr.register(http); + Scheme https = new Scheme("https", + SSLSocketFactory.getSocketFactory(), 443); + sr.register(https); + + return sr; + } + +} diff --git a/src/de/danoeh/antennapod/service/download/HttpDownloader.java b/src/de/danoeh/antennapod/service/download/HttpDownloader.java index 94cf01188..fc2b3178b 100644 --- a/src/de/danoeh/antennapod/service/download/HttpDownloader.java +++ b/src/de/danoeh/antennapod/service/download/HttpDownloader.java @@ -1,24 +1,5 @@ package de.danoeh.antennapod.service.download; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.*; - -import org.apache.commons.io.IOUtils; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.params.HttpClientParams; -import org.apache.http.impl.client.AbstractHttpClient; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; - import android.net.http.AndroidHttpClient; import android.util.Log; import de.danoeh.antennapod.AppConfig; @@ -26,36 +7,25 @@ import de.danoeh.antennapod.PodcastApp; import de.danoeh.antennapod.R; import de.danoeh.antennapod.util.DownloadError; import de.danoeh.antennapod.util.StorageUtils; +import org.apache.commons.io.IOUtils; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; + +import java.io.*; +import java.net.*; public class HttpDownloader extends Downloader { private static final String TAG = "HttpDownloader"; - private static final int MAX_REDIRECTS = 5; - private static final int BUFFER_SIZE = 8 * 1024; - private static final int CONNECTION_TIMEOUT = 30000; - private static final int SOCKET_TIMEOUT = 30000; public HttpDownloader(DownloadRequest request) { super(request); } - private DefaultHttpClient createHttpClient() { - DefaultHttpClient httpClient = new DefaultHttpClient(); - HttpParams params = httpClient.getParams(); - params.setIntParameter("http.protocol.max-redirects", MAX_REDIRECTS); - params.setBooleanParameter("http.protocol.reject-relative-redirect", - false); - HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT); - HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT); - HttpClientParams.setRedirecting(params, true); - - // Workaround for broken URLs in redirection - ((AbstractHttpClient) httpClient) - .setRedirectHandler(new APRedirectHandler()); - return httpClient; - } - private URI getURIFromRequestUrl(String source) { try { URL url = new URL(source); @@ -69,12 +39,11 @@ public class HttpDownloader extends Downloader { @Override protected void download() { - DefaultHttpClient httpClient = null; + HttpClient httpClient = AntennapodHttpClient.getHttpClient(); BufferedOutputStream out = null; InputStream connection = null; try { HttpGet httpGet = new HttpGet(getURIFromRequestUrl(request.getSource())); - httpClient = createHttpClient(); HttpResponse response = httpClient.execute(httpGet); HttpEntity httpEntity = response.getEntity(); int responseCode = response.getStatusLine().getStatusCode(); @@ -176,9 +145,7 @@ public class HttpDownloader extends Downloader { onFail(DownloadError.ERROR_CONNECTION_ERROR, request.getSource()); } finally { IOUtils.closeQuietly(out); - if (httpClient != null) { - httpClient.getConnectionManager().shutdown(); - } + AntennapodHttpClient.cleanup(); } }