网络通信系列文章序
彻底掌握网络通信(一)Http协议基础知识
彻底掌握网络通信(二)Apache的HttpClient基础知识
彻底掌握网络通信(三)Android源码中HttpClient的在不同版本的使用
彻底掌握网络通信(四)Android源码中HttpClient的发送框架解析
彻底掌握网络通信(五)DefaultRequestDirector解析
彻底掌握网络通信(六)HttpRequestRetryHandler解析
彻底掌握网络通信(七)ConnectionReuseStrategy,ConnectionKeepAliveStrategy解析
彻底掌握网络通信(八)AsyncHttpClient源码解读
彻底掌握网络通信(九)AsyncHttpClient为什么无法用Fiddler来抓包
彻底掌握网络通信(十)AsyncHttpClient如何发送JSON解析JSON,以及一些其他用法
在之前的文章中,我们系统的分析了httpclient的内部发送过程,以及httpclient是如何重连,保活等机制;这章我们就来看下以httpclient为原型而封装的一个异步请求发送库AsyncHttpClient,下载地址
1:一次完整发送过程
可见AsyncHttpClient的发送也是依赖DefaultHttpClient类的execute方法,总结一下具体的步骤
1.1)创建AsyncHttpClient实例,在这个过程中主要完成一些httpparams参数的设置,线程池的创建,重发机制的创建等等
1.2)调用post或者get等方法,传入实现了ResponseHandlerInterface接口的类,该接口主要用于回调请求成功和失败
1.3)在调用post和get之后,会构建AsyncHttpRequest实例,该实例实现了runnable接口,并将该实例提交到线程池
1.4)提交到线程池之后,AsyncHttpRequest的makeRequest方法会调用DefaultHttpClient的excute方法完成http请求,之后调用ResponseHandlerInterface的sendResponseMessage方法
1.5)在ResponseHandlerInterface具体实现类的sendResponseMessage方法中会完成资源的释放,并通过handler将处理结果(如成功,失败的)消息发送出去
2:我们看下AsyncHttpClient中的DefaultHttpClient和Apach中的DefaultHttpClient在实现上有什么区别
2.1)AsyncHttpClient中的DefaultHttpClient的类图
2.2)Apach中的DefaultHttpClient的类图
从上面的两个类图可见,他们两者之间的区别就在于AbstractHttpClient的实现上,AsyncHttpClient继承了CloseableHttpClient类,同时CloseableHttpClient实现了Closeable接口和HttpClient接口,这样做的好处就是AsyncHttpClient更容易对资源进行释放和处理
3:AsyncHttpClient的常用发起请求的代码
AsyncHttpClient client = new AsyncHttpClient();
client.get("http://www.baidu.com", new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int i, cz.msebera.android.httpclient.Header[] headers, byte[] bytes) {
Log.d("hwj", "**AsyncHttpClientActivity onSuccess**");
}
@Override
public void onFailure(int i, cz.msebera.android.httpclient.Header[] headers, byte[] bytes, Throwable throwable) {
Log.d("hwj", "**AsyncHttpClientActivity onFailure**");
}
});
很简答的代码,下面我们就这个post请求做下源码分析
4:源码分析
4.1)实例化AsyncHttpClient
public AsyncHttpClient() {
this(false, 80, 443);
}
调用
public AsyncHttpClient(boolean fixNoHttpResponseException, int httpPort, int httpsPort) {
this(getDefaultSchemeRegistry(fixNoHttpResponseException, httpPort, httpsPort));
}
最终调用如下方法完成实例的创建
public AsyncHttpClient(SchemeRegistry schemeRegistry) {
this.maxConnections = 10;
this.connectTimeout = 10000;
this.responseTimeout = 10000;
this.isUrlEncodingEnabled = true;
BasicHttpParams httpParams = new BasicHttpParams();
ConnManagerParams.setTimeout(httpParams, (long)this.connectTimeout);
ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(this.maxConnections));
ConnManagerParams.setMaxTotalConnections(httpParams, 10);
HttpConnectionParams.setSoTimeout(httpParams, this.responseTimeout);
HttpConnectionParams.setConnectionTimeout(httpParams, this.connectTimeout);
HttpConnectionParams.setTcpNoDelay(httpParams, true);
HttpConnectionParams.setSocketBufferSize(httpParams, 8192);
HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
ClientConnectionManager cm = this.createConnectionManager(schemeRegistry, httpParams);
Utils.asserts(cm != null, "Custom implementation of #createConnectionManager(SchemeRegistry, BasicHttpParams) returned null");
this.threadPool = this.getDefaultThreadPool();
this.requestMap = Collections.synchronizedMap(new WeakHashMap());
this.clientHeaderMap = new HashMap();
this.httpContext = new SyncBasicHttpContext(new BasicHttpContext());
this.httpClient = new DefaultHttpClient(cm, httpParams);
this.httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
public void process(HttpRequest request, HttpContext context) {
if (!request.containsHeader("Accept-Encoding")) {
request.addHeader("Accept-Encoding", "gzip");
}
String header;
for(Iterator var3 = AsyncHttpClient.this.clientHeaderMap.keySet().iterator(); var3.hasNext(); request.addHeader(header, (String)AsyncHttpClient.this.clientHeaderMap.get(header))) {
header = (String)var3.next();
if (request.containsHeader(header)) {
Header overwritten = request.getFirstHeader(header);
AsyncHttpClient.log.d("AsyncHttpClient", String.format("Headers were overwritten! (%s | %s) overwrites (%s | %s)", header, AsyncHttpClient.this.clientHeaderMap.get(header), overwritten.getName(), overwritten.getValue()));
request.removeHeader(overwritten);
}
}
}
});
this.httpClient.addResponseInterceptor(new HttpResponseInterceptor() {
public void process(HttpResponse response, HttpContext context) {
HttpEntity entity = response.getEntity();
if (entity != null) {
Header encoding = entity.getContentEncoding();
if (encoding != null) {
HeaderElement[] var5 = encoding.getElements();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
HeaderElement element = var5[var7];
if (element.getName().equalsIgnoreCase("gzip")) {
response.setEntity(new AsyncHttpClient.InflatingEntity(entity));
break;
}
}
}
}
}
});
this.httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState)context.getAttribute("http.auth.target-scope");
CredentialsProvider credsProvider = (CredentialsProvider)context.getAttribute("http.auth.credentials-provider");
HttpHost targetHost = (HttpHost)context.getAttribute("http.target_host");
if (authState.getAuthScheme() == null) {
AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort());
Credentials creds = credsProvider.getCredentials(authScope);
if (creds != null) {
authState.setAuthScheme(new BasicScheme());
authState.setCredentials(creds);
}
}
}
}, 0);
this.httpClient.setHttpRequestRetryHandler(new RetryHandler(5, 1500));
}
第6~14行,设置httpparams参数
第15行,设置客户端连接管理,在Apache的DefaultHttpClient中,客户端连接管理默认为SingleClientConnManager,管理客户端连接管理者的作用,可参考这里管理客户端连接管理者
第17行,设置线程池,用于将http请求提交到线程池中
第21行,设置DefaultHttpClient的实例
第22行~76行,设置Http请求的拦截器
第77行,设置http请求的重试机制
4.1.1)看下线程池
protected ExecutorService getDefaultThreadPool() {
return Executors.newCachedThreadPool();
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
通过源码我们可以看见创建了一个max_value,队列为SynchronousQueue的线程池
4.2)提交http请求到线程池中
在构建好AsyncHttpClient实例之后,就可以调用post方法完成请求的发送
public RequestHandle post(String url, ResponseHandlerInterface responseHandler) {
return this.post((Context)null, url, (RequestParams)null, responseHandler);
}
其最终调用
public RequestHandle post(Context context, String url, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) {
return this.sendRequest(this.httpClient, this.httpContext, this.addEntityToRequestBase(new HttpPost(this.getURI(url)), entity), contentType, responseHandler, context);
}
这个方法里面,有一个HttpEntity entity的参数,当你的http请求中,如果有需要添加到消息体里的内容,就可以通过这个参数进行添加
我们看下sendRequest方法
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");
} else if (responseHandler == null) {
throw new IllegalArgumentException("ResponseHandler must not be null");
} else 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.");
} else {
if (contentType != null) {
if (uriRequest instanceof HttpEntityEnclosingRequestBase && ((HttpEntityEnclosingRequestBase)uriRequest).getEntity() != null && uriRequest.containsHeader("Content-Type")) {
log.w("AsyncHttpClient", "Passed contentType will be ignored because HttpEntity sets content type");
} else {
uriRequest.setHeader("Content-Type", contentType);
}
}
responseHandler.setRequestHeaders(uriRequest.getAllHeaders());
responseHandler.setRequestURI(uriRequest.getURI());
AsyncHttpRequest request = this.newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context);
this.threadPool.submit(request);
RequestHandle requestHandle = new RequestHandle(request);
if (context != null) {
Map var10 = this.requestMap;
List requestList;
synchronized(this.requestMap) {
requestList = (List)this.requestMap.get(context);
if (requestList == null) {
requestList = Collections.synchronizedList(new LinkedList());
this.requestMap.put(context, requestList);
}
}
requestList.add(requestHandle);
Iterator iterator = requestList.iterator();
while(iterator.hasNext()) {
if (((RequestHandle)iterator.next()).shouldBeGarbageCollected()) {
iterator.remove();
}
}
}
return requestHandle;
}
}
第19行,创建AsyncHttpRequest request实例,AsyncHttpRequest 实现了runnable接口
第20行,将上面创建的request提交到线程池中去进行http请求的发送
第21行,创建RequestHandle requestHandle
第22行~33行,将requestHandle添加到requestList 的list中,这样开发者就可以调用cancelRequests来取消一个正在进行http请求了
第34行~41行,如果是已经执行的http请求,则需要在requestList 清空掉
4.3:发送http请求
当我们把AsyncHttpRequest request提交到线程池中,一个http请求就会被发起,看下AsyncHttpRequest 的run方法
public void run() {
if (!this.isCancelled()) {
if (!this.isRequestPreProcessed) {
this.isRequestPreProcessed = true;
this.onPreProcessRequest(this);
}
if (!this.isCancelled()) {
this.responseHandler.sendStartMessage();
if (!this.isCancelled()) {
try {
this.makeRequestWithRetries();
} catch (IOException var2) {
if (!this.isCancelled()) {
this.responseHandler.sendFailureMessage(0, (Header[])null, (byte[])null, var2);
} else {
AsyncHttpClient.log.e("AsyncHttpRequest", "makeRequestWithRetries returned error", var2);
}
}
if (!this.isCancelled()) {
this.responseHandler.sendFinishMessage();
if (!this.isCancelled()) {
this.onPostProcessRequest(this);
this.isFinished = true;
}
}
}
}
}
}
第8行,当这个请求没有被cancle的情况下,即没有调用cancelRequests的情况下,方将请求发出去
第9行,通过handler发送一个what为2的message给AsyncHttpResponseHandler的handleMessage处理,AsyncHttpResponseHandler在接收到what为2的msg回调onStart方法,onStart是空方法体,可自行实现其逻辑
第12行,调用makeRequestWithRetries将请求真正发送出去
第15行,当http请求在经过重试并出现IOException 异常的时候,通过handler发送一个what为1的message给AsyncHttpResponseHandler的handleMessage处理,AsyncHttpResponseHandler在接收到what为1的msg回调onFailure方法
第21行~26行,当一个请求被正常执行,通过handler发送一个what为3的message给AsyncHttpResponseHandler的handleMessage处理,AsyncHttpResponseHandler在接收到what为3的msg回调onFinish方法,onFinish是空方法体,可自行实现其逻辑
我们重点看下makeRequestWithRetries是如何发送http请求的
private void makeRequestWithRetries() throws IOException {
boolean retry = true;
IOException cause = null;
HttpRequestRetryHandler retryHandler = this.client.getHttpRequestRetryHandler();
while(true) {
try {
if (retry) {
try {
this.makeRequest();
return;
} catch (UnknownHostException var5) {
cause = new IOException("UnknownHostException exception: " + var5.getMessage());
retry = this.executionCount > 0 && retryHandler.retryRequest(var5, ++this.executionCount, this.context);
} catch (NullPointerException var6) {
cause = new IOException("NPE in HttpClient: " + var6.getMessage());
retry = retryHandler.retryRequest(cause, ++this.executionCount, this.context);
} catch (IOException var7) {
if (this.isCancelled()) {
return;
}
cause = var7;
retry = retryHandler.retryRequest(var7, ++this.executionCount, this.context);
}
if (retry) {
this.responseHandler.sendRetryMessage(this.executionCount);
}
continue;
}
} catch (Exception var8) {
AsyncHttpClient.log.e("AsyncHttpRequest", "Unhandled exception origin cause", var8);
cause = new IOException("Unhandled exception: " + var8.getMessage());
}
throw cause;
}
}
第4行,得到我们在创建AsyncHttpClient实例的时候设置的HttpRequestRetryHandler
第6行,开启while循环,当出现异常的情况下,我们可以通过HttpRequestRetryHandler来尝试重新发送http请求
第10行,调用 makeRequest完成http消息发送
private void makeRequest() throws IOException {
if (!this.isCancelled()) {
if (this.request.getURI().getScheme() == null) {
throw new MalformedURLException("No valid URI scheme was provided");
} else {
if (this.responseHandler instanceof RangeFileAsyncHttpResponseHandler) {
((RangeFileAsyncHttpResponseHandler)this.responseHandler).updateRequestHeaders(this.request);
}
HttpResponse response = this.client.execute(this.request, this.context);
if (!this.isCancelled()) {
this.responseHandler.onPreProcessResponse(this.responseHandler, response);
if (!this.isCancelled()) {
this.responseHandler.sendResponseMessage(response);
if (!this.isCancelled()) {
this.responseHandler.onPostProcessResponse(this.responseHandler, response);
}
}
}
}
}
}
第10行,通过调用DefaultHttpClient的execute方法完成http请求的发送和接收,这个过程和Apache的DefaultHttpClient的执行过程是一致的,有不明白的地方可以参考
彻底掌握网络通信(四)Android源码中HttpClient的发送框架解析
彻底掌握网络通信(五)DefaultRequestDirector解析
第14行,调用AsyncHttpResponseHandler的sendResponseMessage,将response传递给AsyncHttpResponseHandler
我们在看下AsyncHttpResponseHandler的sendResponseMessage方法
public void sendResponseMessage(HttpResponse response) throws IOException {
if (!Thread.currentThread().isInterrupted()) {
StatusLine status = response.getStatusLine();
byte[] responseBody = this.getResponseData(response.getEntity());
if (!Thread.currentThread().isInterrupted()) {
if (status.getStatusCode() >= 300) {
this.sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), responseBody, new HttpResponseException(status.getStatusCode(), status.getReasonPhrase()));
} else {
this.sendSuccessMessage(status.getStatusCode(), response.getAllHeaders(), responseBody);
}
}
}
}
第4行,将response转为byte[]
第9行,通过handler发送一个what为0的message给AsyncHttpResponseHandler的handleMessage处理,AsyncHttpResponseHandler在接收到what为0的msg回调onSuccess方法
我们注意看下第4行的getResponseData的方法,这里有很关键的资源释放动作
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 > 2147483647L) {
throw new IllegalArgumentException("HTTP entity too large to be buffered in memory");
}
int buffersize = contentLength <= 0L ? 4096 : (int)contentLength;
try {
ByteArrayBuffer buffer = new ByteArrayBuffer(buffersize);
try {
byte[] tmp = new byte[4096];
long count = 0L;
int l;
while((l = instream.read(tmp)) != -1 && !Thread.currentThread().isInterrupted()) {
count += (long)l;
buffer.append(tmp, 0, l);
this.sendProgressMessage(count, contentLength <= 0L ? 1L : contentLength);
}
} finally {
AsyncHttpClient.silentCloseInputStream(instream);
AsyncHttpClient.endEntityViaReflection(entity);
}
responseBody = buffer.toByteArray();
} catch (OutOfMemoryError var16) {
System.gc();
throw new IOException("File too large to fit into available memory");
}
}
}
return responseBody;
}
从第26行开始,就是默认处理一些资源的回收动作
AsyncHttpClient的分析到此结束 - _ -