Skip to content

Commit 579a3e0

Browse files
committed
Merge pull request android-async-http#395 from leonardoxh/2.0
Integration with OkHttpClient 2.0
2 parents f903007 + f869d99 commit 579a3e0

File tree

4 files changed

+105
-239
lines changed

4 files changed

+105
-239
lines changed

library/src/main/java/com/loopj/android/http/AsyncHttpClient.java

Lines changed: 39 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,12 @@
6464

6565
import java.io.IOException;
6666
import java.io.InputStream;
67+
import java.io.OutputStream;
6768
import java.lang.ref.WeakReference;
69+
import java.net.HttpURLConnection;
70+
import java.net.MalformedURLException;
71+
import java.net.ProtocolException;
72+
import java.net.URL;
6873
import java.util.HashMap;
6974
import java.util.LinkedList;
7075
import java.util.List;
@@ -73,6 +78,7 @@
7378
import java.util.concurrent.Executors;
7479
import java.util.concurrent.Future;
7580
import java.util.concurrent.ThreadPoolExecutor;
81+
import java.util.concurrent.TimeUnit;
7682
import java.util.zip.GZIPInputStream;
7783

7884

@@ -106,149 +112,17 @@ public class AsyncHttpClient {
106112
private int maxConnections = DEFAULT_MAX_CONNECTIONS;
107113
private int timeout = DEFAULT_SOCKET_TIMEOUT;
108114

109-
private final OkHttpClient httpClient;
110-
private final HttpContext httpContext;
115+
private final OkHttpClient httpClient = new OkHttpClient();
111116
private ThreadPoolExecutor threadPool;
112-
private final Map<Context, List<WeakReference<Future<?>>>> requestMap;
113-
private final Map<String, String> clientHeaderMap;
117+
// private final Map<Context, List<WeakReference<Future<?>>>> requestMap;
118+
private final Map<String, String> clientHeaderMap = new HashMap<String, String>();
114119
private boolean isUrlEncodingEnabled = true;
115120

116121
/**
117122
* Creates a new AsyncHttpClient with default constructor arguments values
118123
*/
119124
public AsyncHttpClient() {
120-
this(false, 80, 443);
121-
}
122-
123-
/**
124-
* Creates a new AsyncHttpClient.
125-
*
126-
* @param httpPort non-standard HTTP-only port
127-
*/
128-
public AsyncHttpClient(int httpPort) {
129-
this(false, httpPort, 443);
130-
}
131-
132-
/**
133-
* Creates a new AsyncHttpClient.
134-
*
135-
* @param httpPort non-standard HTTP-only port
136-
* @param httpsPort non-standard HTTPS-only port
137-
*/
138-
public AsyncHttpClient(int httpPort, int httpsPort) {
139-
this(false, httpPort, httpsPort);
140-
}
141-
142-
/**
143-
* Creates new AsyncHttpClient using given params
144-
*
145-
* @param fixNoHttpResponseException Whether to fix or not issue, by ommiting SSL verification
146-
* @param httpPort HTTP port to be used, must be greater than 0
147-
* @param httpsPort HTTPS port to be used, must be greater than 0
148-
*/
149-
public AsyncHttpClient(boolean fixNoHttpResponseException, int httpPort, int httpsPort) {
150-
this(getDefaultSchemeRegistry(fixNoHttpResponseException, httpPort, httpsPort));
151-
}
152-
153-
/**
154-
* Returns default instance of SchemeRegistry
155-
*
156-
* @param fixNoHttpResponseException Whether to fix or not issue, by ommiting SSL verification
157-
* @param httpPort HTTP port to be used, must be greater than 0
158-
* @param httpsPort HTTPS port to be used, must be greater than 0
159-
*/
160-
private static SchemeRegistry getDefaultSchemeRegistry(boolean fixNoHttpResponseException, int httpPort, int httpsPort) {
161-
if (fixNoHttpResponseException) {
162-
Log.d(LOG_TAG, "Beware! Using the fix is insecure, as it doesn't verify SSL certificates.");
163-
}
164-
165-
if (httpPort < 1) {
166-
httpPort = 80;
167-
Log.d(LOG_TAG, "Invalid HTTP port number specified, defaulting to 80");
168-
}
169-
170-
if (httpsPort < 1) {
171-
httpsPort = 443;
172-
Log.d(LOG_TAG, "Invalid HTTPS port number specified, defaulting to 443");
173-
}
174-
175-
// Fix to SSL flaw in API < ICS
176-
// See https://code.google.com/p/android/issues/detail?id=13117
177-
SSLSocketFactory sslSocketFactory;
178-
if (fixNoHttpResponseException)
179-
sslSocketFactory = MySSLSocketFactory.getFixedSocketFactory();
180-
else
181-
sslSocketFactory = SSLSocketFactory.getSocketFactory();
182-
183-
SchemeRegistry schemeRegistry = new SchemeRegistry();
184-
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), httpPort));
185-
schemeRegistry.register(new Scheme("https", sslSocketFactory, httpsPort));
186-
187-
return schemeRegistry;
188-
}
189-
190-
/**
191-
* Creates a new AsyncHttpClient.
192-
*
193-
* @param schemeRegistry SchemeRegistry to be used
194-
*/
195-
public AsyncHttpClient(SchemeRegistry schemeRegistry) {
196-
197-
BasicHttpParams httpParams = new BasicHttpParams();
198-
199-
ConnManagerParams.setTimeout(httpParams, timeout);
200-
ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(maxConnections));
201-
ConnManagerParams.setMaxTotalConnections(httpParams, DEFAULT_MAX_CONNECTIONS);
202-
203-
HttpConnectionParams.setSoTimeout(httpParams, timeout);
204-
HttpConnectionParams.setConnectionTimeout(httpParams, timeout);
205-
HttpConnectionParams.setTcpNoDelay(httpParams, true);
206-
HttpConnectionParams.setSocketBufferSize(httpParams, DEFAULT_SOCKET_BUFFER_SIZE);
207-
208-
HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
209-
HttpProtocolParams.setUserAgent(httpParams, String.format("android-async-http/%s (http://loopj.com/android-async-http)", VERSION));
210-
211-
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(httpParams, schemeRegistry);
212-
213-
threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(DEFAULT_MAX_CONNECTIONS);
214-
requestMap = new WeakHashMap<Context, List<WeakReference<Future<?>>>>();
215-
clientHeaderMap = new HashMap<String, String>();
216-
217-
httpContext = new SyncBasicHttpContext(new BasicHttpContext());
218-
httpClient = new OkHttpClient(cm, httpParams);
219-
httpClient.setFollowProtocolRedirects()
220-
httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
221-
@Override
222-
public void process(HttpRequest request, HttpContext context) {
223-
if (!request.containsHeader(HEADER_ACCEPT_ENCODING)) {
224-
request.addHeader(HEADER_ACCEPT_ENCODING, ENCODING_GZIP);
225-
}
226-
for (String header : clientHeaderMap.keySet()) {
227-
request.addHeader(header, clientHeaderMap.get(header));
228-
}
229-
}
230-
});
231-
232-
httpClient.addResponseInterceptor(new HttpResponseInterceptor() {
233-
@Override
234-
public void process(HttpResponse response, HttpContext context) {
235-
final HttpEntity entity = response.getEntity();
236-
if (entity == null) {
237-
return;
238-
}
239-
final Header encoding = entity.getContentEncoding();
240-
if (encoding != null) {
241-
for (HeaderElement element : encoding.getElements()) {
242-
if (element.getName().equalsIgnoreCase(ENCODING_GZIP)) {
243-
response.setEntity(new InflatingEntity(entity));
244-
break;
245-
}
246-
}
247-
}
248-
}
249-
});
250-
251-
httpClient.setHttpRequestRetryHandler(new RetryHandler(DEFAULT_MAX_RETRIES, DEFAULT_RETRY_SLEEP_TIME_MILLIS));
125+
252126
}
253127

