Skip to content

Commit 2bc4b72

Browse files
committed
1 parent 71cb695 commit 2bc4b72

File tree

5 files changed

+114
-37
lines changed

5 files changed

+114
-37
lines changed

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

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,14 @@
6363

6464
import java.io.IOException;
6565
import java.io.InputStream;
66-
import java.lang.ref.WeakReference;
6766
import java.util.HashMap;
67+
import java.util.Iterator;
6868
import java.util.LinkedList;
6969
import java.util.List;
7070
import java.util.Map;
7171
import java.util.WeakHashMap;
7272
import java.util.concurrent.ExecutorService;
7373
import java.util.concurrent.Executors;
74-
import java.util.concurrent.Future;
7574
import java.util.concurrent.ThreadPoolExecutor;
7675
import java.util.zip.GZIPInputStream;
7776

@@ -109,7 +108,7 @@ public class AsyncHttpClient {
109108
private final DefaultHttpClient httpClient;
110109
private final HttpContext httpContext;
111110
private ExecutorService threadPool;
112-
private final Map<Context, List<WeakReference<Future<?>>>> requestMap;
111+
private final Map<Context, List<RequestHandle>> requestMap;
113112
private final Map<String, String> clientHeaderMap;
114113
private boolean isUrlEncodingEnabled = true;
115114

@@ -211,7 +210,7 @@ public AsyncHttpClient(SchemeRegistry schemeRegistry) {
211210
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(httpParams, schemeRegistry);
212211

213212
threadPool = Executors.newCachedThreadPool();
214-
requestMap = new WeakHashMap<Context, List<WeakReference<Future<?>>>>();
213+
requestMap = new WeakHashMap<Context, List<RequestHandle>>();
215214
clientHeaderMap = new HashMap<String, String>();
216215

217216
httpContext = new SyncBasicHttpContext(new BasicHttpContext());
@@ -488,16 +487,13 @@ public void clearBasicAuth() {
488487
* pending requests.
489488
*/
490489
public void cancelRequests(Context context, boolean mayInterruptIfRunning) {
491-
List<WeakReference<Future<?>>> requestList = requestMap.get(context);
490+
List<RequestHandle> requestList = requestMap.get(context);
492491
if (requestList != null) {
493-
for (WeakReference<Future<?>> requestRef : requestList) {
494-
Future<?> request = requestRef.get();
495-
if (request != null) {
496-
request.cancel(mayInterruptIfRunning);
497-
}
492+
for (RequestHandle requestHandle : requestList) {
493+
requestHandle.cancel(mayInterruptIfRunning);
498494
}
495+
requestMap.remove(context);
499496
}
500-
requestMap.remove(context);
501497
}
502498

503499
// [+] HTTP HEAD
@@ -897,22 +893,29 @@ protected RequestHandle sendRequest(DefaultHttpClient client, HttpContext httpCo
897893
responseHandler.setRequestHeaders(uriRequest.getAllHeaders());
898894
responseHandler.setRequestURI(uriRequest.getURI());
899895

900-
Future<?> request = threadPool.submit(new AsyncHttpRequest(client, httpContext, uriRequest, responseHandler));
896+
AsyncHttpRequest request = new AsyncHttpRequest(client, httpContext, uriRequest, responseHandler);
897+
threadPool.submit(request);
898+
RequestHandle requestHandle = new RequestHandle(request);
901899

902900
if (context != null) {
903901
// Add request to request map
904-
List<WeakReference<Future<?>>> requestList = requestMap.get(context);
902+
List<RequestHandle> requestList = requestMap.get(context);
905903
if (requestList == null) {
906-
requestList = new LinkedList<WeakReference<Future<?>>>();
904+
requestList = new LinkedList<RequestHandle>();
907905
requestMap.put(context, requestList);
908906
}
909907

910-
requestList.add(new WeakReference<Future<?>>(request));
908+
requestList.add(requestHandle);
911909

912-
// TODO: Remove dead weakrefs from requestLists?
910+
Iterator<RequestHandle> iterator = requestList.iterator();
911+
while (iterator.hasNext()) {
912+
if (iterator.next().shouldBeGarbageCollected()) {
913+
iterator.remove();
914+
}
915+
}
913916
}
914917

915-
return new RequestHandle(request);
918+
return requestHandle;
916919
}
917920

918921
/**

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

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ class AsyncHttpRequest implements Runnable {
3939
private final HttpUriRequest request;
4040
private final ResponseHandlerInterface responseHandler;
4141
private int executionCount;
42+
private boolean isCancelled = false;
43+
private boolean cancelIsNotified = false;
44+
private boolean isFinished = false;
4245

4346
public AsyncHttpRequest(AbstractHttpClient client, HttpContext context, HttpUriRequest request, ResponseHandlerInterface responseHandler) {
4447
this.client = client;
@@ -49,40 +52,53 @@ public AsyncHttpRequest(AbstractHttpClient client, HttpContext context, HttpUriR
4952

5053
@Override
5154
public void run() {
55+
if (isCancelled()) {
56+
return;
57+
}
58+
5259
if (responseHandler != null) {
5360
responseHandler.sendStartMessage();
5461
}
5562

63+
if (isCancelled()) {
64+
return;
65+
}
66+
5667
try {
5768
makeRequestWithRetries();
5869
} catch (IOException e) {
59-
if (responseHandler != null) {
70+
if (!isCancelled() && responseHandler != null) {
6071
responseHandler.sendFailureMessage(0, null, null, e);
6172
} else {
6273
Log.e("AsyncHttpRequest", "makeRequestWithRetries returned error, but handler is null", e);
6374
}
6475
}
6576

77+
if (isCancelled()) {
78+
return;
79+
}
80+
6681
if (responseHandler != null) {
6782
responseHandler.sendFinishMessage();
6883
}
84+
85+
isFinished = true;
6986
}
7087

7188
private void makeRequest() throws IOException {
72-
if (!Thread.currentThread().isInterrupted()) {
73-
// Fixes #115
74-
if (request.getURI().getScheme() == null) {
75-
// subclass of IOException so processed in the caller
76-
throw new MalformedURLException("No valid URI scheme was provided");
77-
}
89+
if (isCancelled()) {
90+
return;
91+
}
92+
// Fixes #115
93+
if (request.getURI().getScheme() == null) {
94+
// subclass of IOException so processed in the caller
95+
throw new MalformedURLException("No valid URI scheme was provided");
96+
}
7897

79-
HttpResponse response = client.execute(request, context);
98+
HttpResponse response = client.execute(request, context);
8099

81-
if (!Thread.currentThread().isInterrupted()) {
82-
if (responseHandler != null) {
83-
responseHandler.sendResponseMessage(response);
84-
}
85-
}
100+
if (!isCancelled() && responseHandler != null) {
101+
responseHandler.sendResponseMessage(response);
86102
}
87103
}
88104

@@ -108,6 +124,10 @@ private void makeRequestWithRetries() throws IOException {
108124
cause = new IOException("NPE in HttpClient: " + e.getMessage());
109125
retry = retryHandler.retryRequest(cause, ++executionCount, context);
110126
} catch (IOException e) {
127+
if (isCancelled()) {
128+
// Eating exception, as the request was cancelled
129+
return;
130+
}
111131
cause = e;
112132
retry = retryHandler.retryRequest(cause, ++executionCount, context);
113133
}
@@ -124,4 +144,31 @@ private void makeRequestWithRetries() throws IOException {
124144
// cleaned up to throw IOException
125145
throw (cause);
126146
}
147+
148+
public boolean isCancelled() {
149+
if (isCancelled) {
150+
sendCancelNotification();
151+
}
152+
return isCancelled;
153+
}
154+
155+
private synchronized void sendCancelNotification() {
156+
if (!isFinished && isCancelled && !cancelIsNotified) {
157+
cancelIsNotified = true;
158+
if (responseHandler != null)
159+
responseHandler.sendCancelMessage();
160+
}
161+
}
162+
163+
public boolean isDone() {
164+
return isCancelled() || isFinished;
165+
}
166+
167+
public boolean cancel(boolean mayInterruptIfRunning) {
168+
isCancelled = true;
169+
if (mayInterruptIfRunning && request != null && !request.isAborted()) {
170+
request.abort();
171+
}
172+
return isCancelled();
173+
}
127174
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ public abstract class AsyncHttpResponseHandler implements ResponseHandlerInterfa
8888
protected static final int FINISH_MESSAGE = 3;
8989
protected static final int PROGRESS_MESSAGE = 4;
9090
protected static final int RETRY_MESSAGE = 5;
91+
protected static final int CANCEL_MESSAGE = 6;
9192

9293
protected static final int BUFFER_SIZE = 4096;
9394

@@ -221,6 +222,10 @@ public void onRetry(int retryNo) {
221222
Log.d(LOG_TAG, String.format("Request retry no. %d", retryNo));
222223
}
223224

225+
public void onCancel() {
226+
Log.d(LOG_TAG, "Request got cancelled");
227+
}
228+
224229
final public void sendProgressMessage(int bytesWritten, int bytesTotal) {
225230
sendMessage(obtainMessage(PROGRESS_MESSAGE, new Object[]{bytesWritten, bytesTotal}));
226231
}
@@ -245,6 +250,10 @@ final public void sendRetryMessage(int retryNo) {
245250
sendMessage(obtainMessage(RETRY_MESSAGE, new Object[]{retryNo}));
246251
}
247252

253+
final public void sendCancelMessage() {
254+
sendMessage(obtainMessage(CANCEL_MESSAGE, null));
255+
}
256+
248257
// Methods which emulate android's Handler and Message methods
249258
protected void handleMessage(Message message) {
250259
Object[] response;
@@ -291,6 +300,9 @@ protected void handleMessage(Message message) {
291300
else
292301
Log.e(LOG_TAG, "RETRY_MESSAGE didn't get enough params");
293302
break;
303+
case CANCEL_MESSAGE:
304+
onCancel();
305+
break;
294306
}
295307
}
296308

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

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

3-
import java.util.concurrent.Future;
3+
import java.lang.ref.WeakReference;
44

55
/**
66
* A Handle to an AsyncRequest which can be used to cancel a running request.
77
*/
88
public class RequestHandle {
9-
private final Future<?> request;
9+
private final WeakReference<AsyncHttpRequest> request;
1010

11-
public RequestHandle(Future<?> request) {
12-
this.request = request;
11+
public RequestHandle(AsyncHttpRequest request) {
12+
this.request = new WeakReference<AsyncHttpRequest>(request);
1313
}
1414

1515
/**
@@ -28,7 +28,8 @@ public RequestHandle(Future<?> request) {
2828
* completed normally; true otherwise
2929
*/
3030
public boolean cancel(boolean mayInterruptIfRunning) {
31-
return this.request != null && request.cancel(mayInterruptIfRunning);
31+
AsyncHttpRequest _request = request.get();
32+
return _request == null || _request.cancel(mayInterruptIfRunning);
3233
}
3334

3435
/**
@@ -38,7 +39,8 @@ public boolean cancel(boolean mayInterruptIfRunning) {
3839
* @return true if this task completed
3940
*/
4041
public boolean isFinished() {
41-
return this.request == null || request.isDone();
42+
AsyncHttpRequest _request = request.get();
43+
return _request == null || _request.isDone();
4244
}
4345

4446
/**
@@ -47,6 +49,14 @@ public boolean isFinished() {
4749
* @return true if this task was cancelled before it completed
4850
*/
4951
public boolean isCancelled() {
50-
return this.request != null && request.isCancelled();
52+
AsyncHttpRequest _request = request.get();
53+
return _request == null || _request.isCancelled();
54+
}
55+
56+
public boolean shouldBeGarbageCollected() {
57+
boolean should = isCancelled() || isFinished();
58+
if (should)
59+
request.clear();
60+
return should;
5161
}
5262
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ public interface ResponseHandlerInterface {
3737
*/
3838
void sendProgressMessage(int bytesWritten, int bytesTotal);
3939

40+
/**
41+
* Notifies callback, that request was cancelled
42+
*/
43+
void sendCancelMessage();
44+
4045
/**
4146
* Notifies callback, that request was handled successfully
4247
*

0 commit comments

Comments
 (0)