1. 介绍
使用Android自带httpclient编写网络请求是一件非常痛苦的事情,你需要熟知http请求首部字段、实体首部等的含义,还需要开启子线程执行相关操作然后将执行结果返回。首部字段玲琅满目和请求结果格式的不统一(json,string,字节流)是编写网络请求代码的痛点,市面上有很多的开源框架帮助我们完成了这些繁杂和重复的操作,比较常用的有:xUtil,aFinal,okHttp,volley,retrofit,android-async-http等。
各个框架原理大致相似,此篇分析android-async-http源码。个人对开源框架的态度是没有必要深抠实现细节,但是大致流程要知道,以免遇到问题不知所措。还有开源框架的模式也是值得我们去学习和模仿的。
android-async-http的项目地址在: http://loopj.com/android-async-http/
2. 源码分析
这是官方提供的使用实例
AsyncHttpClient client = new AsyncHttpClient();
client.get("https://www.google.com", new AsyncHttpResponseHandler() {
@Override
public void onStart() {
// called before request is started
}
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] response) {
// called when response HTTP status is "200 OK"
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
// called when response HTTP status is "4XX" (eg. 401, 403, 404)
}
@Override
public void onRetry(int retryNo) {
// called when request is retried
}
});
- 请求预处理
这里的url为baseUrl,内部会把通过getUrlWithQueryString方法将url和RequestParams拼接为全连接形式的字符串,然后将该字符串作为参数创建为内部的一个HttpGet对象,并调用sendReques方法将请求组织成内部使用的一个AsyncHttpRequest对象
AsyncHttpRequest request = newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context);
threadPool.submit(request);
RequestHandle requestHandle = new RequestHandle(request);
if (context != null) {
List<RequestHandle> requestList;
// Add request to request map
synchronized (requestMap) {
requestList = requestMap.get(context);
if (requestList == null) {
requestList = Collections.synchronizedList(new LinkedList<RequestHandle>());
requestMap.put(context, requestList);
}
}
requestList.add(requestHandle);
Iterator<RequestHandle> iterator = requestList.iterator();
while (iterator.hasNext()) {
if (iterator.next().shouldBeGarbageCollected()) {
iterator.remove();
}
}
}
第1行:将请求组成为一个AsyncHttpRequest对象,该对象为一个Runnable对象,负责网络请求的连接,请求结果的分发、逻辑取消、重试等逻辑
第2行:将AsyncHttpRequest投递到内部的一个线程池中,自然AsyncHttpRequest内部逻辑在该线程获得cpu资源的时候执行
第3行:将AsyncHttpRequest作为参数创建为一个RequestHandle实例,该类负责请求的取消功能
第5-23行:AsyncHttpClient 内部维持了一个Map,以context为key,RequestHandle集合为值,这样就可以通过context取消该context绑定的请求和处理线程关系了。
- 实际网络请求、重试逻辑
AsyncHttpRequest内部有很多的内部属性,前几个是网络请求相关api,isCancelled标示该请求是否被取消,executionCount用来记录请求执行次数从而处理重试逻辑,cancelIsNotified用来标示是否已经处理过cancel消息,isFinished标示请求是否完成(这里的完成跟请求结果成功、失败、被取消无关),isRequestPreProcessed标示在请求前的一个回调方法(该方法在子线程中执行)。
@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.sendStartMessage();
if (isCancelled()) {
return;
}
try {
makeRequestWithRetries();
} catch (IOException e) {
if (!isCancelled()) {
responseHandler.sendFailureMessage(0, null, null, e);
} else {
AsyncHttpClient.log.e("AsyncHttpRequest", "makeRequestWithRetries returned error", e);
}
}
if (isCancelled()) {
return;
}
responseHandler.sendFinishMessage();
if (isCancelled()) {
return;
}
// Carry out post-processing for this request.
onPostProcessRequest(this);
isFinished = true;
}
第7-10行:处理onPreProcessRequest函数,该函数在实际网络请求前执行且只会被执行一次,目前该方法默认空置,可由子类扩展
第17行:发出开始消息,代表开始网络请求
第23-30行:发出网络请求,请求失败会发出失败消息
第37行:发出网络请求完成消息,结果可能成功也可能失败
第44行:处理onPostProcessRequest函数,与onPreProcessRequest对应,默认空置,可由子类拓展
makeRequestWithRetries函数主要是配合HttpRequestRetryHandler接口完成重试操作,实际网络请求在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);
}
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.
responseHandler.sendResponseMessage(response);
if (isCancelled()) {
return;
}
// Carry out post-processing for this response.
responseHandler.onPostProcessResponse(responseHandler, response);
}
这样就把执行结果以及各种状态消息统统传递给了ResponseHandlerInterface,下面再来看消息的分发
- 执行结果以及状态消息的分发
执行结果和消息的分发操作在AsyncHttpResponseHandler类中实现的
是根据useSynchronousMode这个属性来判断分发操作在那个线程执行,分发操作比较简单,比较重要的是线程的选择,是在setUseSynchronousMode这一个方法中完成的。
首先当我们创建一个AsyncHttpResponseHandler子类对象的时候,默认会通过this(null)执行该构造方法
public AsyncHttpResponseHandler(Looper looper) {
this.looper = looper == null ? Looper.myLooper() : looper;
// Use asynchronous mode by default.
setUseSynchronousMode(false);
// Do not use the pool's thread to fire callbacks by default.
setUsePoolThread(false);
}
这里参数默认为空,所以会通过Looper.myLooper获取当前线程ThreadLocal存储的那个Looper然后赋值给this.looper属性,然后调用setUseSynchronousMode方法操作Handler
public void setUseSynchronousMode(boolean sync) {
// A looper must be prepared before setting asynchronous mode.
if (!sync && looper == null) {
sync = true;
AsyncHttpClient.log.w(LOG_TAG, "Current thread has not called Looper.prepare(). Forcing synchronous mode.");
}
// If using asynchronous mode.
if (!sync && handler == null) {
// Create a handler on current thread to submit tasks
handler = new ResponderHandler(this, looper);
} else if (sync && handler != null) {
// TODO: Consider adding a flag to remove all queued messages.
handler = null;
}
useSynchronousMode = sync;
}
这里会根据是否同步和Looper的有无,来创建Handler或者置handler为null,后边sendMessage方法根据handler是否为null来执行同步回调还是异步回调,然后定义了好多种不同类型的回调类供我们选择是将结果处理为json、string或者是字节流了。