254128
/**
@@ -258,18 +132,8 @@ public void process(HttpResponse response, HttpContext context) {
258132
*
259133
* @return underlying HttpClient instance
260134
*/
261-
public HttpClient getHttpClient() {
262-
return this.httpClient;
263-
}
264-
265-
/**
266-
* Get the underlying HttpContext instance. This is useful for getting and setting fine-grained
267-
* settings for requests by accessing the context's attributes such as the CookieStore.
268-
*
269-
* @return underlying HttpContext instance
270-
*/
271-
public HttpContext getHttpContext() {
272-
return this.httpContext;
135+
public OkHttpClient getHttpClient() {
136+
return httpClient;
273137
}
274138

275139
/**
@@ -354,15 +218,14 @@ public int getTimeout() {
354218
* Set the connection and socket timeout. By default, 10 seconds.
355219
*
356220
* @param timeout the connect/socket timeout in milliseconds, at least 1 second
221+
* @deprecated use {@link #setTimeout(long, TimeUnit)} instead
357222
*/
358223
public void setTimeout(int timeout) {
359-
if (timeout < 1000)
360-
timeout = DEFAULT_SOCKET_TIMEOUT;
361-
this.timeout = timeout;
362-
final HttpParams httpParams = this.httpClient.getParams();
363-
ConnManagerParams.setTimeout(httpParams, this.timeout);
364-
HttpConnectionParams.setSoTimeout(httpParams, this.timeout);
365-
HttpConnectionParams.setConnectionTimeout(httpParams, this.timeout);
224+
setTimeout(timeout, TimeUnit.MILLISECONDS);
225+
}
226+
227+
public void setTimeout(long timeout, TimeUnit unit) {
228+
httpClient.setConnectTimeout(timeout, unit);
366229
}
367230

368231
/**
@@ -884,30 +747,33 @@ public RequestHandle delete(Context context, String url, Header[] headers, Reque
884747
* HttpPost, HttpGet, HttpPut, etc.
885748
* @return RequestHandle of future request process
886749
*/
887-
protected RequestHandle sendRequest(OkHttpClient client, HttpContext httpContext, HttpUriRequest uriRequest, String contentType, ResponseHandlerInterface responseHandler, Context context) {
888-
if (contentType != null) {
889-
uriRequest.addHeader("Content-Type", contentType);
750+
protected RequestHandle sendRequest(OkHttpClient client, HttpUriRequest uriRequest, String contentType, ResponseHandlerInterface responseHandler, Context context) {
751+
HttpURLConnection connection = null;
752+
try {
753+
connection = client.open(uriRequest.getURI().toURL());
754+
connection.setRequestMethod(uriRequest.getMethod()); //TODO write the params to connection.getOutputStream(void)
755+
for(Map.Entry<String, String> header : clientHeaderMap.entrySet()) {
756+
connection.setRequestProperty(header.getKey(), header.getValue());
757+
}
758+
if(contentType != null) {
759+
uriRequest.addHeader("Content-Type", contentType);
760+
connection.setRequestProperty("Content-Type", contentType);
761+
}
762+
} catch(ProtocolException e) {
763+
764+
} catch(MalformedURLException e) {
765+
890766
}
891767

892768
responseHandler.setRequestHeaders(uriRequest.getAllHeaders());
893769
responseHandler.setRequestURI(uriRequest.getURI());
894770

895-
Future<?> request = threadPool.submit(new AsyncHttpRequest(client, httpContext, uriRequest, responseHandler));
896-
897-
if (context != null) {
898-
// Add request to request map
899-
List<WeakReference<Future<?>>> requestList = requestMap.get(context);
900-
if (requestList == null) {
901-
requestList = new LinkedList<WeakReference<Future<?>>>();
902-
requestMap.put(context, requestList);
903-
}
904-
905-
requestList.add(new WeakReference<Future<?>>(request));
906-
907-
// TODO: Remove dead weakrefs from requestLists?
771+
if(connection != null) {
772+
Future<?> request = threadPool.submit(new AsyncHttpRequest(connection, uriRequest, responseHandler));
773+
return new RequestHandle(request);
908774
}
909-
910-
return new RequestHandle(request);
775+
776+
return null;
911777
}
912778

913779
/**

library/src/main/java/com/loopj/android/http/AsyncHttpRequest.java

Lines changed: 7 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,16 @@
1818

1919
package com.loopj.android.http;
2020

21-
import android.util.Log;
22-
23-
import com.squareup.okhttp.OkHttpClient;
24-
25-
import org.apache.http.HttpResponse;
26-
import org.apache.http.client.HttpRequestRetryHandler;
27-
import org.apache.http.client.methods.HttpUriRequest;
28-
import org.apache.http.protocol.HttpContext;
29-
3021
import java.io.IOException;
31-
import java.net.MalformedURLException;
32-
import java.net.UnknownHostException;
22+
import java.io.InputStream;
23+
import java.net.HttpURLConnection;
3324

3425
class AsyncHttpRequest implements Runnable {
35-
private final OkHttpClient client;
36-
private final HttpContext context;
37-
private final HttpUriRequest request;
26+
private final HttpURLConnection client;
3827
private final ResponseHandlerInterface responseHandler;
39-
private int executionCount;
4028

41-
public AsyncHttpRequest(OkHttpClient client, HttpContext context, HttpUriRequest request, ResponseHandlerInterface responseHandler) {
29+
public AsyncHttpRequest(HttpURLConnection client, ResponseHandlerInterface responseHandler) {
4230
this.client = client;
43-
this.context = context;
44-
this.request = request;
4531
this.responseHandler = responseHandler;
4632
}
4733

@@ -52,7 +38,7 @@ public void run() {
5238
}
5339

5440
try {
55-
makeRequestWithRetries();
41+
makeRequest();
5642
} catch (IOException e) {
5743
if (responseHandler != null) {
5844
responseHandler.sendFailureMessage(0, null, null, e);
@@ -66,13 +52,8 @@ public void run() {
6652

6753
private void makeRequest() throws IOException {
6854
if (!Thread.currentThread().isInterrupted()) {
69-
// Fixes #115
70-
if (request.getURI().getScheme() == null) {
71-
// subclass of IOException so processed in the caller
72-
throw new MalformedURLException("No valid URI scheme was provided");
73-
}
7455

75-
HttpResponse response = client.execute(request, context);
56+
InputStream response = client.getInputStream();
7657

7758
if (!Thread.currentThread().isInterrupted()) {
7859
if (responseHandler != null) {
@@ -81,43 +62,5 @@ private void makeRequest() throws IOException {
8162
}
8263
}
8364
}
84-
85-
private void makeRequestWithRetries() throws IOException {
86-
boolean retry = true;
87-
IOException cause = null;
88-
HttpRequestRetryHandler retryHandler = client.getHttpRequestRetryHandler();
89-
try {
90-
while (retry) {
91-
try {
92-
makeRequest();
93-
return;
94-
} catch (UnknownHostException e) {
95-
// switching between WI-FI and mobile data networks can cause a retry which then results in an UnknownHostException
96-
// while the WI-FI is initialising. The retry logic will be invoked here, if this is NOT the first retry
97-
// (to assist in genuine cases of unknown host) which seems better than outright failure
98-
cause = new IOException("UnknownHostException exception: " + e.getMessage());
99-
retry = (executionCount > 0) && retryHandler.retryRequest(cause, ++executionCount, context);
100-
} catch (NullPointerException e) {
101-
// there's a bug in HttpClient 4.0.x that on some occasions causes
102-
// DefaultRequestExecutor to throw an NPE, see
103-
// http://code.google.com/p/android/issues/detail?id=5255
104-
cause = new IOException("NPE in HttpClient: " + e.getMessage());
105-
retry = retryHandler.retryRequest(cause, ++executionCount, context);
106-
} catch (IOException e) {
107-
cause = e;
108-
retry = retryHandler.retryRequest(cause, ++executionCount, context);
109-
}
110-
if (retry && (responseHandler != null)) {
111-
responseHandler.sendRetryMessage();
112-
}
113-
}
114-
} catch (Exception e) {
115-
// catch anything else to ensure failure message is propagated
116-
Log.e("AsyncHttpRequest", "Unhandled exception origin cause", e);
117-
cause = new IOException("Unhandled exception: " + e.getMessage());
118-
}
119-
120-
// cleaned up to throw IOException
121-
throw (cause);
122-
}
65+
12366
}

library/src/main/java/com/loopj/android/http/ResponseHandlerInterface.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package com.loopj.android.http;
22

33
import org.apache.http.Header;
4-
import org.apache.http.HttpResponse;
54

65
import java.io.IOException;
6+
import java.io.InputStream;
77
import java.net.URI;
88

99
/**
@@ -17,7 +17,7 @@ public interface ResponseHandlerInterface {
1717
* @param response HttpResponse object with data
1818
* @throws java.io.IOException if retrieving data from response fails
1919
*/
20-
void sendResponseMessage(HttpResponse response) throws IOException;
20+
void sendResponseMessage(InputStream response) throws IOException;
2121

2222
/**
2323
* Notifies callback, that request started execution

0 commit comments

Comments
 (0)