diff --git a/library/src/com/loopj/android/http/AsyncHttpClient.java b/library/src/com/loopj/android/http/AsyncHttpClient.java
index 2bfcabc4e..f9ab1ad05 100644
--- a/library/src/com/loopj/android/http/AsyncHttpClient.java
+++ b/library/src/com/loopj/android/http/AsyncHttpClient.java
@@ -889,7 +889,7 @@ private HttpEntity paramsToEntity(RequestParams params, AsyncHttpResponseHandler
}
} catch (Throwable t) {
if (responseHandler != null)
- responseHandler.sendFailureMessage(0, null, t, (String) null);
+ responseHandler.sendFailureMessage(0, null, (byte[]) null, t);
else
t.printStackTrace();
}
diff --git a/library/src/com/loopj/android/http/AsyncHttpRequest.java b/library/src/com/loopj/android/http/AsyncHttpRequest.java
index 339c6c0f5..db3891ed7 100644
--- a/library/src/com/loopj/android/http/AsyncHttpRequest.java
+++ b/library/src/com/loopj/android/http/AsyncHttpRequest.java
@@ -38,7 +38,6 @@ class AsyncHttpRequest implements Runnable {
private final HttpContext context;
private final HttpUriRequest request;
private final AsyncHttpResponseHandler responseHandler;
- private boolean isBinaryRequest;
private int executionCount;
public AsyncHttpRequest(AbstractHttpClient client, HttpContext context, HttpUriRequest request, AsyncHttpResponseHandler responseHandler) {
@@ -46,110 +45,81 @@ public AsyncHttpRequest(AbstractHttpClient client, HttpContext context, HttpUriR
this.context = context;
this.request = request;
this.responseHandler = responseHandler;
- if (responseHandler instanceof BinaryHttpResponseHandler) {
- this.isBinaryRequest = true;
- }
}
@Override
public void run() {
- try {
- if (responseHandler != null) {
- responseHandler.sendStartMessage();
- }
+ if (responseHandler != null) {
+ responseHandler.sendStartMessage();
+ }
+ try {
makeRequestWithRetries();
-
- if (responseHandler != null) {
- responseHandler.sendFinishMessage();
- }
} catch (IOException e) {
if (responseHandler != null) {
- responseHandler.sendFinishMessage();
- if (this.isBinaryRequest) {
- responseHandler.sendFailureMessage(e, (byte[]) null);
- } else {
- responseHandler.sendFailureMessage(e, (String) null);
- }
+ responseHandler.sendFailureMessage(0, null, (byte[]) null, e);
}
}
+
+ if (responseHandler != null) {
+ responseHandler.sendFinishMessage();
+ }
}
- private void makeRequest() throws IOException, InterruptedException {
+ private void makeRequest() throws IOException {
if (!Thread.currentThread().isInterrupted()) {
- try {
- // Fixes #115
- if (request.getURI().getScheme() == null)
- throw new MalformedURLException("No valid URI scheme was provided");
- HttpResponse response = client.execute(request, context);
- if (!Thread.currentThread().isInterrupted()) {
- if (responseHandler != null) {
- responseHandler.sendResponseMessage(response);
- }
- } else {
- throw new InterruptedException("makeRequest was interrupted");
- }
- } catch (IOException e) {
- if (!Thread.currentThread().isInterrupted()) {
- throw e;
+ // Fixes #115
+ if (request.getURI().getScheme() == null) {
+ // subclass of IOException so processed in the caller
+ throw new MalformedURLException("No valid URI scheme was provided");
+ }
+
+ HttpResponse response = client.execute(request, context);
+
+ if (!Thread.currentThread().isInterrupted()) {
+ if (responseHandler != null) {
+ responseHandler.sendResponseMessage(response);
}
}
}
}
- private void makeRequestWithRetries() throws ConnectException {
- // This is an additional layer of retry logic lifted from droid-fu
- // See: https://github.com/kaeppler/droid-fu/blob/master/src/main/java/com/github/droidfu/http/BetterHttpRequestBase.java
+ private void makeRequestWithRetries() throws IOException {
boolean retry = true;
IOException cause = null;
HttpRequestRetryHandler retryHandler = client.getHttpRequestRetryHandler();
- while (retry) {
- try {
- makeRequest();
- return;
- } catch (ClientProtocolException e) {
- if (responseHandler != null) {
- responseHandler.sendFailureMessage(e, "cannot repeat the request");
- }
- return;
- } catch (UnknownHostException e) {
- if (responseHandler != null) {
- responseHandler.sendFailureMessage(e, "can't resolve host");
+ try
+ {
+ while (retry) {
+ try {
+ makeRequest();
+ return;
+ } catch (UnknownHostException e) {
+ // switching between WI-FI and mobile data networks can cause a retry which then results in an UnknownHostException
+ // while the WI-FI is initialising. The retry logic will be invoked here, if this is NOT the first retry
+ // (to assist in genuine cases of unknown host) which seems better than outright failure
+ cause = new IOException("UnknownHostException exception: " + e.getMessage());
+ retry = (executionCount > 0) && retryHandler.retryRequest(cause, ++executionCount, context);
+ } catch (NullPointerException e) {
+ // there's a bug in HttpClient 4.0.x that on some occasions causes
+ // DefaultRequestExecutor to throw an NPE, see
+ // http://code.google.com/p/android/issues/detail?id=5255
+ cause = new IOException("NPE in HttpClient: " + e.getMessage());
+ retry = retryHandler.retryRequest(cause, ++executionCount, context);
+ } catch (IOException e) {
+ cause = e;
+ retry = retryHandler.retryRequest(cause, ++executionCount, context);
}
- return;
- } catch (ConnectTimeoutException e) {
- if (responseHandler != null) {
- responseHandler.sendFailureMessage(e, "connection timed out");
- }
- } catch (SocketException e) {
- // Added to detect host unreachable
- if (responseHandler != null) {
- responseHandler.sendFailureMessage(e, "can't resolve host");
+ if(retry && (responseHandler != null)) {
+ responseHandler.sendRetryMessage();
}
- return;
- } catch (SocketTimeoutException e) {
- if (responseHandler != null) {
- responseHandler.sendFailureMessage(e, "socket time out");
- }
- return;
- } catch (IOException e) {
- cause = e;
- retry = retryHandler.retryRequest(cause, ++executionCount, context);
- } catch (NullPointerException e) {
- // there's a bug in HttpClient 4.0.x that on some occasions causes
- // DefaultRequestExecutor to throw an NPE, see
- // http://code.google.com/p/android/issues/detail?id=5255
- cause = new IOException("NPE in HttpClient" + e.getMessage());
- retry = retryHandler.retryRequest(cause, ++executionCount, context);
- } catch (InterruptedException e) {
- cause = new IOException("Request was interrupted while executing");
- retry = retryHandler.retryRequest(cause, ++executionCount, context);
}
+ } catch (Exception e) {
+ // catch anything else to ensure failure message is propagated
+ cause = new IOException("Unhandled exception: " + e.getMessage());
}
-
- // no retries left, crap out with exception
- ConnectException ex = new ConnectException();
- ex.initCause(cause);
- throw ex;
+
+ // cleaned up to throw IOException
+ throw(cause);
}
}
diff --git a/library/src/com/loopj/android/http/AsyncHttpResponseHandler.java b/library/src/com/loopj/android/http/AsyncHttpResponseHandler.java
index 5525391a3..ffef41ee2 100644
--- a/library/src/com/loopj/android/http/AsyncHttpResponseHandler.java
+++ b/library/src/com/loopj/android/http/AsyncHttpResponseHandler.java
@@ -21,24 +21,27 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.util.Log;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpResponseException;
-import org.apache.http.entity.BufferedHttpEntity;
-import org.apache.http.util.EntityUtils;
+import org.apache.http.util.ByteArrayBuffer;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.lang.ref.WeakReference;
/**
* Used to intercept and handle the responses from requests made using
- * {@link AsyncHttpClient}. The {@link #onSuccess(String)} method is
+ * {@link AsyncHttpClient}. The {@link #onSuccess(int, org.apache.http.Header[], byte[])} method is
* designed to be anonymously overridden with your own response handling code.
*
- * Additionally, you can override the {@link #onFailure(Throwable, String)},
- * {@link #onStart()}, and {@link #onFinish()} methods as required.
+ * Additionally, you can override the {@link #onFailure(int, org.apache.http.Header[], byte[], Throwable)},
+ * {@link #onStart()}, {@link #onFinish()}, {@link #onRetry()} and {@link #onProgress(int, int)} methods as required.
*
* For example:
*
@@ -51,16 +54,26 @@
* }
*
* @Override
- * public void onSuccess(String response) {
+ * public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
* // Successfully got a response
* }
*
* @Override
- * public void onFailure(Throwable e, String response) {
+ * public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
* // Response failed :(
* }
*
* @Override
+ * public void onRetry() {
+ * // Request was retried
+ * }
+ *
+ * @Override
+ * public void onProgress(int bytesWritten, int totalSize) {
+ * // Progress notification
+ * }
+ *
+ * @Override
* public void onFinish() {
* // Completed the request (either success or failure)
* }
@@ -68,20 +81,58 @@
*
*/
public class AsyncHttpResponseHandler {
+ private static final String LOG_TAG = "AsyncHttpResponseHandler";
+
protected static final int SUCCESS_MESSAGE = 0;
protected static final int FAILURE_MESSAGE = 1;
protected static final int START_MESSAGE = 2;
protected static final int FINISH_MESSAGE = 3;
protected static final int PROGRESS_MESSAGE = 4;
+ protected static final int RETRY_MESSAGE = 5;
+
+ protected static final int BUFFER_SIZE = 4096;
private Handler handler;
private String responseCharset = "UTF-8";
+ private Boolean useSynchronousMode = false;
+
+ // avoid leaks by using a non-anonymous handler class
+ // with a weak reference
+ static class ResponderHandler extends Handler {
+ private final WeakReference mResponder;
+
+ ResponderHandler(AsyncHttpResponseHandler service) {
+ mResponder = new WeakReference(service);
+ }
+ @Override
+ public void handleMessage(Message msg)
+ {
+ AsyncHttpResponseHandler service = mResponder.get();
+ if (service != null) {
+ service.handleMessage(msg);
+ }
+ }
+ }
+
+ public boolean getUseSynchronousMode() {
+ return (useSynchronousMode);
+ }
+
+ /**
+ * Set the response handler to use synchronous mode or not
+ *
+ * @param value true indicates that synchronous mode should be used
+ */
+ public void setUseSynchronousMode(Boolean value) {
+ useSynchronousMode = value;
+ }
/**
* Sets the charset for the response string. If not set, the default is UTF-8.
*
* @param charset to be used for the response string.
* @see Charset
+ * @deprecated use {@link com.loopj.android.http.TextHttpResponseHandler} instead
*/
public void setCharset(final String charset) {
this.responseCharset = charset;
@@ -97,12 +148,7 @@ public String getCharset() {
public AsyncHttpResponseHandler() {
// Set up a handler to post events back to the correct thread if possible
if (Looper.myLooper() != null) {
- handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- AsyncHttpResponseHandler.this.handleMessage(msg);
- }
- };
+ handler = new ResponderHandler(this);
}
}
@@ -136,7 +182,9 @@ public void onFinish() {
* Fired when a request returns successfully, override to handle in your own code
*
* @param content the body of the HTTP response from the server
+ * @deprecated use {@link #onSuccess(int, Header[], byte[])}
*/
+ @Deprecated
public void onSuccess(String content) {
}
@@ -146,7 +194,9 @@ public void onSuccess(String content) {
* @param statusCode the status code of the response
* @param headers the headers of the HTTP response
* @param content the body of the HTTP response from the server
+ * @deprecated use {@link #onSuccess(int, Header[], byte[])}
*/
+ @Deprecated
public void onSuccess(int statusCode, Header[] headers, String content) {
onSuccess(statusCode, content);
}
@@ -156,11 +206,29 @@ public void onSuccess(int statusCode, Header[] headers, String content) {
*
* @param statusCode the status code of the response
* @param content the body of the HTTP response from the server
+ * @deprecated use {@link #onSuccess(int, Header[], byte[])}
*/
+ @Deprecated
public void onSuccess(int statusCode, String content) {
onSuccess(content);
}
+ /**
+ * Fired when a request returns successfully, override to handle in your own code
+ *
+ * @param statusCode the status code of the response
+ * @param headers return headers, if any
+ * @param responseBody the body of the HTTP response from the server
+ */
+ public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
+ try {
+ String response = new String(responseBody, getCharset());
+ onSuccess(statusCode, headers, response);
+ } catch (UnsupportedEncodingException e) {
+ Log.e(LOG_TAG, e.toString());
+ onFailure(statusCode, headers, e, (String) null);
+ }
+ }
/**
* Fired when a request fails to complete, override to handle in your own code
*
@@ -176,7 +244,9 @@ public void onFailure(Throwable error) {
*
* @param error the underlying cause of the failure
* @param content the response body, if any
+ * @deprecated use {@link #onFailure(int, Header[], byte[], Throwable)}
*/
+ @Deprecated
public void onFailure(Throwable error, String content) {
// By default, call the deprecated onFailure(Throwable) for compatibility
onFailure(error);
@@ -188,7 +258,9 @@ public void onFailure(Throwable error, String content) {
* @param statusCode return HTTP status code
* @param error the underlying cause of the failure
* @param content the response body, if any
+ * @deprecated use {@link #onFailure(int, Header[], byte[], Throwable)}
*/
+ @Deprecated
public void onFailure(int statusCode, Throwable error, String content) {
// By default, call the chain method onFailure(Throwable,String)
onFailure(error, content);
@@ -201,12 +273,40 @@ public void onFailure(int statusCode, Throwable error, String content) {
* @param headers return headers, if any
* @param error the underlying cause of the failure
* @param content the response body, if any
+ * @deprecated use {@link #onFailure(int, Header[], byte[], Throwable)}
*/
+ @Deprecated
public void onFailure(int statusCode, Header[] headers, Throwable error, String content) {
// By default, call the chain method onFailure(int,Throwable,String)
onFailure(statusCode, error, content);
}
+ /**
+ * Fired when a request fails to complete, override to handle in your own code
+ *
+ * @param statusCode return HTTP status code
+ * @param headers return headers, if any
+ * @param responseBody the response body, if any
+ * @param error the underlying cause of the failure
+ */
+ public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
+ String response = null;
+ try {
+ response = new String(responseBody, getCharset());
+ } catch (UnsupportedEncodingException e) {
+ Log.e(LOG_TAG, e.toString());
+ onFailure(statusCode, headers, e, null);
+ }
+ onFailure(statusCode, headers, error, response);
+ }
+
+ /**
+ * Fired when a retry occurs, override to handle in your own code
+ *
+ */
+ public void onRetry() {
+ }
+
//
// Pre-processing of messages (executes in background threadpool thread)
@@ -216,26 +316,12 @@ protected void sendProgressMessage(int bytesWritten, int totalSize) {
sendMessage(obtainMessage(PROGRESS_MESSAGE, new Object[]{bytesWritten, totalSize}));
}
- protected void sendSuccessMessage(int statusCode, Header[] headers, String responseBody) {
+ protected void sendSuccessMessage(int statusCode, Header[] headers, byte[] responseBody) {
sendMessage(obtainMessage(SUCCESS_MESSAGE, new Object[]{statusCode, headers, responseBody}));
}
- protected void sendFailureMessage(int statusCode, Header[] headers, Throwable e, String responseBody) {
- sendMessage(obtainMessage(FAILURE_MESSAGE, new Object[]{statusCode, headers, e, responseBody}));
- }
-
- @Deprecated
- protected void sendFailureMessage(Throwable e, String responseBody) {
- sendMessage(obtainMessage(FAILURE_MESSAGE, new Object[]{0, null, e, responseBody}));
- }
-
- protected void sendFailureMessage(int statusCode, Header[] headers, Throwable e, byte[] responseBody) {
- sendMessage(obtainMessage(FAILURE_MESSAGE, new Object[]{statusCode, headers, e, responseBody}));
- }
-
- @Deprecated
- protected void sendFailureMessage(Throwable e, byte[] responseBody) {
- sendMessage(obtainMessage(FAILURE_MESSAGE, new Object[]{0, null, e, responseBody}));
+ protected void sendFailureMessage(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
+ sendMessage(obtainMessage(FAILURE_MESSAGE, new Object[]{statusCode, headers, responseBody, error}));
}
protected void sendStartMessage() {
@@ -246,20 +332,10 @@ protected void sendFinishMessage() {
sendMessage(obtainMessage(FINISH_MESSAGE, null));
}
-
- //
- // Pre-processing of messages (in original calling thread, typically the UI thread)
- //
-
- protected void handleSuccessMessage(int statusCode, Header[] headers, String responseBody) {
- onSuccess(statusCode, headers, responseBody);
+ protected void sendRetryMessage() {
+ sendMessage(obtainMessage(RETRY_MESSAGE, null));
}
-
- protected void handleFailureMessage(int statusCode, Header[] headers, Throwable e, String responseBody) {
- onFailure(statusCode, headers, e, responseBody);
- }
-
-
+
// Methods which emulate android's Handler and Message methods
protected void handleMessage(Message msg) {
Object[] response;
@@ -267,11 +343,11 @@ protected void handleMessage(Message msg) {
switch (msg.what) {
case SUCCESS_MESSAGE:
response = (Object[]) msg.obj;
- handleSuccessMessage((Integer) response[0], (Header[]) response[1], (String) response[2]);
+ onSuccess((Integer) response[0], (Header[]) response[1], (byte[]) response[2]);
break;
case FAILURE_MESSAGE:
response = (Object[]) msg.obj;
- handleFailureMessage((Integer) response[0], (Header[]) response[1], (Throwable) response[2], (String) response[3]);
+ onFailure((Integer) response[0], (Header[]) response[1], (byte[]) response[2], (Throwable) response[3]);
break;
case START_MESSAGE:
onStart();
@@ -283,14 +359,17 @@ protected void handleMessage(Message msg) {
response = (Object[]) msg.obj;
onProgress((Integer) response[0], (Integer) response[1]);
break;
+ case RETRY_MESSAGE:
+ onRetry();
+ break;
}
}
protected void sendMessage(Message msg) {
- if (handler != null) {
- handler.sendMessage(msg);
- } else {
+ if (getUseSynchronousMode() || handler == null) {
handleMessage(msg);
+ } else if (!Thread.currentThread().isInterrupted()) { // do not send messages if request has been cancelled
+ handler.sendMessage(msg);
}
}
@@ -315,35 +394,56 @@ protected Message obtainMessage(int responseMessage, Object response) {
}
// Interface to AsyncHttpRequest
- protected void sendResponseMessage(HttpResponse response) {
- if (response == null) {
- sendFailureMessage(0, null, new IllegalStateException("No response"), (String) null);
- return;
- }
- StatusLine status = response.getStatusLine();
- String responseBody = null;
- try {
- HttpEntity entity;
- HttpEntity temp = response.getEntity();
- if (temp != null) {
- entity = new BufferedHttpEntity(temp);
- responseBody = EntityUtils.toString(entity, getCharset());
- }
- } catch (IOException e) {
- try {
- if (response.getEntity() != null)
- response.getEntity().consumeContent();
- } catch (Throwable t) {
- t.printStackTrace();
+ void sendResponseMessage(HttpResponse response) throws IOException {
+ // do not process if request has been cancelled
+ if (!Thread.currentThread().isInterrupted()) {
+ StatusLine status = response.getStatusLine();
+ byte[] responseBody = null;
+ responseBody = getResponseData(response.getEntity());
+ // additional cancellation check as getResponseData() can take non-zero time to process
+ if (!Thread.currentThread().isInterrupted()) {
+ if (status.getStatusCode() >= 300) {
+ sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), responseBody, new HttpResponseException(status.getStatusCode(), status.getReasonPhrase()));
+ } else {
+ sendSuccessMessage(status.getStatusCode(), response.getAllHeaders(), responseBody);
+ }
}
- sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), e, (String) null);
- return;
}
+ }
- if (status.getStatusCode() >= 300) {
- sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), new HttpResponseException(status.getStatusCode(), status.getReasonPhrase()), responseBody);
- } else {
- sendSuccessMessage(status.getStatusCode(), response.getAllHeaders(), responseBody);
+ byte[] getResponseData(HttpEntity entity) throws IOException {
+ byte[] responseBody = null;
+ if (entity != null) {
+ InputStream instream = entity.getContent();
+ if (instream != null) {
+ long contentLength = entity.getContentLength();
+ if (contentLength > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("HTTP entity too large to be buffered in memory");
+ }
+ if (contentLength < 0) {
+ contentLength = BUFFER_SIZE;
+ }
+ try{
+ ByteArrayBuffer buffer = new ByteArrayBuffer((int) contentLength);
+ try {
+ byte[] tmp = new byte[BUFFER_SIZE];
+ int l, count = 0;
+ // do not send messages if request has been cancelled
+ while ((l = instream.read(tmp)) != -1 && !Thread.currentThread().isInterrupted()) {
+ count += l;
+ buffer.append(tmp, 0, l);
+ sendProgressMessage(count, (int) contentLength);
+ }
+ } finally {
+ instream.close();
+ }
+ responseBody = buffer.buffer();
+ } catch( OutOfMemoryError e ) {
+ System.gc();
+ throw new IOException("File too large to fit into available memory");
+ }
+ }
}
+ return (responseBody);
}
}
diff --git a/library/src/com/loopj/android/http/BinaryHttpResponseHandler.java b/library/src/com/loopj/android/http/BinaryHttpResponseHandler.java
index d8e5cdda3..91adc5601 100644
--- a/library/src/com/loopj/android/http/BinaryHttpResponseHandler.java
+++ b/library/src/com/loopj/android/http/BinaryHttpResponseHandler.java
@@ -18,15 +18,10 @@
package com.loopj.android.http;
-import android.os.Message;
-
import org.apache.http.Header;
-import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpResponseException;
-import org.apache.http.entity.BufferedHttpEntity;
-import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.regex.Pattern;
@@ -104,74 +99,45 @@ public void onSuccess(int statusCode, byte[] binaryData) {
}
/**
- * Fired when a request fails to complete, override to handle in your own code
+ * Fired when a request returns successfully, override to handle in your own code
*
- * @param statusCode response HTTP statuse code
- * @param headers response headers, if any
- * @param error the underlying cause of the failure
- * @param binaryData the response body, if any
- * @deprecated
+ * @param statusCode response HTTP statuse code
+ * @param headers response headers, if any
+ * @param responseData the response body, if any
*/
- @Deprecated
- public void onFailure(int statusCode, Header[] headers, Throwable error, byte[] binaryData) {
- // By default, call the deprecated onFailure(Throwable) for compatibility
- onFailure(statusCode, error, null);
- }
-
-
- //
- // Pre-processing of messages (executes in background threadpool thread)
- //
- protected void sendSuccessMessage(int statusCode, byte[] responseBody) {
- sendMessage(obtainMessage(SUCCESS_MESSAGE, new Object[]{statusCode, responseBody}));
+ @Override
+ public void onSuccess(int statusCode, Header[] headers, byte[] responseData) {
+ onSuccess(statusCode, responseData);
}
+ /**
+ * Fired when a request fails to complete, override to handle in your own code
+ *
+ * @param statusCode response HTTP statuse code
+ * @param headers response headers, if any
+ * @param responseData the response body, if any
+ * @param error the underlying cause of the failure
+ */
+
@Override
- protected void sendFailureMessage(int statusCode, Header[] headers, Throwable e, byte[] responseBody) {
- sendMessage(obtainMessage(FAILURE_MESSAGE, new Object[]{statusCode, headers, e, responseBody}));
+ public void onFailure(int statusCode, Header[] headers, byte[] responseData, Throwable error) {
+ onFailure(statusCode, error, null);
}
//
// Pre-processing of messages (in original calling thread, typically the UI thread)
//
- protected void handleSuccessMessage(int statusCode, byte[] responseBody) {
- onSuccess(statusCode, responseBody);
- }
-
- protected void handleFailureMessage(int statusCode, Header[] headers, Throwable e, byte[] responseBody) {
- onFailure(statusCode, headers, e, responseBody);
- }
-
- // Methods which emulate android's Handler and Message methods
- @Override
- protected void handleMessage(Message msg) {
- Object[] response;
- switch (msg.what) {
- case SUCCESS_MESSAGE:
- response = (Object[]) msg.obj;
- handleSuccessMessage((Integer) response[0], (byte[]) response[1]);
- break;
- case FAILURE_MESSAGE:
- response = (Object[]) msg.obj;
- handleFailureMessage((Integer) response[0], (Header[]) response[1], (Throwable) response[2], (byte[]) response[3]);
- break;
- default:
- super.handleMessage(msg);
- break;
- }
- }
-
// Interface to AsyncHttpRequest
@Override
- protected void sendResponseMessage(HttpResponse response) {
+ protected void sendResponseMessage(HttpResponse response) throws IOException {
StatusLine status = response.getStatusLine();
Header[] contentTypeHeaders = response.getHeaders("Content-Type");
byte[] responseBody = null;
if (contentTypeHeaders.length != 1) {
//malformed/ambiguous HTTP Header, ABORT!
- sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), new HttpResponseException(status.getStatusCode(), "None, or more than one, Content-Type Header found!"), (String) null);
+ sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), (byte[]) null, new HttpResponseException(status.getStatusCode(), "None, or more than one, Content-Type Header found!"));
return;
}
Header contentTypeHeader = contentTypeHeaders[0];
@@ -183,24 +149,9 @@ protected void sendResponseMessage(HttpResponse response) {
}
if (!foundAllowedContentType) {
//Content-Type not in allowed list, ABORT!
- sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), new HttpResponseException(status.getStatusCode(), "Content-Type not allowed!"), (String) null);
+ sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), (byte[]) null, new HttpResponseException(status.getStatusCode(), "Content-Type not allowed!"));
return;
}
- try {
- HttpEntity entity = null;
- HttpEntity temp = response.getEntity();
- if (temp != null) {
- entity = new BufferedHttpEntity(temp);
- }
- responseBody = EntityUtils.toByteArray(entity);
- } catch (IOException e) {
- sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), e, (byte[]) null);
- }
-
- if (status.getStatusCode() >= 300) {
- sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), new HttpResponseException(status.getStatusCode(), status.getReasonPhrase()), responseBody);
- } else {
- sendSuccessMessage(status.getStatusCode(), responseBody);
- }
+ super.sendResponseMessage( response );
}
}
diff --git a/library/src/com/loopj/android/http/FileAsyncHttpResponseHandler.java b/library/src/com/loopj/android/http/FileAsyncHttpResponseHandler.java
index 7bb674fca..f528982fe 100644
--- a/library/src/com/loopj/android/http/FileAsyncHttpResponseHandler.java
+++ b/library/src/com/loopj/android/http/FileAsyncHttpResponseHandler.java
@@ -1,11 +1,7 @@
package com.loopj.android.http;
-import android.os.Message;
-
import org.apache.http.Header;
-import org.apache.http.HttpResponse;
-import org.apache.http.StatusLine;
-import org.apache.http.client.HttpResponseException;
+import org.apache.http.HttpEntity;
import java.io.File;
import java.io.FileOutputStream;
@@ -44,66 +40,40 @@ public void onFailure(int statusCode, Header[] headers, Throwable e, File respon
onFailure(statusCode, e, response);
}
-
- protected void sendSuccessMessage(int statusCode, File file) {
- sendMessage(obtainMessage(SUCCESS_MESSAGE, new Object[]{statusCode, file}));
- }
-
- protected void sendFailureMessage(int statusCode, Header[] headers, Throwable e, File file) {
- sendMessage(obtainMessage(FAILURE_MESSAGE, new Object[]{statusCode, headers, e, file}));
- }
-
- protected void handleSuccessMessage(int statusCode, File responseBody) {
- onSuccess(statusCode, responseBody);
- }
-
- protected void handleFailureMessage(int statusCode, Header[] headers, Throwable e, File responseBody) {
- onFailure(statusCode, headers, e, responseBody);
+ @Override
+ public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
+ onFailure(statusCode, headers, error, mFile);
}
- // Methods which emulate android's Handler and Message methods
- protected void handleMessage(Message msg) {
- Object[] response;
- switch (msg.what) {
- case SUCCESS_MESSAGE:
- response = (Object[]) msg.obj;
- handleSuccessMessage((Integer) response[0], (File) response[1]);
- break;
- case FAILURE_MESSAGE:
- response = (Object[]) msg.obj;
- handleFailureMessage((Integer) response[0], (Header[]) response[1], (Throwable) response[2], (File) response[3]);
- break;
- default:
- super.handleMessage(msg);
- break;
- }
+ @Override
+ public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
+ onSuccess(statusCode, mFile);
}
@Override
- protected void sendResponseMessage(HttpResponse response) {
- StatusLine status = response.getStatusLine();
-
- try {
- FileOutputStream buffer = new FileOutputStream(this.mFile);
- InputStream is = response.getEntity().getContent();
-
- int nRead;
- byte[] data = new byte[16384];
-
- while ((nRead = is.read(data, 0, data.length)) != -1)
- buffer.write(data, 0, nRead);
-
- buffer.flush();
- buffer.close();
-
- } catch (IOException e) {
- sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), e, this.mFile);
- }
-
- if (status.getStatusCode() >= 300) {
- sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), new HttpResponseException(status.getStatusCode(), status.getReasonPhrase()), this.mFile);
- } else {
- sendSuccessMessage(status.getStatusCode(), this.mFile);
- }
- }
-}
\ No newline at end of file
+ byte[] getResponseData(HttpEntity entity) throws IOException {
+ if (entity != null) {
+ InputStream instream = entity.getContent();
+ long contentLength = entity.getContentLength();
+ FileOutputStream buffer = new FileOutputStream(this.mFile);
+ if (instream != null) {
+ try {
+ byte[] tmp = new byte[BUFFER_SIZE];
+ int l, count = 0;;
+ // do not send messages if request has been cancelled
+ while ((l = instream.read(tmp)) != -1 && !Thread.currentThread().isInterrupted()) {
+ count += l;
+ buffer.write(tmp, 0, l);
+ sendProgressMessage(count, (int) contentLength);
+ }
+ } finally {
+ instream.close();
+ buffer.flush();
+ buffer.close();
+ }
+ }
+ }
+ return (null);
+ }
+
+}
diff --git a/library/src/com/loopj/android/http/JsonHttpResponseHandler.java b/library/src/com/loopj/android/http/JsonHttpResponseHandler.java
index 138b61162..a5a0bd953 100644
--- a/library/src/com/loopj/android/http/JsonHttpResponseHandler.java
+++ b/library/src/com/loopj/android/http/JsonHttpResponseHandler.java
@@ -18,8 +18,6 @@
package com.loopj.android.http;
-import android.os.Message;
-
import org.apache.http.Header;
import org.apache.http.HttpStatus;
import org.json.JSONArray;
@@ -27,6 +25,8 @@
import org.json.JSONObject;
import org.json.JSONTokener;
+import java.io.UnsupportedEncodingException;
+
/**
* Used to intercept and handle the responses from requests made using
* {@link AsyncHttpClient}, with automatic parsing into a {@link JSONObject}
@@ -40,8 +40,6 @@
* parent class.
*/
public class JsonHttpResponseHandler extends AsyncHttpResponseHandler {
- protected static final int SUCCESS_JSON_MESSAGE = 100;
-
//
// Callbacks to be overridden, typically anonymously
//
@@ -141,13 +139,8 @@ public void onFailure(int statusCode, Header[] headers, Throwable e, JSONArray e
onFailure(statusCode, e, errorResponse);
}
-
- //
- // Pre-processing of messages (executes in background threadpool thread)
- //
-
@Override
- protected void sendSuccessMessage(final int statusCode, final Header[] headers, final String responseBody) {
+ public void onSuccess(final int statusCode, final Header[] headers, final byte[] responseBody) {
if (statusCode != HttpStatus.SC_NO_CONTENT) {
new Thread(new Runnable() {
@Override
@@ -157,70 +150,35 @@ public void run() {
postRunnable(new Runnable() {
@Override
public void run() {
- sendMessage(obtainMessage(SUCCESS_JSON_MESSAGE, new Object[]{statusCode, headers, jsonResponse}));
+ if (jsonResponse instanceof JSONObject) {
+ onSuccess(statusCode, headers, (JSONObject) jsonResponse);
+ } else if (jsonResponse instanceof JSONArray) {
+ onSuccess(statusCode, headers, (JSONArray) jsonResponse);
+ } else if (jsonResponse instanceof String) {
+ onSuccess(statusCode, headers, (String) jsonResponse);
+ } else {
+ onFailure(new JSONException("Unexpected type " + jsonResponse.getClass().getName()), (JSONObject) null);
+ }
+
}
});
- } catch (final JSONException e) {
+ } catch (final JSONException ex) {
postRunnable(new Runnable() {
@Override
public void run() {
- sendFailureMessage(statusCode, headers, e, responseBody);
+ onFailure(ex, (JSONObject) null);
}
});
}
}
}).start();
} else {
- sendMessage(obtainMessage(SUCCESS_JSON_MESSAGE, new Object[]{statusCode, headers, new JSONObject()}));
+ onSuccess(statusCode, headers, new JSONObject());
}
}
-
- //
- // Pre-processing of messages (in original calling thread, typically the UI thread)
- //
-
@Override
- protected void handleMessage(Message msg) {
- switch (msg.what) {
- case SUCCESS_JSON_MESSAGE:
- Object[] response = (Object[]) msg.obj;
- handleSuccessJsonMessage((Integer) response[0], (Header[]) response[1], response[2]);
- break;
- default:
- super.handleMessage(msg);
- }
- }
-
- protected void handleSuccessJsonMessage(int statusCode, Header[] headers, Object jsonResponse) {
- if (jsonResponse instanceof JSONObject) {
- onSuccess(statusCode, headers, (JSONObject) jsonResponse);
- } else if (jsonResponse instanceof JSONArray) {
- onSuccess(statusCode, headers, (JSONArray) jsonResponse);
- } else if (jsonResponse instanceof String) {
- onSuccess(statusCode, headers, (String) jsonResponse);
- } else {
- onFailure(new JSONException("Unexpected type " + jsonResponse.getClass().getName()), (JSONObject) null);
- }
- }
-
- protected Object parseResponse(String responseBody) throws JSONException {
- if (null == responseBody)
- return null;
- Object result = null;
- //trim the string to prevent start with blank, and test if the string is valid JSON, because the parser don't do this :(. If Json is not valid this will return null
- responseBody = responseBody.trim();
- if (responseBody.startsWith("{") || responseBody.startsWith("[")) {
- result = new JSONTokener(responseBody).nextValue();
- }
- if (result == null) {
- result = responseBody;
- }
- return result;
- }
-
- @Override
- protected void handleFailureMessage(final int statusCode, final Header[] headers, final Throwable e, final String responseBody) {
+ public void onFailure(final int statusCode, final Header[] headers, final byte[] responseBody, final Throwable e) {
if (responseBody != null) {
new Thread(new Runnable() {
@Override
@@ -237,16 +195,16 @@ public void run() {
} else if (jsonResponse instanceof String) {
onFailure(statusCode, headers, e, (String) jsonResponse);
} else {
- onFailure(statusCode, headers, e, responseBody);
+ onFailure(new JSONException("Unexpected type " + jsonResponse.getClass().getName()), (JSONObject) null);
}
}
});
- } catch (JSONException ex) {
+ } catch (final JSONException ex) {
postRunnable(new Runnable() {
@Override
public void run() {
- onFailure(statusCode, headers, e, responseBody);
+ onFailure(ex, (JSONObject) null );
}
});
@@ -256,6 +214,23 @@ public void run() {
} else {
onFailure(e, "");
}
+ }
+ protected Object parseResponse(byte[] responseBody) throws JSONException {
+ if (null == responseBody)
+ return null;
+ Object result = null;
+ try {
+ //trim the string to prevent start with blank, and test if the string is valid JSON, because the parser don't do this :(. If Json is not valid this will return null
+ String jsonString = new String(responseBody, "UTF-8").trim();
+ if (jsonString.startsWith("{") || jsonString.startsWith("[")) {
+ result = new JSONTokener(jsonString).nextValue();
+ }
+ if (result == null) {
+ result = jsonString;
+ }
+ } catch (UnsupportedEncodingException ex) {
+ }
+ return result;
}
}
diff --git a/library/src/com/loopj/android/http/SyncHttpClient.java b/library/src/com/loopj/android/http/SyncHttpClient.java
index 879abb1f5..fbaae7ab4 100644
--- a/library/src/com/loopj/android/http/SyncHttpClient.java
+++ b/library/src/com/loopj/android/http/SyncHttpClient.java
@@ -1,59 +1,59 @@
package com.loopj.android.http;
import android.content.Context;
-import android.os.Message;
-
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HttpContext;
-public abstract class SyncHttpClient extends AsyncHttpClient {
- private int responseCode;
- /*
- * as this is a synchronous request this is just a helping mechanism to pass
- * the result back to this method. Therefore the result object has to be a
- * field to be accessible
- */
- protected String result;
- protected AsyncHttpResponseHandler responseHandler = new AsyncHttpResponseHandler() {
+public class SyncHttpClient extends AsyncHttpClient {
- @Override
- protected void sendResponseMessage(HttpResponse response) {
- responseCode = response.getStatusLine().getStatusCode();
- super.sendResponseMessage(response);
- }
+ /**
+ * Creates a new SyncHttpClient with default constructor arguments values
+ */
+ public SyncHttpClient() {
+ super(false, 80, 443);
+ }
- @Override
- protected void sendMessage(Message msg) {
- /*
- * Dont use the handler and send it directly to the analysis
- * (because its all the same thread)
- */
- handleMessage(msg);
- }
+ /**
+ * Creates a new SyncHttpClient.
+ *
+ * @param httpPort non-standard HTTP-only port
+ */
+ public SyncHttpClient(int httpPort) {
+ super(false, httpPort, 443);
+ }
- @Override
- public void onSuccess(String content) {
- result = content;
- }
+ /**
+ * Creates a new SyncHttpClient.
+ *
+ * @param httpPort non-standard HTTP-only port
+ * @param httpsPort non-standard HTTPS-only port
+ */
+ public SyncHttpClient(int httpPort, int httpsPort) {
+ super(false, httpPort, httpsPort);
+ }
- @Override
- public void onFailure(Throwable error, String content) {
- result = onRequestFailed(error, content);
- }
- };
+ /**
+ * Creates new SyncHttpClient using given params
+ *
+ * @param fixNoHttpResponseException Whether to fix or not issue, by ommiting SSL verification
+ * @param httpPort HTTP port to be used, must be greater than 0
+ * @param httpsPort HTTPS port to be used, must be greater than 0
+ */
+ public SyncHttpClient(boolean fixNoHttpResponseException, int httpPort, int httpsPort) {
+ super(fixNoHttpResponseException, httpPort, httpsPort);
+ }
/**
- * @return the response code for the last request, might be usefull
- * sometimes
+ * Creates a new SyncHttpClient.
+ *
+ * @param schemeRegistry SchemeRegistry to be used
*/
- public int getResponseCode() {
- return responseCode;
+ public SyncHttpClient(SchemeRegistry schemeRegistry) {
+ super(schemeRegistry);
}
- // Private stuff
@Override
protected void sendRequest(DefaultHttpClient client,
HttpContext httpContext, HttpUriRequest uriRequest,
@@ -63,67 +63,11 @@ protected void sendRequest(DefaultHttpClient client,
uriRequest.addHeader("Content-Type", contentType);
}
+ responseHandler.setUseSynchronousMode(true);
+
/*
* will execute the request directly
- */
- new AsyncHttpRequest(client, httpContext, uriRequest, responseHandler)
- .run();
+ */
+ new AsyncHttpRequest(client, httpContext, uriRequest, responseHandler).run();
}
-
- public abstract String onRequestFailed(Throwable error, String content);
-
- public void delete(String url, RequestParams queryParams,
- AsyncHttpResponseHandler responseHandler) {
- delete(getUrlWithQueryString(isUrlEncodingEnabled(), url, queryParams), responseHandler);
- }
-
- public String get(String url, RequestParams params) {
- this.get(url, params, responseHandler);
- /*
- * the response handler will have set the result when this line is
- * reached
- */
- return result;
- }
-
- public String get(String url) {
- this.get(url, null, responseHandler);
- return result;
- }
-
- public String put(String url, RequestParams params) {
- this.put(url, params, responseHandler);
- return result;
- }
-
- public String put(String url) {
- this.put(url, null, responseHandler);
- return result;
- }
-
- public String post(String url, HttpEntity entity) {
- this.post(null, url, entity, null, responseHandler);
- return result;
- }
-
- public String post(String url, RequestParams params) {
- this.post(url, params, responseHandler);
- return result;
- }
-
- public String post(String url) {
- this.post(url, null, responseHandler);
- return result;
- }
-
- public String delete(String url, RequestParams params) {
- this.delete(url, params, responseHandler);
- return result;
- }
-
- public String delete(String url) {
- this.delete(url, null, responseHandler);
- return result;
- }
-
}
diff --git a/library/src/com/loopj/android/http/TextHttpResponseHandler.java b/library/src/com/loopj/android/http/TextHttpResponseHandler.java
new file mode 100644
index 000000000..ff7bfc872
--- /dev/null
+++ b/library/src/com/loopj/android/http/TextHttpResponseHandler.java
@@ -0,0 +1,124 @@
+package com.loopj.android.http;
+
+import org.apache.http.Header;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Used to intercept and handle the responses from requests made using
+ * {@link AsyncHttpClient}. The {@link #onSuccess(String)} method is
+ * designed to be anonymously overridden with your own response handling code.
+ *
+ * Additionally, you can override the {@link #onFailure(String, Throwable)},
+ * {@link #onStart()}, and {@link #onFinish()} methods as required.
+ *
+ * For example:
+ *
+ *
+ * AsyncHttpClient client = new AsyncHttpClient();
+ * client.get("http://www.google.com", new TextHttpResponseHandler() {
+ * @Override
+ * public void onStart() {
+ * // Initiated the request
+ * }
+ *
+ * @Override
+ * public void onSuccess(String responseBody) {
+ * // Successfully got a response
+ * }
+ *
+ * @Override
+ * public void onFailure(String responseBody, Throwable e) {
+ * // Response failed :(
+ * }
+ *
+ * @Override
+ * public void onFinish() {
+ * // Completed the request (either success or failure)
+ * }
+ * });
+ *
+ */
+public class TextHttpResponseHandler extends AsyncHttpResponseHandler {
+
+ private String _encoding;
+ /**
+ * Creates a new TextHttpResponseHandler
+ */
+
+ public TextHttpResponseHandler()
+ {
+ this("UTF-8");
+ }
+
+ public TextHttpResponseHandler(String encoding) {
+ super();
+ _encoding = encoding;
+ }
+ //
+ // Callbacks to be overridden, typically anonymously
+ //
+
+ /**
+ * Fired when a request returns successfully, override to handle in your own
+ * code
+ *
+ * @param responseBody the body of the HTTP response from the server
+ */
+ public void onSuccess(String responseBody) {
+ }
+
+ /**
+ * Fired when a request returns successfully, override to handle in your own
+ * code
+ *
+ * @param statusCode the status code of the response
+ * @param headers HTTP response headers
+ * @param responseBody the body of the HTTP response from the server
+ */
+ public void onSuccess(int statusCode, Header[] headers, String responseBody) {
+ onSuccess( responseBody );
+ }
+
+ /**
+ * Fired when a request fails to complete, override to handle in your own
+ * code
+ *
+ * @param responseBody the response body, if any
+ * @param error the underlying cause of the failure
+ */
+ public void onFailure(String responseBody, Throwable error) {
+ }
+
+ /**
+ * Fired when a request fails to complete, override to handle in your own
+ * code
+ *
+ * @param statusCode the status code of the response
+ * @param headers HTTP response headers
+ * @param responseBody the response body, if any
+ * @param error the underlying cause of the failure
+ */
+ public void onFailure(int statusCode, Header[] headers, String responseBody, Throwable error) {
+ onFailure( responseBody, error );
+ }
+
+ @Override
+ public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
+ try {
+ onSuccess(statusCode, headers, new String(responseBody, _encoding));
+ } catch (UnsupportedEncodingException e) {
+ onFailure(0, headers, (String) null, e);
+ }
+ }
+
+ @Override
+ public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
+ try {
+ onFailure(statusCode, headers, new String(responseBody, _encoding), error);
+ } catch (UnsupportedEncodingException e) {
+ onFailure(0, headers, (String) null, e);
+ }
+ }
+
+}
diff --git a/sample/src/main/java/com/loopj/android/http/sample/MainActivity.java b/sample/src/main/java/com/loopj/android/http/sample/MainActivity.java
index f5096ae2f..c5b373e6a 100644
--- a/sample/src/main/java/com/loopj/android/http/sample/MainActivity.java
+++ b/sample/src/main/java/com/loopj/android/http/sample/MainActivity.java
@@ -12,6 +12,7 @@
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
+import com.loopj.android.http.TextHttpResponseHandler;
import org.apache.http.Header;
@@ -59,7 +60,7 @@ public void onClick(View v) {
}
private void startRequest() {
- aclient.get(this, getURLString(), new AsyncHttpResponseHandler() {
+ aclient.get(this, getURLString(), new TextHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, String content) {