它是专门针对Android在Apache的HttpClient基础上构建的异步的callback-based http client。所有的请求
全在UI线程之外发生,而callback发生在创建它的线程中,应用了Android的Handler发送消息机制。你也可以把AsyncHttpClient应用在
Service中或者后台线程中,库代码会自动识别出它所运行的context。它的feature包括:
1. 发送异步http请求,在匿名callback对象中处理response;
2. http请求发生在UI线程之外;
3. 内部采用线程池来处理并发请求;
4. GET/POST 参数构造,通过RequestParams类。
5. 内置多部分文件上传,不需要第三方库支持;
6. 流式Json上传,不需要额外的库;
7. 能处理环行和相对重定向;
8. 和你的app大小相比来说,库的size很小,所有的一切只有90kb;
9. 自动智能的请求重试机制在各种各样的移动连接环境中;
10. 自动的gzip响应解码;
11. 内置多种形式的响应解析,有原生的字节流,string,json对象,甚至可以将response写到文件中;
12. 永久的cookie保存,内部实现用的是Android的SharedPreferences;
13. 通过BaseJsonHttpResponseHandler和
各种json库集成;
14. 支持SAX解析器;
15. 支持各种语言和content编码,不仅仅是UTF-8。
简单来说你只需要3步,
1. 创建一个AsyncHttpClient;
2. (可选的)通过RequestParams对象设置请求参数;
3. 调用AsyncHttpClient的某个get方法,传递你需要的(成功和失败时)callback接口实现,一般都是匿名内部类
,实现了AsyncHttpResponseHandler,类库自己也提供了好些现成的response handler,你一般不需要自己创建一个。
来看看代码如何写:
1 AsyncHttpClient client = new AsyncHttpClient(); 2 client.get("http://www.google.com", new AsyncHttpResponseHandler() { 3 4 @Override 5 public void onStart() { 6 // called before request is started 7 } 8 9 @Override 10 public void onSuccess(int statusCode, Header[] headers, byte[] response) { 11 // called when response HTTP status is "200 OK" 12 } 13 14 @Override 15 public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) { 16 // called when response HTTP status is "4XX" (eg. 401, 403, 404) 17 } 18 19 @Override 20 public void onRetry(int retryNo) { 21 // called when request is retried 22 } 23 });
这里你只需要通过匿名内部类的方式实现AsyncHttpResponseHandler,也可以通过RequestParams来传递各种参数
1 AsyncHttpClient client = new AsyncHttpClient(); 2 RequestParams params = new RequestParams(); 3 params.put("key", "value"); 4 params.put("more", "data"); 5 client.get("http://www.google.com", params, new 6 AsyncHttpResponseHandler() { 7 @Override 8 public void onSuccess(int statusCode, Header[] headers, byte[] response) { 9 System.out.println(response); 10 } 11 12 @Override 13 public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) { 14 Log.d("ERROR", error); 15 } 16 } 17 );
以上的例子是返回的response直接是原生字节流的情况,如果你需要把返回的结果当一个String对待,这时只需要匿名实现一个
TextHttpResponseHandler就行,其继承自AsyncHttpResponse,并将原生的字节流根据指定的encoding转化成了string对象,
代码如下:
1 AsyncHttpClient client = new AsyncHttpClient(); 2 RequestParams params = new RequestParams(); 3 params.put("key", "value"); 4 params.put("more", "data"); 5 client.get("http://www.google.com", params, new 6 TextHttpResponseHandler() { 7 @Override 8 public void onSuccess(int statusCode, Header[] headers, String response) { 9 System.out.println(response); 10 } 11 12 @Override 13 public void onFailure(int statusCode, Header[] headers, String responseBody, Throwable error) { 14 Log.d("ERROR", error); 15 } 16 } 17 );
同样的方式,你可以发送json请求,代码如下:
1 String url = "https://ajax.googleapis.com/ajax/services/search/images"; 2 AsyncHttpClient client = new AsyncHttpClient(); 3 RequestParams params = new RequestParams(); 4 params.put("q", "android"); 5 params.put("rsz", "8"); 6 client.get(url, params, new JsonHttpResponseHandler() { 7 @Override 8 public void onSuccess(int statusCode, Header[] headers, JSONObject response) { 9 // Handle resulting parsed JSON response here 10 } 11 @Override 12 public void onSuccess(int statusCode, Header[] headers, JSONArray response) { 13 // Handle resulting parsed JSON response here 14 } 15 });
返回的response已经自动转化成JSONObject了,当然也支持JSONArray类型,override你需要的那个版本就行。
AsyncHttpClient 源码分析
1. AsyncHttpClient自己一个模块;
2. AsyncHttpRequest和RequestHandler一个模块;
3. AsyncHttpResponseHandler及其各种特定子类一个模块;
4. RetryHandler,自动重试机制。
我们可以很清楚的看出门道来,大体是按照client、request、response,这样的方式组织的。接下来我们的代码分析也就按照这个顺序进行。
先来说AsyncHttpClient,来看其关键字段和ctor,代码如下:
1 public static final String LOG_TAG = "AsyncHttpClient"; 2 3 public static final String HEADER_CONTENT_TYPE = "Content-Type"; 4 public static final String HEADER_CONTENT_RANGE = "Content-Range"; 5 public static final String HEADER_CONTENT_ENCODING = "Content-Encoding"; 6 public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition"; 7 public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding"; 8 public static final String ENCODING_GZIP = "gzip"; 9 10 public static final int DEFAULT_MAX_CONNECTIONS = 10; 11 public static final int DEFAULT_SOCKET_TIMEOUT = 10 * 1000; 12 public static final int DEFAULT_MAX_RETRIES = 5; 13 public static final int DEFAULT_RETRY_SLEEP_TIME_MILLIS = 1500; 14 public static final int DEFAULT_SOCKET_BUFFER_SIZE = 8192; 15 16 private int maxConnections = DEFAULT_MAX_CONNECTIONS; 17 private int connectTimeout = DEFAULT_SOCKET_TIMEOUT; 18 private int responseTimeout = DEFAULT_SOCKET_TIMEOUT; // 各种参数设置 19 20 private final DefaultHttpClient httpClient; // 包装的Apache DefaultHttpClient 21 private final HttpContext httpContext; 22 private ExecutorService threadPool; // 执行网络请求的线程池 23 private final Map<Context, List<RequestHandle>> requestMap; // 与Android Context对应的请求map 24 private final Map<String, String> clientHeaderMap; // 客户端的请求header map 25 private boolean isUrlEncodingEnabled = true; // 允许url encoding
接下来看看各种ctor,如下:
1 /** 2 * Creates a new AsyncHttpClient with default constructor arguments values 3 */ 4 public AsyncHttpClient() { // 一般客户端代码中都直接调用这个版本的ctor 5 this(false, 80, 443); 6 } 7 8 /** 9 * Creates a new AsyncHttpClient. 10 * 11 * @param httpPort non-standard HTTP-only port 12 */ 13 public AsyncHttpClient(int httpPort) { 14 this(false, httpPort, 443); 15 } 16 17 /** 18 * Creates a new AsyncHttpClient. 19 * 20 * @param httpPort non-standard HTTP-only port 21 * @param httpsPort non-standard HTTPS-only port 22 */ 23 public AsyncHttpClient(int httpPort, int httpsPort) { 24 this(false, httpPort, httpsPort); 25 } 26 27 /** 28 * Creates new AsyncHttpClient using given params 29 * 30 * @param fixNoHttpResponseException Whether to fix or not issue, by omitting SSL verification 31 * @param httpPort HTTP port to be used, must be greater than 0 32 * @param httpsPort HTTPS port to be used, must be greater than 0 33 */ 34 public AsyncHttpClient(boolean fixNoHttpResponseException, int httpPort, int httpsPort) { 35 this(getDefaultSchemeRegistry(fixNoHttpResponseException, httpPort, httpsPort)); 36 } 37 38 /** 39 * Returns default instance of SchemeRegistry 40 * 41 * @param fixNoHttpResponseException Whether to fix or not issue, by omitting SSL verification 42 * @param httpPort HTTP port to be used, must be greater than 0 43 * @param httpsPort HTTPS port to be used, must be greater than 0 44 */ 45 private static SchemeRegistry getDefaultSchemeRegistry(boolean fixNoHttpResponseException, int httpPort, int httpsPort) { 46 if (fixNoHttpResponseException) { // 如果你请求的url是https的,并且遇到了SSL验证之类的错误,那么你应该将此值设为true试试 47 Log.d(LOG_TAG, "Beware! Using the fix is insecure, as it doesn‘t verify SSL certificates."); 48 } 49 50 if (httpPort < 1) { 51 httpPort = 80; 52 Log.d(LOG_TAG, "Invalid HTTP port number specified, defaulting to 80"); 53 } 54 55 if (httpsPort < 1) { 56 httpsPort = 443; 57 Log.d(LOG_TAG, "Invalid HTTPS port number specified, defaulting to 443"); 58 } 59 60 // Fix to SSL flaw in API < ICS 61 // See https://code.google.com/p/android/issues/detail?id=13117 62 SSLSocketFactory sslSocketFactory; 63 if (fixNoHttpResponseException) { // 感兴趣的同学可自行看看MySSLSocketFactory的实现,基本上是省略了SSL验证环节 64 sslSocketFactory = MySSLSocketFactory.getFixedSocketFactory(); 65 } else { 66 sslSocketFactory = SSLSocketFactory.getSocketFactory(); 67 } 68 69 SchemeRegistry schemeRegistry = new SchemeRegistry(); 70 schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), httpPort)); 71 schemeRegistry.register(new Scheme("https", sslSocketFactory, httpsPort)); 72 73 return schemeRegistry; 74 } 75 76 /** 77 * Creates a new AsyncHttpClient. 78 * 79 * @param schemeRegistry SchemeRegistry to be used 80 */ 81 public AsyncHttpClient(SchemeRegistry schemeRegistry) { // 最终调到的是这个版本。。。 82 83 BasicHttpParams httpParams = new BasicHttpParams(); 84 // 接下来是设置各种参数。。。 85 ConnManagerParams.setTimeout(httpParams, connectTimeout); 86 ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(maxConnections)); 87 ConnManagerParams.setMaxTotalConnections(httpParams, DEFAULT_MAX_CONNECTIONS); 88 89 HttpConnectionParams.setSoTimeout(httpParams, responseTimeout); 90 HttpConnectionParams.setConnectionTimeout(httpParams, connectTimeout); 91 HttpConnectionParams.setTcpNoDelay(httpParams, true); 92 HttpConnectionParams.setSocketBufferSize(httpParams, DEFAULT_SOCKET_BUFFER_SIZE); 93 94 HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1); 95 96 ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(httpParams, schemeRegistry); 97 // 初始化关键字段 98 threadPool = getDefaultThreadPool(); 99 requestMap = Collections.synchronizedMap(new WeakHashMap<Context, List<RequestHandle>>()); 100 clientHeaderMap = new HashMap<String, String>(); 101 102 httpContext = new SyncBasicHttpContext(new BasicHttpContext()); 103 httpClient = new DefaultHttpClient(cm, httpParams); 104 httpClient.addRequestInterceptor(new HttpRequestInterceptor() { 105 @Override 106 public void process(HttpRequest request, HttpContext context) { 107 if (!request.containsHeader(HEADER_ACCEPT_ENCODING)) { 108 request.addHeader(HEADER_ACCEPT_ENCODING, ENCODING_GZIP); 109 } 110 for (String header : clientHeaderMap.keySet()) { 111 if (request.containsHeader(header)) { 112 Header overwritten = request.getFirstHeader(header); 113 Log.d(LOG_TAG, 114 String.format("Headers were overwritten! (%s | %s) overwrites (%s | %s)", 115 header, clientHeaderMap.get(header), 116 overwritten.getName(), overwritten.getValue()) 117 ); 118 119 //remove the overwritten header 120 request.removeHeader(overwritten); 121 } 122 request.addHeader(header, clientHeaderMap.get(header)); 123 } 124 } 125 }); 126 127 httpClient.addResponseInterceptor(new HttpResponseInterceptor() { 128 @Override 129 public void process(HttpResponse response, HttpContext context) { 130 final HttpEntity entity = response.getEntity(); 131 if (entity == null) { 132 return; 133 } 134 final Header encoding = entity.getContentEncoding(); 135 if (encoding != null) { 136 for (HeaderElement element : encoding.getElements()) { 137 if (element.getName().equalsIgnoreCase(ENCODING_GZIP)) { 138 response.setEntity(new InflatingEntity(entity)); 139 break; 140 } 141 } 142 } 143 } 144 }); 145 146 httpClient.addRequestInterceptor(new HttpRequestInterceptor() { 147 @Override 148 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { 149 AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE); 150 CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute( 151 ClientContext.CREDS_PROVIDER); 152 HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST); 153 154 if (authState.getAuthScheme() == null) { 155 AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort()); 156 Credentials creds = credsProvider.getCredentials(authScope); 157 if (creds != null) { 158 authState.setAuthScheme(new BasicScheme()); 159 authState.setCredentials(creds); 160 } 161 } 162 } 163 }, 0); 164 // 设置重试Handler,会在合适的情况下自动重试 165 httpClient.setHttpRequestRetryHandler(new RetryHandler(DEFAULT_MAX_RETRIES, DEFAULT_RETRY_SLEEP_TIME_MILLIS)); 166 }
接下来重要的就是各种HTTP head、get、post、delete方法,它们最终调用的都是sendRequest方法,如下:
1 /** 2 * Puts a new request in queue as a new thread in pool to be executed 3 * 4 * @param client HttpClient to be used for request, can differ in single requests 5 * @param contentType MIME body type, for POST and PUT requests, may be null 6 * @param context Context of Android application, to hold the reference of request 7 * @param httpContext HttpContext in which the request will be executed 8 * @param responseHandler ResponseHandler or its subclass to put the response into 9 * @param uriRequest instance of HttpUriRequest, which means it must be of HttpDelete, 10 * HttpPost, HttpGet, HttpPut, etc. 11 * @return RequestHandle of future request process 12 */ 13 protected RequestHandle sendRequest(DefaultHttpClient client, HttpContext httpContext, HttpUriRequest uriRequest, 14 String contentType, ResponseHandlerInterface responseHandler, Context context) { 15 if (uriRequest == null) { 16 throw new IllegalArgumentException("HttpUriRequest must not be null"); 17 } 18 19 if (responseHandler == null) { 20 throw new IllegalArgumentException("ResponseHandler must not be null"); 21 } 22 23 if (responseHandler.getUseSynchronousMode()) { 24 throw new IllegalArgumentException("Synchronous ResponseHandler used in AsyncHttpClient. You should create your response handler in a looper thread or use SyncHttpClient instead."); 25 } 26 27 if (contentType != null) { 28 uriRequest.setHeader(HEADER_CONTENT_TYPE, contentType); 29 } 30 31 responseHandler.setRequestHeaders(uriRequest.getAllHeaders()); 32 responseHandler.setRequestURI(uriRequest.getURI()); 33 // 下面的这3行是重点,创建请求,提交请求到线程池,将请求包装到RequestHandle用于之后的取消、管理 34 AsyncHttpRequest request = newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context); 35 threadPool.submit(request); // 能submit说明request至少是个Runnable 36 RequestHandle requestHandle = new RequestHandle(request); 37 38 if (context != null) { // 如果Android context非空的话,做一些关联操作,后面可以通过context来取消request的执行 39 // Add request to request map 40 List<RequestHandle> requestList = requestMap.get(context); 41 synchronized (requestMap) { 42 if (requestList == null) { 43 requestList = Collections.synchronizedList(new LinkedList<RequestHandle>()); 44 requestMap.put(context, requestList); 45 } 46 } 47 48 if (responseHandler instanceof RangeFileAsyncHttpResponseHandler) 49 ((RangeFileAsyncHttpResponseHandler) responseHandler).updateRequestHeaders(uriRequest); 50 51 requestList.add(requestHandle); 52 53 Iterator<RequestHandle> iterator = requestList.iterator(); 54 while (iterator.hasNext()) { 55 if (iterator.next().shouldBeGarbageCollected()) { 56 iterator.remove(); // 清理已经完成/取消了的请求 57 } 58 } 59 } 60 61 return requestHandle; 62 }
看到了吧,发送请求的过程其实重点是创建请求,然后submit到线程池,剩下的事情就交给线程池自己处理了,我们只需要坐等被调用。
来看看创建请求的方法,代码如下:
1 /** 2 * Instantiate a new asynchronous HTTP request for the passed parameters. 3 * 4 * @param client HttpClient to be used for request, can differ in single requests 5 * @param contentType MIME body type, for POST and PUT requests, may be null 6 * @param context Context of Android application, to hold the reference of request 7 * @param httpContext HttpContext in which the request will be executed 8 * @param responseHandler ResponseHandler or its subclass to put the response into 9 * @param uriRequest instance of HttpUriRequest, which means it must be of HttpDelete, 10 * HttpPost, HttpGet, HttpPut, etc. 11 * @return AsyncHttpRequest ready to be dispatched 12 */ 13 protected AsyncHttpRequest newAsyncHttpRequest(DefaultHttpClient client, HttpContext httpContext, HttpUriRequest uriRequest, String contentType, ResponseHandlerInterface responseHandler, Context context) { 14 return new AsyncHttpRequest(client, httpContext, uriRequest, responseHandler); 15 }
紧接着我们看看AsyncHttpRequest的实现:
/** * Internal class, representing the HttpRequest, done in asynchronous manner */ public class AsyncHttpRequest implements Runnable { // 这就是submit到线程池的Runnable private final AbstractHttpClient client; private final HttpContext context; private final HttpUriRequest request; private final ResponseHandlerInterface responseHandler; private int executionCount; private boolean isCancelled; private boolean cancelIsNotified; private boolean isFinished; private boolean isRequestPreProcessed; public AsyncHttpRequest(AbstractHttpClient client, HttpContext context, HttpUriRequest request, ResponseHandlerInterface responseHandler) { this.client = client; this.context = context; this.request = request; this.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... } @Override public void run() { // 这是在线程池中执行的方法,我们重点看看 if (isCancelled()) { // 检测,如果已经取消了则直接返回,下面的代码有好多次做这个检测,因为你永远不知道什么时候会被取消 return; // 同时也说明了我们的Request是支持取消的 } // Carry out pre-processing for this request only once. if (!isRequestPreProcessed) { isRequestPreProcessed = true; onPreProcessRequest(this); // callback接口,在一次请求中只调用一次 } if (isCancelled()) { // 再次检查 return; } if (responseHandler != null) { responseHandler.sendStartMessage(); // 发送开始请求消息 } if (isCancelled()) { // 检查 return; } try { makeRequestWithRetries(); // 带自动retry机制的请求 } catch (IOException e) { if (!isCancelled() && responseHandler != null) { responseHandler.sendFailureMessage(0, null, null, e); // 在没取消的情况下,发送失败消息 } else { Log.e("AsyncHttpRequest", "makeRequestWithRetries returned error, but handler is null", e); } } if (isCancelled()) { // 检查again return; } if (responseHandler != null) { // 没取消的情况下,发送完成消息 responseHandler.sendFinishMessage(); } if (isCancelled()) { return; } // Carry out post-processing for this request. onPostProcessRequest(this); // 处理了请求之后的callback isFinished = true; // 设置为true表示这个请求执行完毕了 } 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"); } // 执行请求获得response HttpResponse response = client.execute(request, context); if (isCancelled() || responseHandler == null) { return; } // Carry out pre-processing for this response. responseHandler.onPreProcessResponse(responseHandler, response); // 处理response前 if (isCancelled()) { return; } // The response is ready, handle it. responseHandler.sendResponseMessage(response); // 发送获得的response if (isCancelled()) { return; } // Carry out post-processing for this response. responseHandler.onPostProcessResponse(responseHandler, response); // 处理response后 } private void makeRequestWithRetries() throws IOException { boolean retry = true; IOException cause = null; HttpRequestRetryHandler retryHandler = client.getHttpRequestRetryHandler(); try { while (retry) { // 注意这个循环,当retry为false的时候退出 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) { if (isCancelled()) { // Eating exception, as the request was cancelled return; } cause = e; retry = retryHandler.retryRequest(cause, ++executionCount, context); } // 各种异常的情况下,计算retry,看还是否需要retry if (retry && (responseHandler != null)) { // 需要retry的时候,发送retry消息并附带第几次retry了 responseHandler.sendRetryMessage(executionCount); } } } catch (Exception e) { // catch anything else to ensure failure message is propagated Log.e("AsyncHttpRequest", "Unhandled exception origin cause", e); // 其他的所有不在上述catch里的异常都在这里统一包装成IOException,在最后抛出 cause = new IOException("Unhandled exception: " + e.getMessage()); } // cleaned up to throw IOException throw (cause); // 抛出,以便上层代码知道发生了什么 } public boolean isCancelled() { if (isCancelled) { sendCancelNotification(); } return isCancelled; } private synchronized void sendCancelNotification() { if (!isFinished && isCancelled && !cancelIsNotified) { cancelIsNotified = true; if (responseHandler != null) responseHandler.sendCancelMessage(); } } public boolean isDone() { return isCancelled() || isFinished; } public boolean cancel(boolean mayInterruptIfRunning) { isCancelled = true; request.abort(); return isCancelled(); } }
紧接着,我们大概提下RequestHandle,它只是一个持有AsyncHttpRequest对象的弱引用,其方法内部都delegate给了AsyncHttpRequest
看完了Request,接下来该看看各种Response了,他们都实现了ResponseHandlerInterface接口,这里我们重点看下AsyncHttpResponseHandler,
因为它是后面所有更具体的子类的基础,其ctor代码如下:
1 /** 2 * Creates a new AsyncHttpResponseHandler 3 */ 4 public AsyncHttpResponseHandler() { // 不指定looper 5 this(null); 6 } 7 8 /** 9 * Creates a new AsyncHttpResponseHandler with a user-supplied looper. If 10 * the passed looper is null, the looper attached to the current thread will 11 * be used. 12 * 13 * @param looper The looper to work with 14 */ 15 public AsyncHttpResponseHandler(Looper looper) { // 如果没指定looper的话,会用当前线程的looper顶替 16 this.looper = looper == null ? Looper.myLooper() : looper; 17 // Use asynchronous mode by default. 18 setUseSynchronousMode(false); // 默认是异步的方式,这里异步的意思是指对response的处理发生在与looper 19 } // 关联的线程中,而不是请求发生的线程池里的线程中 20 21 @Override 22 public void setUseSynchronousMode(boolean sync) { 23 // A looper must be prepared before setting asynchronous mode. 24 if (!sync && this.looper == null) { 25 sync = true; // 一种错误的情况,强制使用同步mode 26 Log.w(LOG_TAG, "Current thread has not called Looper.prepare(). Forcing synchronous mode."); 27 } 28 29 // If using asynchronous mode. 30 if (!sync && handler == null) { // 初始化handler 31 // Create a handler on current thread to submit tasks 32 handler = new ResponderHandler(this, this.looper); 33 } else if (sync && handler != null) { 34 // TODO: Consider adding a flag to remove all queued messages. 35 handler = null; 36 } 37 38 useSynchronousMode = sync; 39 }
一般来说,我们会直接在UI线程中调用无参版本的ctor,也就是说response是和UI线程关联的,所有对其的处理handleMessage是发生
在UI线程中的。如果你想用response的结果来更新UI则这是正确的方式。
接着我们看看和处理response相关的代码:
1 /** 2 * Avoid leaks by using a non-anonymous handler class. 3 */ 4 private static class ResponderHandler extends Handler { 5 private final AsyncHttpResponseHandler mResponder; 6 7 ResponderHandler(AsyncHttpResponseHandler mResponder, Looper looper) { 8 super(looper); 9 this.mResponder = mResponder; 10 } 11 12 @Override 13 public void handleMessage(Message msg) { // 一个简单的Handler,其handleMessage delegate给了mResponder 14 mResponder.handleMessage(msg); 15 } 16 } 17 18 // Methods which emulate android‘s Handler and Message methods 19 protected void handleMessage(Message message) { // 对各种message的处理,回调各种onXXX方法 20 Object[] response; 21 22 switch (message.what) { 23 case SUCCESS_MESSAGE: 24 response = (Object[]) message.obj; 25 if (response != null && response.length >= 3) { 26 onSuccess((Integer) response[0], (Header[]) response[1], (byte[]) response[2]); 27 } else { 28 Log.e(LOG_TAG, "SUCCESS_MESSAGE didn‘t got enough params"); 29 } 30 break; 31 case FAILURE_MESSAGE: 32 response = (Object[]) message.obj; 33 if (response != null && response.length >= 4) { 34 onFailure((Integer) response[0], (Header[]) response[1], (byte[]) response[2], (Throwable) response[3]); 35 } else { 36 Log.e(LOG_TAG, "FAILURE_MESSAGE didn‘t got enough params"); 37 } 38 break; 39 case START_MESSAGE: 40 onStart(); 41 break; 42 case FINISH_MESSAGE: 43 onFinish(); 44 break; 45 case PROGRESS_MESSAGE: 46 response = (Object[]) message.obj; 47 if (response != null && response.length >= 2) { 48 try { 49 onProgress((Integer) response[0], (Integer) response[1]); 50 } catch (Throwable t) { 51 Log.e(LOG_TAG, "custom onProgress contains an error", t); 52 } 53 } else { 54 Log.e(LOG_TAG, "PROGRESS_MESSAGE didn‘t got enough params"); 55 } 56 break; 57 case RETRY_MESSAGE: 58 response = (Object[]) message.obj; 59 if (response != null && response.length == 1) { 60 onRetry((Integer) response[0]); 61 } else { 62 Log.e(LOG_TAG, "RETRY_MESSAGE didn‘t get enough params"); 63 } 64 break; 65 case CANCEL_MESSAGE: 66 onCancel(); 67 break; 68 } 69 } 70 71 protected void sendMessage(Message msg) { 72 if (getUseSynchronousMode() || handler == null) { 73 handleMessage(msg); // 如果是同步的方式,则handleMessage发生在调用sendMessage的线程中 74 } else if (!Thread.currentThread().isInterrupted()) { // do not send messages if request has been cancelled 75 handler.sendMessage(msg); // 否则发生在与handler关联的线程中,一般多为UI线程 76 } 77 }
代码中各种sendXXXMessage都会调用这里的sendMessage方法,只是构造的msg的what、obj不同而已。而sendXXXMessage方法
会在request的不同阶段自动被调用,详见AsyncHttpRequest中。下一步我们看眼对response的解析过程,代码如下:
1 @Override 2 public void sendResponseMessage(HttpResponse response) throws IOException { 3 // do not process if request has been cancelled 4 if (!Thread.currentThread().isInterrupted()) { 5 StatusLine status = response.getStatusLine(); 6 byte[] responseBody; 7 responseBody = getResponseData(response.getEntity()); // 将response解析成字节数组 8 // additional cancellation check as getResponseData() can take non-zero time to process 9 if (!Thread.currentThread().isInterrupted()) { 10 if (status.getStatusCode() >= 300) { // 标志失败的情况 11 sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), responseBody, new HttpResponseException(status.getStatusCode(), status.getReasonPhrase())); 12 } else { // 成功的情况 13 sendSuccessMessage(status.getStatusCode(), response.getAllHeaders(), responseBody); 14 } 15 } 16 } 17 } 18 19 /** 20 * Returns byte array of response HttpEntity contents 21 * 22 * @param entity can be null 23 * @return response entity body or null 24 * @throws java.io.IOException if reading entity or creating byte array failed 25 */ 26 byte[] getResponseData(HttpEntity entity) throws IOException { 27 byte[] responseBody = null; 28 if (entity != null) { 29 InputStream instream = entity.getContent(); // 从entity中读取字节流 30 if (instream != null) { 31 long contentLength = entity.getContentLength(); 32 if (contentLength > Integer.MAX_VALUE) { 33 throw new IllegalArgumentException("HTTP entity too large to be buffered in memory"); 34 } 35 int buffersize = (contentLength <= 0) ? BUFFER_SIZE : (int) contentLength; 36 try { 37 ByteArrayBuffer buffer = new ByteArrayBuffer(buffersize); 38 try { 39 byte[] tmp = new byte[BUFFER_SIZE]; 40 int l, count = 0; 41 // do not send messages if request has been cancelled 42 while ((l = instream.read(tmp)) != -1 && !Thread.currentThread().isInterrupted()) { 43 count += l; 44 buffer.append(tmp, 0, l); 45 sendProgressMessage(count, (int) (contentLength <= 0 ? 1 : contentLength)); 46 } 47 } finally { 48 AsyncHttpClient.silentCloseInputStream(instream); 49 AsyncHttpClient.endEntityViaReflection(entity); 50 } 51 responseBody = buffer.toByteArray(); 52 } catch (OutOfMemoryError e) { 53 System.gc(); 54 throw new IOException("File too large to fit into available memory"); 55 } 56 } 57 } 58 return responseBody; 59 }
onXXX方法除了onSuccess和onFailure外都做了默认实现即啥也不做,所以继承至它的子类至少要实现这2个方法,其他的方法你可以选择性实现。
接下来我们看看TextHttpResponseHandler子类的实现,关键代码如下:
1 @Override // 对上述2个方法的重载,其中将byte[]通过getResponseString方法转化成了String对象 2 public void onSuccess(int statusCode, Header[] headers, byte[] responseBytes) { 3 onSuccess(statusCode, headers, getResponseString(responseBytes, getCharset())); 4 } 5 6 @Override 7 public void onFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) { 8 onFailure(statusCode, headers, getResponseString(responseBytes, getCharset()), throwable); 9 } 10 11 /** 12 * Attempts to encode response bytes as string of set encoding 13 * 14 * @param charset charset to create string with 15 * @param stringBytes response bytes 16 * @return String of set encoding or null 17 */ 18 public static String getResponseString(byte[] stringBytes, String charset) { 19 try { 20 String toReturn = (stringBytes == null) ? null : new String(stringBytes, charset); 21 if (toReturn != null && toReturn.startsWith(UTF8_BOM)) { 22 return toReturn.substring(1); 23 } 24 return toReturn; 25 } catch (UnsupportedEncodingException e) { 26 Log.e(LOG_TAG, "Encoding response into string failed", e); 27 return null; 28 } 29 }
说白了,也就是在父类基础上多了一层处理,将byte[]根据特定的编码转化成String而已,类似的JsonHttpResponseHandler又在此基础上
将String转化成JSONObject或JSONArray,细节不赘述。
ResponseHandler介绍完了,这里我们提下RetryHandler,这个类也很简单,根据内部的白/黑名单等规则来确定是否要retry。
AsyncHttpClient当然也提供了对Cookie的支持,默认是保存在Android的SharedPreferences中,具体代码见PersistentCookieStore。
还有一个功能丰富的RequestParams类,据此你不仅可以为GET/POST方法提供参数,甚至你可以上传本地文件到server端。