主要特点:处理异步Http请求,并通过匿名内部类处理回调结果,Http异步请求均位于非UI线程,不会阻塞UI操作,通过线程池处理并发请求处理文件上传、下载,响应结果自动打包JSON格式.
一, Android-async-http的项目链接
Github地址:https://github.com/loopj/android-async-http
V1.4.9的javadoc: https://loopj.com/android-async-http/doc/
在DowloadZip下载zip包解压后是Android Studio工程的目录如下:
examples:里面有简单的例子
library:里面存放的是android-async-http开源项目的源码(方法一:可以把library\src\main\Java文件下面的文件拷贝到,你应用的src下也可以直接使用)
releases:里面存放的是各个版本的jar文件,(方法二:只需把最新的jar文件拷贝到你应用的libs目录下即可.)
samples:里面存放的也是例子(可供参考)
备注:方法一和方法二只能采用其中之一,建议采用方法二
二,主要特性
1,Make asynchronous HTTP requests, handle responses in anonymous callbacks
异步http请求,匿名回调处理响应
2,HTTP requests happen outside the UI thread
在UI线程之外进行HTTP请求
3,Requests use a threadpool to cap concurrent resource usage
请求使用线程池,限制使用资源并发情况
4,GET/POST params builder (RequestParams)
使用RequestParams封装GET/POST请求参数
5,Multipart file uploads with no additional third party libraries
不使用第三方库多任务上传
6,Tiny size overhead to your application, only 60kb for everything
占用应用的控件少只需要60kb
7,Automatic smart request retries optimized for spotty mobile connections
自动智能重试请求
8,Optional built-in response parsing into JSON (JsonHttpResponseHandler)
内置响应解析成JSON,使用JsonHttpResponseHandler
9,Optional persistent cookie store, saves cookies into your app's SharedPreferences
有选择的持久化cookie存储,保存在app的SharedPreferences文件
三,核心类介绍
对于http请求网络的方式,无非就解决三个问题,第一,请求客户端的方法,第二,请求参数的封装,第三,请求响应的接收处理
先来看一下最基本的用法好有个大概的了解
AsyncHttpClient client = new AsyncHttpClient();
client.get("http://www.google.com", new AsyncHttpResponseHandler() {
@Override
public void onSuccess(String response) {
System.out.println(response);
}
});
1,Http客户端是谁?AsyncHttpClient
AsyncHttpClient:通过AsyncHttpClient类的实例就可以执行网络请求,包括get、put、post、head、delete。并指定一个ResponseHandlerInterface的实例接收请求结果。
核心类,使用HttpClient执行网络请求,提供了get,put,post,delete,head等请求方法,使用起来很简单,只需以url及RequestParams调用相应的方法即可,还可以选择性地传入Context,用于取消Content相关的请求,同时必须提供ResponseHandlerInterface(AsyncHttpResponseHandler继承自ResponseHandlerInterface)的实现类,一般为AsyncHttpResponseHandler的子类,AsyncHttpClient内部有一个线程池,当使用AsyncHttpClient执行网络请求时,最终都会调用sendRequest方法,在这个方法内部将请求参数封装成AsyncHttpRequest(继承自Runnable)交由内部的线程池执行
2,封装的请求对象是谁?
AsyncHttpRequest:继承自Runnabler,被submit至线程池执行网络请求并发送start,success等消息
3,请求参数和url怎么封装?
RequestParams:请求参数,可以添加普通的字符串参数,并可添加File,InputStream上传文件,内部使用Map添加Key-Value
3,Http请求怎样被执行?
AsyncHttpClient对象执行get等方法,将Context,Url,RequestParams,responseHandler等参数传入,在get()方法内部又封装成sendRequest方法,sendRequest方法内又封装请求AsyncHttpRequest,被提交到线程池的阻塞队列,等待执行,AsyncHttpRequest实现了Runnable方法,线程执行run()方法,在run方法内,responseHandler可以调用sendStartMessage(), makeRequestWithRetries(),sendFinishMessage()记录任务执行的过程日志Log,在执行makeRequestWithRetries方法中执行真正的网络执行语句HttpResponse response = client.execute(request, context);之后响应处理者调用sendResponseMessage(response),发送响应消息,最终转到OnSuccess(int statusCode, Header[] headers, byte[] responseBody);响应处理者子类只需要重写这个方法,对返回的响应做响应的处理
4,请求响应用什么方式处理?AsyncHttpResponseHandler, TextHttpResponseHandler, JsonHttpResponseHandler, BaseJsonHttpResponseHandler
继承图
接收请求结果,一般重写onSuccess及onFailure接收请求成功或失败的消息,还有onStart,onFinish等消息
//这个方法是子类必须重写的方法,来处理响应的
public abstract void onSuccess(int statusCode, Header[] headers, byte[] responseBody);
/**
* 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 abstract void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error);
继承AsyncHttpResponseHandler的子类,这是一个字节流返回处理的类, 该类用于处理图片,流的形式;
@Override
public abstract void onSuccess(int statusCode, Header[] headers, byte[] binaryData);
@Override
public abstract void onFailure(int statusCode, Header[] headers, byte[] binaryData, Throwable error);
继承自AsyncHttpResponseHandler,只是重写了AsyncHttpResponseHandler的onSuccess和onFailure方法,将请求结果由byte数组转换为String
/**
* Called when request fails
*
* @param statusCode http response status line
* @param headers response headers if any
* @param responseString string response of given charset
* @param throwable throwable returned when processing request
*/
public abstract void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable);
/**
* Called when request succeeds
*
* @param statusCode http response status line
* @param headers response headers if any
* @param responseString string response of given charset
*/
public abstract void onSuccess(int statusCode, Header[] headers, String responseString);
继承自TextHttpResponseHandler,是一个泛型类,提供了parseResponse方法,子类需要提供实现,将请求结果解析成需要的类型,子类可以灵活地使用解析方法,可以直接原始解析,使用gson等。
/**
* Returns when request succeeds
*
* @param statusCode http response status line
* @param headers response headers if any
* @param response parsed response if any
*/
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
AsyncHttpClient.log.w(LOG_TAG, "onSuccess(int, Header[], JSONObject) was not overriden, but callback was received");
}
/**
* Returns when request succeeds
*
* @param statusCode http response status line
* @param headers response headers if any
* @param response parsed response if any
*/
public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
AsyncHttpClient.log.w(LOG_TAG, "onSuccess(int, Header[], JSONArray) was not overriden, but callback was received");
}
/**
* Returns when request failed
*
* @param statusCode http response status line
* @param headers response headers if any
* @param throwable throwable describing the way request failed
* @param errorResponse parsed response if any
*/
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
AsyncHttpClient.log.w(LOG_TAG, "onFailure(int, Header[], Throwable, JSONObject) was not overriden, but callback was received", throwable);
}
/**
* Returns when request failed
*
* @param statusCode http response status line
* @param headers response headers if any
* @param throwable throwable describing the way request failed
* @param errorResponse parsed response if any
*/
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
AsyncHttpClient.log.w(LOG_TAG, "onFailure(int, Header[], Throwable, JSONArray) was not overriden, but callback was received", throwable);
}
@Override
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
AsyncHttpClient.log.w(LOG_TAG, "onFailure(int, Header[], String, Throwable) was not overriden, but callback was received", throwable);
}
@Override
public void onSuccess(int statusCode, Header[] headers, String responseString) {
AsyncHttpClient.log.w(LOG_TAG, "onSuccess(int, Header[], String) was not overriden, but callback was received");
}
/**
* Returns Object of type {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, Long,
* Double or {@link JSONObject#NULL}, see {@link org.json.JSONTokener#nextValue()}
*
* @param responseBody response bytes to be assembled in String and parsed as JSON
* @return Object parsedResponse
* @throws org.json.JSONException exception if thrown while parsing JSON
*/
protected Object parseResponse(byte[] 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
String jsonString = getResponseString(responseBody, getCharset());
if (jsonString != null) {
jsonString = jsonString.trim();
if (useRFC5179CompatibilityMode) {
if (jsonString.startsWith("{") || jsonString.startsWith("[")) {
result = new JSONTokener(jsonString).nextValue();
}
} else {
// Check if the string is an JSONObject style {} or JSONArray style []
// If not we consider this as a string
if ((jsonString.startsWith("{") && jsonString.endsWith("}"))
|| jsonString.startsWith("[") && jsonString.endsWith("]")) {
result = new JSONTokener(jsonString).nextValue();
}
// Check if this is a String "my String value" and remove quote
// Other value type (numerical, boolean) should be without quote
else if (jsonString.startsWith("\"") && jsonString.endsWith("\"")) {
result = jsonString.substring(1, jsonString.length() - 1);
}
}
}
if (result == null) {
result = jsonString;
}
return result;
}
1,调用AsyncHttpClient的get或post等方法发起网络请求
2,所有的请求都走了sendRequest,在sendRequest中把请求封装为AsyncHttpRequest,并添加到线程池执行,
3,当请求被执行时(即AsyncHttpRequest的run方法),执行AsyncHttpRequest的makeRequestWithRetries方法执行实际的请求,当请求失败时可以重试。并在请求开始,结束,成功或失败时向请求时传的ResponseHandlerInterface实例发送消息
4,基本上使用的都是AsyncHttpResponseHandler的子类,调用其onStart,onSuccess等方法返回请求结果
五,核心源码解读
1,AsyncHttpClient.java
以Get方法为例分析源码:
/**
* Perform a HTTP GET request, without any parameters.
* 执行一个没有参数的HTTP Get请求
* @param url the URL to send the request to. 请求Url
* @param responseHandler the response handler instance that should handle the response.处理响应对象
* @return RequestHandle of future request process
*/
public RequestHandle get(String url, ResponseHandlerInterface responseHandler) {
return get(null, url, null, responseHandler);//转到另一个重载函数,四个参数是Context,Url,RequestParams,ResponseHandler
}
// [-] HTTP GET
// [+] HTTP POST
/**
* Perform a HTTP GET request with parameters.
*执行一个有参数的HTTP Get请求
* @param url the URL to send the request to.
* @param params additional GET parameters to send with the request.
* @param responseHandler the response handler instance that should handle the response.
* @return RequestHandle of future request process
*/
public RequestHandle get(String url, RequestParams params, ResponseHandlerInterface responseHandler) {
return get(null, url, params, responseHandler);//转到另一个重载函数,四个参数是Context,Url,RequestParams,ResponseHandler
}
/**
* Perform a HTTP GET request without any parameters and track the Android Context which
* initiated the request.
*
* @param context the Android Context which initiated the request.
* @param url the URL to send the request to.
* @param responseHandler the response handler instance that should handle the response.
* @return RequestHandle of future request process
*/
public RequestHandle get(Context context, String url, ResponseHandlerInterface responseHandler) {
return get(context, url, null, responseHandler);
}
/**
* Perform a HTTP GET request and track the Android Context which initiated the request.
*执行一个Http Get请求,有Context参数,初始化request
* @param context the Android Context which initiated the request. // android上下文context
* @param url the URL to send the request to.//请去的url
* @param params additional GET parameters to send with the request.//请求的参数
* @param responseHandler the response handler instance that should handle the response.//处理响应对象
* @return RequestHandle of future request process
*/
public RequestHandle get(Context context, String url, RequestParams params, ResponseHandlerInterface responseHandler) {
/*
* 封装sendRequest的方法,感觉和HttpClient的方式相似,不是很懂,参数如下
*1,DefaultHttpClient httpClient
*2,HttpContext httpContext
*3,HttpGet httpget
*4,
*5,ResponseHandlerInterface responseHandler
*6,Context context
*/
return sendRequest(httpClient, httpContext, new HttpGet(getUrlWithQueryString(isUrlEncodingEnabled, url, params)), null, responseHandler, context);
}
/**
* Perform a HTTP GET request and track the Android Context which initiated the request with
* customized headers
* 执行有自定义Header的请求
* @param context Context to execute request against
* @param url the URL to send the request to.
* @param headers set headers only for this request
* @param params additional GET parameters to send with the request.
* @param responseHandler the response handler instance that should handle the response.
* @return RequestHandle of future request process
*/
public RequestHandle get(Context context, String url, Header[] headers, RequestParams params, ResponseHandlerInterface responseHandler) {
HttpUriRequest request = new HttpGet(getUrlWithQueryString(isUrlEncodingEnabled, url, params));
if (headers != null) request.setHeaders(headers);//自定义的Header加入到,HttpGet对象中
return sendRequest(httpClient, httpContext, request, null, responseHandler,
context);
}
/**
* Puts a new request in queue as a new thread in pool to be executed
* 把一个新的请求放入线程池的队列被执行
* @param client HttpClient to be used for request, can differ in single requests
* @param contentType MIME body type, for POST and PUT requests, may be null
* @param context Context of Android application, to hold the reference of request
* @param httpContext HttpContext in which the request will be executed
* @param responseHandler ResponseHandler or its subclass to put the response into
* @param uriRequest instance of HttpUriRequest, which means it must be of HttpDelete,
* HttpPost, HttpGet, HttpPut, etc.
* @return RequestHandle of future request process
*/
/*
* 封装sendRequest的方法,感觉和HttpClient的方式相似,不是很懂,参数如下
*1,DefaultHttpClient httpClient
*2,HttpContext httpContext
*3,HttpGet httpget
*4,String contentType,
*5,ResponseHandlerInterface responseHandler
*6,Context context
*/
protected RequestHandle sendRequest(DefaultHttpClient client, HttpContext httpContext, HttpUriRequest uriRequest, String contentType, ResponseHandlerInterface responseHandler, Context context) {
if (uriRequest == null) {
throw new IllegalArgumentException("HttpUriRequest must not be null");
}
if (responseHandler == null) {
throw new IllegalArgumentException("ResponseHandler must not be null");
}
if (responseHandler.getUseSynchronousMode() && !responseHandler.getUsePoolThread()) {
throw new IllegalArgumentException("Synchronous ResponseHandler used in AsyncHttpClient. You should create your response handler in a looper thread or use SyncHttpClient instead.");
}
if (contentType != null) {
if (uriRequest instanceof HttpEntityEnclosingRequestBase && ((HttpEntityEnclosingRequestBase) uriRequest).getEntity() != null && uriRequest.containsHeader(HEADER_CONTENT_TYPE)) {
log.w(LOG_TAG, "Passed contentType will be ignored because HttpEntity sets content type");
} else {
uriRequest.setHeader(HEADER_CONTENT_TYPE, contentType);
}
}
//responseHandler设置请求头和请去Url
responseHandler.setRequestHeaders(uriRequest.getAllHeaders());
responseHandler.setRequestURI(uriRequest.getURI());
//构造一个执行请求的AsyncHttpRequest对象,准备被发送到线程池的任务队列,等待执行AsyncHttpRequest对象中的run方法
AsyncHttpRequest request = newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context);
threadPool.submit(request);//重点!!!!: 把AsyncHttpRequest对象提交到线程池等待执行
/**
* A Handle to an AsyncRequest which can be used to cancel a running request.
* 一个可以用来取消正在运行的请求的手柄或者说是操作者
*/
RequestHandle requestHandle = new RequestHandle(request);
if (context != null) {
List requestList;
// Add request to request map
synchronized (requestMap) {
requestList = requestMap.get(context);
if (requestList == null) {
requestList = Collections.synchronizedList(new LinkedList());
requestMap.put(context, requestList);
}
}
requestList.add(requestHandle);
Iterator iterator = requestList.iterator();
while (iterator.hasNext()) {
if (iterator.next().shouldBeGarbageCollected()) {
iterator.remove();
}
}
}
return requestHandle;//返回手柄
}
AsyncHttpRequest.java
/**
* Internal class, representing the HttpRequest, done in asynchronous manner
*/
//异步HTTP请求对象,实现的是Runnable接口,说明AsyncHttpRequest是一个供线程执行的任务类,主要关注run方法
public class AsyncHttpRequest implements Runnable {
private final AbstractHttpClient client;//Http客户端,Httpclient
private final HttpContext context;
private final HttpUriRequest request;//保存HttpGet对象
private final ResponseHandlerInterface responseHandler;//保存响应处理者对象,可以跟踪任务的执行,start,fihish等
private final AtomicBoolean isCancelled = new AtomicBoolean();
private int executionCount;
private boolean cancelIsNotified;
private volatile boolean isFinished;
private boolean isRequestPreProcessed;
public AsyncHttpRequest(AbstractHttpClient client, HttpContext context, HttpUriRequest request, ResponseHandlerInterface responseHandler) {
this.client = Utils.notNull(client, "client");
this.context = Utils.notNull(context, "context");
this.request = Utils.notNull(request, "request");
this.responseHandler = Utils.notNull(responseHandler, "responseHandler");
}
/**
* This method is called once by the system when the request is about to be
* processed by the system. The library makes sure that a single request
* is pre-processed only once.
*
* Please note: pre-processing does NOT run on the main thread, and thus
* any UI activities that you must perform should be properly dispatched to
* the app's UI thread.
*
* @param request The request to pre-process
*/
public void onPreProcessRequest(AsyncHttpRequest request) {
// default action is to do nothing...
}
/**
* This method is called once by the system when the request has been fully
* sent, handled and finished. The library makes sure that a single request
* is post-processed only once.
*
* Please note: post-processing does NOT run on the main thread, and thus
* any UI activities that you must perform should be properly dispatched to
* the app's UI thread.
*
* @param request The request to post-process
*/
public void onPostProcessRequest(AsyncHttpRequest request) {
// default action is to do nothing...
}
/*
* 在线程池中执行run方法过程
*/
@Override
public void run() {
if (isCancelled()) {
return;
}
// Carry out pre-processing for this request only once.
if (!isRequestPreProcessed) {
isRequestPreProcessed = true;
onPreProcessRequest(this);
}
if (isCancelled()) {
return;
}
//responseHandler发送开始消息,响应处理者设置start消息
responseHandler.sendStartMessage();
if (isCancelled()) {
return;
}
try {
//重点是这个方法!!!
makeRequestWithRetries();
} catch (IOException e) {
if (!isCancelled()) {
//任务执行失败,响应处理者设置failure消息
responseHandler.sendFailureMessage(0, null, null, e);
} else {
AsyncHttpClient.log.e("AsyncHttpRequest", "makeRequestWithRetries returned error", e);
}
}
if (isCancelled()) {
return;
}
//任务执行完成,响应处理者设置finish消息
responseHandler.sendFinishMessage();
if (isCancelled()) {
return;
}
// Carry out post-processing for this request.
//任务执行完毕,调用这个方法
onPostProcessRequest(this);
isFinished = true;
}
//makeRequest
private void makeRequest() throws IOException {
if (isCancelled()) {
return;
}
// Fixes #115
if (request.getURI().getScheme() == null) {
// subclass of IOException so processed in the caller
throw new MalformedURLException("No valid URI scheme was provided");
}
if (responseHandler instanceof RangeFileAsyncHttpResponseHandler) {
((RangeFileAsyncHttpResponseHandler) responseHandler).updateRequestHeaders(request);
}
//终于看到这一句了,感觉就是我们常用的HttpClient,HttpGet,HttpResponse模式
HttpResponse response = client.execute(request, context);
if (isCancelled()) {
return;
}
// Carry out pre-processing for this response.
//翻译是在返回响应之前预处理
responseHandler.onPreProcessResponse(responseHandler, response);
if (isCancelled()) {
return;
}
// The response is ready, handle it.
//猜到就是把HttpResponse对象封装到响应处理者responseHandler中,responseHandler中肯定有保存HttpResponse对象的变量
responseHandler.sendResponseMessage(response);
if (isCancelled()) {
return;
}
// Carry out post-processing for this response.
//返回响应之后的后处理,一个请求就这么处理完了,接下来只需要出去处理responseHandler就可以得到响应了
responseHandler.onPostProcessResponse(responseHandler, response);
}
//重点是这个方法!!!做请求带有重试次数的
private void makeRequestWithRetries() throws IOException {
boolean retry = true;
IOException cause = null;
HttpRequestRetryHandler retryHandler = client.getHttpRequestRetryHandler();
try {
while (retry) {
try {
//重点是去makerequest
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(), e);
//如果执行抛出异常,会重试连接,可能是这样理解的
retry = (executionCount > 0) && retryHandler.retryRequest(e, ++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
// https://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) {
if (isCancelled()) {
// Eating exception, as the request was cancelled
return;
}
cause = e;
retry = retryHandler.retryRequest(cause, ++executionCount, context);
}
if (retry) {
responseHandler.sendRetryMessage(executionCount);
}
}
} catch (Exception e) {
// catch anything else to ensure failure message is propagated
AsyncHttpClient.log.e("AsyncHttpRequest", "Unhandled exception origin cause", e);
cause = new IOException("Unhandled exception: " + e.getMessage(), cause);
}
// cleaned up to throw IOException
throw (cause);
}
AsyncHttpResponseHandler.java
/**
* Fired when the request is started, override to handle in your own code
*/
/*asyncHttpRequst的run方法执行时调用sendStartMessage发送开始消息
* 其实就是转入这个OnStart函数,如果想跟踪任务执行的话写log就重写这个方法
*/
public void onStart() {
// default log warning is not necessary, because this method is just optional notification
}
/**
* Fired in all cases when the request is finished, after both success and failure, override to
* handle in your own code
*/
/*asyncHttpRequst的run方法执行时调用sendFinishMessage发送开始消息
* 其实就是转入这个onFinish函数,如果想跟踪任务执行的话写log就重写这个方法
*/
public void onFinish() {
// default log warning is not necessary, because this method is just optional notification
}
/*
*asyncHttpRequst的run方法执行到makeRequest方法时,执行完毕会调用一下两个函数
*onPreProcessResponse 是指在返回响应之前的预处理
*onPostProcessResponse 是指返回响应之后的处理
*/
@Override
public void onPreProcessResponse(ResponseHandlerInterface instance, HttpResponse response) {
// default action is to do nothing...
}
@Override
public void onPostProcessResponse(ResponseHandlerInterface instance, HttpResponse response) {
// default action is to do nothing...
}
/**
* 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 响应的Header头信息
* @param responseBody the body of the HTTP response from the server 响应体
*/
//这个方法是子类必须重写的方法,来处理响应的
public abstract void onSuccess(int statusCode, Header[] headers, byte[] responseBody);
/**
* 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 abstract void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error);
// Methods which emulate android's Handler and Message methods
protected void handleMessage(Message message) {
Object[] response;
try {
switch (message.what) {
case SUCCESS_MESSAGE:
response = (Object[]) message.obj;
if (response != null && response.length >= 3) {
//调用onSuccess方法
onSuccess((Integer) response[0], (Header[]) response[1], (byte[]) response[2]);
} else {
AsyncHttpClient.log.e(LOG_TAG, "SUCCESS_MESSAGE didn't got enough params");
}
break;
case FAILURE_MESSAGE:
response = (Object[]) message.obj;
if (response != null && response.length >= 4) {
//调用onFailure方法
onFailure((Integer) response[0], (Header[]) response[1], (byte[]) response[2], (Throwable) response[3]);
} else {
AsyncHttpClient.log.e(LOG_TAG, "FAILURE_MESSAGE didn't got enough params");
}
break;
case START_MESSAGE:
//调用onStart方法
onStart();
break;
case FINISH_MESSAGE:
//调用onFinish方法
onFinish();
break;
case PROGRESS_MESSAGE:
response = (Object[]) message.obj;
if (response != null && response.length >= 2) {
try {
onProgress((Long) response[0], (Long) response[1]);
} catch (Throwable t) {
AsyncHttpClient.log.e(LOG_TAG, "custom onProgress contains an error", t);
}
} else {
AsyncHttpClient.log.e(LOG_TAG, "PROGRESS_MESSAGE didn't got enough params");
}
break;
case RETRY_MESSAGE:
response = (Object[]) message.obj;
if (response != null && response.length == 1) {
onRetry((Integer) response[0]);
} else {
AsyncHttpClient.log.e(LOG_TAG, "RETRY_MESSAGE didn't get enough params");
}
break;
case CANCEL_MESSAGE:
//调用onFinish方法
onCancel();
break;
}
} catch (Throwable error) {
onUserException(error);
}
}
}
/*asyncHttpRequst的run方法执行到makeRequest方法时,获得HttpResponse后会调用如下方法sendResponseMessage
*
*
**/
@Override
public void sendResponseMessage(HttpResponse response) throws IOException {
// do not process if request has been cancelled
//因为asyncHttpRequst在线程池中执行,先判断线程
if (!Thread.currentThread().isInterrupted()) {
//获取状态行
StatusLine status = response.getStatusLine();
byte[] responseBody;
//获取响应体的字节数组
responseBody = getResponseData(response.getEntity());
// additional cancellation check as getResponseData() can take non-zero time to process
if (!Thread.currentThread().isInterrupted()) {
if (status.getStatusCode() >= 300) {
//如果响应码大于等于300,则发送失败消息,失败和成功消息是判断响应码发生的,开始和结束消息是任务执行发生的
sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), responseBody, new HttpResponseException(status.getStatusCode(), status.getReasonPhrase()));
} else {
//否则发送成功消息,参数是响应码,响应头信息,响应体
sendSuccessMessage(status.getStatusCode(), response.getAllHeaders(), responseBody);
}
}
}
}
AsyncRequestParams.java
public class RequestParams implements Serializable {
public final static String APPLICATION_OCTET_STREAM =
"application/octet-stream";
public final static String APPLICATION_JSON =
"application/json";
protected final static String LOG_TAG = "RequestParams";
//d都是用的是并发HashMap,Url的参数
protected final ConcurrentHashMap urlParams = new ConcurrentHashMap();
//各种流的参数HashMap
protected final ConcurrentHashMap streamParams = new ConcurrentHashMap();
//各种文件的参数HashMap
protected final ConcurrentHashMap fileParams = new ConcurrentHashMap();
//多文件的参数HashMap
protected final ConcurrentHashMap> fileArrayParams = new ConcurrentHashMap>();
//带对象的Url的参数HashMap
protected final ConcurrentHashMap urlParamsWithObjects = new ConcurrentHashMap();
protected boolean isRepeatable;
protected boolean forceMultipartEntity = false;
protected boolean useJsonStreamer;
protected String elapsedFieldInJsonStreamer = "_elapsed";
protected boolean autoCloseInputStreams;
protected String contentEncoding = HTTP.UTF_8;
/**
* Constructs a new empty {@code RequestParams} instance.
*/
public RequestParams() {
this((Map) null);
}
/**
* Constructs a new RequestParams instance containing the key/value string params from the
* specified map.
*
* @param source the source key/value string map to add.
*/
public RequestParams(Map source) {
if (source != null) {
for (Map.Entry entry : source.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
}
/**
* Constructs a new RequestParams instance and populate it with a single initial key/value
* string param.
*
* @param key the key name for the intial param.
* @param value the value string for the initial param.
*/
public RequestParams(final String key, final String value) {
this(new HashMap() {{
put(key, value);
}});
}
/**
* Constructs a new RequestParams instance and populate it with multiple initial key/value
* string param.
*
* @param keysAndValues a sequence of keys and values. Objects are automatically converted to
* Strings (including the value {@code null}).
* @throws IllegalArgumentException if the number of arguments isn't even.
*/
public RequestParams(Object... keysAndValues) {
int len = keysAndValues.length;
if (len % 2 != 0)
throw new IllegalArgumentException("Supplied arguments must be even");
for (int i = 0; i < len; i += 2) {
String key = String.valueOf(keysAndValues[i]);
String val = String.valueOf(keysAndValues[i + 1]);
put(key, val);
}
}
/**
* Sets content encoding for return value of {@link #getParamString()} and {@link
* #createFormEntity()}
Default encoding is "UTF-8"
*
* @param encoding String constant from {@link HTTP}
*/
public void setContentEncoding(final String encoding) {
if (encoding != null) {
this.contentEncoding = encoding;
} else {
AsyncHttpClient.log.d(LOG_TAG, "setContentEncoding called with null attribute");
}
}
/**
* If set to true will force Content-Type header to `multipart/form-data`
* even if there are not Files or Streams to be send
*
* Default value is false
*
* @param force boolean, should declare content-type multipart/form-data even without files or streams present
*/
public void setForceMultipartEntityContentType(boolean force) {
this.forceMultipartEntity = force;
}
/**
* Adds a key/value string pair to the request.
*
* @param key the key name for the new param.
* @param value the value string for the new param.
*/
public void put(String key, String value) {
if (key != null && value != null) {
urlParams.put(key, value);
}
}
/**
* Adds files array to the request.
*
* @param key the key name for the new param.
* @param files the files array to add.
* @throws FileNotFoundException if one of passed files is not found at time of assembling the requestparams into request
*/
public void put(String key, File files[]) throws FileNotFoundException {
put(key, files, null, null);
}
/**
* Adds files array to the request with both custom provided file content-type and files name
*
* @param key the key name for the new param.
* @param files the files array to add.
* @param contentType the content type of the file, eg. application/json
* @param customFileName file name to use instead of real file name
* @throws FileNotFoundException throws if wrong File argument was passed
*/
public void put(String key, File files[], String contentType, String customFileName) throws FileNotFoundException {
if (key != null) {
List fileWrappers = new ArrayList();
for (File file : files) {
if (file == null || !file.exists()) {
throw new FileNotFoundException();
}
fileWrappers.add(new FileWrapper(file, contentType, customFileName));
}
fileArrayParams.put(key, fileWrappers);
}
}
/**
* Adds a file to the request.
*
* @param key the key name for the new param.
* @param file the file to add.
* @throws FileNotFoundException throws if wrong File argument was passed
*/
public void put(String key, File file) throws FileNotFoundException {
put(key, file, null, null);
}
/**
* Adds a file to the request with custom provided file name
*
* @param key the key name for the new param.
* @param file the file to add.
* @param customFileName file name to use instead of real file name
* @throws FileNotFoundException throws if wrong File argument was passed
*/
public void put(String key, String customFileName, File file) throws FileNotFoundException {
put(key, file, null, customFileName);
}
/**
* Adds a file to the request with custom provided file content-type
*
* @param key the key name for the new param.
* @param file the file to add.
* @param contentType the content type of the file, eg. application/json
* @throws FileNotFoundException throws if wrong File argument was passed
*/
public void put(String key, File file, String contentType) throws FileNotFoundException {
put(key, file, contentType, null);
}
/**
* Adds a file to the request with both custom provided file content-type and file name
*
* @param key the key name for the new param.
* @param file the file to add.
* @param contentType the content type of the file, eg. application/json
* @param customFileName file name to use instead of real file name
* @throws FileNotFoundException throws if wrong File argument was passed
*/
public void put(String key, File file, String contentType, String customFileName) throws FileNotFoundException {
if (file == null || !file.exists()) {
throw new FileNotFoundException();
}
if (key != null) {
fileParams.put(key, new FileWrapper(file, contentType, customFileName));
}
}
/**
* Adds an input stream to the request.
*
* @param key the key name for the new param.
* @param stream the input stream to add.
*/
public void put(String key, InputStream stream) {
put(key, stream, null);
}
/**
* Adds an input stream to the request.
*
* @param key the key name for the new param.
* @param stream the input stream to add.
* @param name the name of the stream.
*/
public void put(String key, InputStream stream, String name) {
put(key, stream, name, null);
}
/**
* Adds an input stream to the request.
*
* @param key the key name for the new param.
* @param stream the input stream to add.
* @param name the name of the stream.
* @param contentType the content type of the file, eg. application/json
*/
public void put(String key, InputStream stream, String name, String contentType) {
put(key, stream, name, contentType, autoCloseInputStreams);
}
/**
* Adds an input stream to the request.
*
* @param key the key name for the new param.
* @param stream the input stream to add.
* @param name the name of the stream.
* @param contentType the content type of the file, eg. application/json
* @param autoClose close input stream automatically on successful upload
*/
public void put(String key, InputStream stream, String name, String contentType, boolean autoClose) {
if (key != null && stream != null) {
streamParams.put(key, StreamWrapper.newInstance(stream, name, contentType, autoClose));
}
}
/**
* Adds param with non-string value (e.g. Map, List, Set).
*
* @param key the key name for the new param.
* @param value the non-string value object for the new param.
*/
public void put(String key, Object value) {
if (key != null && value != null) {
urlParamsWithObjects.put(key, value);
}
}
/**
* Adds a int value to the request.
*
* @param key the key name for the new param.
* @param value the value int for the new param.
*/
public void put(String key, int value) {
if (key != null) {
urlParams.put(key, String.valueOf(value));
}
}
/**
* Adds a long value to the request.
*
* @param key the key name for the new param.
* @param value the value long for the new param.
*/
public void put(String key, long value) {
if (key != null) {
urlParams.put(key, String.valueOf(value));
}
}
/**
* Adds string value to param which can have more than one value.
*
* @param key the key name for the param, either existing or new.
* @param value the value string for the new param.
*/
public void add(String key, String value) {
if (key != null && value != null) {
Object params = urlParamsWithObjects.get(key);
if (params == null) {
// Backward compatible, which will result in "k=v1&k=v2&k=v3"
params = new HashSet();
this.put(key, params);
}
if (params instanceof List) {
((List
官方建议:Recommended Usage: Make a Static Http Client
import com.loopj.android.http.*;
public class TwitterRestClient {
private static final String BASE_URL = "https://api.twitter.com/1/";
private static AsyncHttpClient client = new AsyncHttpClient();
public static void get(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
client.get(getAbsoluteUrl(url), params, responseHandler);
}
public static void post(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
client.post(getAbsoluteUrl(url), params, responseHandler);
}
private static String getAbsoluteUrl(String relativeUrl) {
return BASE_URL + relativeUrl;
}
}
AsyncRequestParams.java'
RequestParams params = new RequestParams();
params.put("username", "yanbober");
params.put("password", "123456");
params.put("email", "[email protected]");
/*
* Upload a File
*/
params.put("file_pic", new File("test.jpg"));
params.put("file_inputStream", inputStream);
params.put("file_bytes", new ByteArrayInputStream(bytes));
/*
* url params: "user[first_name]=jesse&user[last_name]=yan"
*/
Map map = new HashMap();
map.put("first_name", "jesse");
map.put("last_name", "yan");
params.put("user", map);
/*
* url params: "what=haha&like=wowo"
*/
Set set = new HashSet();
set.add("haha");
set.add("wowo");
params.put("what", set);
/*
* url params: "languages[]=Java&languages[]=C"
*/
List list = new ArrayList();
list.add("Java");
list.add("C");
params.put("languages", list);
/*
* url params: "colors[]=blue&colors[]=yellow"
*/
String[] colors = { "blue", "yellow" };
params.put("colors", colors);
/*
* url params: "users[][age]=30&users[][gender]=male&users[][age]=25&users[][gender]=female"
*/
List
JsonHttpResponseHandler带Json参数的POST:
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("username", "ryantang");
StringEntity stringEntity = new StringEntity(jsonObject.toString());
client.post(mContext, "http://api.com/login", stringEntity, "application/json", new JsonHttpResponseHandler(){
@Override
public void onSuccess(JSONObject jsonObject) {
super.onSuccess(jsonObject);
}
});
} catch (JSONException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
BinaryHttpResponseHandler下载文件:
client.get("http://download/file/test.java", new BinaryHttpResponseHandler() {
@Override
public void onSuccess(byte[] arg0) {
super.onSuccess(arg0);
File file = Environment.getExternalStorageDirectory();
File file2 = new File(file, "down");
file2.mkdir();
file2 = new File(file2, "down_file.jpg");
try {
FileOutputStream oStream = new FileOutputStream(file2);
oStream.write(arg0);
oStream.flush();
oStream.close();
} catch (Exception e) {
e.printStackTrace();
Log.i(null, e.toString());
}
}
});
支持相应文件图片上传的话:
String path="http://sv1.livechano.com:8080/upload.action?&action=1.6&type=1&ext=png";
File myFile = new File("/sdcard/test.png");
RequestParams params = new RequestParams();
try {
params.put("image", myFile,"application/octet-stream");
AsyncHttpClient client = new AsyncHttpClient();
client.post(path, params, new AsyncHttpResponseHandler(){
@Override
public void onFailure(Throwable error, String content) {
// TODO Auto-generated method stub
super.onFailure(error, content);
Toast.makeText(MainActivity.this, "上传失败!"+content, Toast.LENGTH_LONG).show();
}
@Override
public void onSuccess(int statusCode, String content) {
// TODO Auto-generated method stub
super.onSuccess(statusCode, content);
System.out
.println("content: "+content);
Toast.makeText(MainActivity.this, "上传成功!"+content, Toast.LENGTH_LONG).show();
}
});
} catch(FileNotFoundException e) {
}
更多参考:
http://my.oschina.net/penngo/blog/488128
http://www.cnblogs.com/manuosex/p/3583775.html
http://blog.csdn.net/wangwei_cq/article/details/9453345
http://blog.csdn.net/yanbober/article/details/45307549
http://www.cnblogs.com/angeldevil/p/3729808.html
http://blog.csdn.net/finddreams/article/details/50887506
http://www.open-open.com/lib/view/open1438784972504.html
http://blog.csdn.net/redarmy_chen/article/details/26980613