参考文档
Android Volley完全解析(四),带你从源码的角度理解Volley : http://blog.csdn.net/guolin_blog/article/details/17656437
Volley的工作流程图
如果你对Volley并不了解 , 那么此刻的你看到这张图一定是懵逼的 , 但是没关系 , 等你看完下文后 , 再看这张图一定会很亲切.
开启Volley的奇幻之旅
Volley的三部曲 :
1.创建对应的请求对象(StringRequest、JsonObjectRequest、JsonArrayRequest、ImageRequest)
2.创建请求队列:RequestQueue queue = Volley.newRequestQueue(context);
3.发起网络请求:queue.add(request)
Request只需要new一个出来即可 , 里面重写了两个回调的方法
// 解析从网络访问请求的数据
protected abstract Response<T> parseNetworkResponse(NetworkResponse var1);
// 将最终解析的数据回调回来,在主线程
protected abstract void deliverResponse(T var1);
然后 , 我们来看一下Volley的入口点 , RequestQueue queue = Volley.newRequestQueue(context);
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, (HttpStack)null);
}
实质上是调用了newRequestQueue()的方法重载,并给第二个参数传入null , 那我们来看这个重载的方法
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), "volley");
String userAgent = "volley/0";
try {
String network = context.getPackageName();
PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
userAgent = network + "/" + queue.versionCode;
} catch (NameNotFoundException var6) {
;
}
if(stack == null) {
if(VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
BasicNetwork network1 = new BasicNetwork((HttpStack)stack);
RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
queue1.start();
return queue1;
}
我们从源码中可以看到 , 第13行 , 判断如果stack是等于null的,则去创建一个HttpStack对象,这里会判断如果手机系统版本号是大于9的,则创建一个HurlStack的实例,否则就创建一个HttpClientStack的实例。实际上HurlStack的内部就是使用HttpURLConnection进行网络通讯的,而HttpClientStack的内部则是使用HttpClient进行网络通讯的.
HttpStack就是一个网络访问的接口 , 选择用哪种方式进行网络请求 , 里面只有一个方法
public interface HttpStack {
HttpResponse performRequest(Request<?> var1, Map<String, String> var2) throws IOException, AuthFailureError;
}
关于Android访问网络,使用HttpURLConnection还是HttpClient?
我推荐一个文档 : http://blog.csdn.net/guolin_blog/article/details/12452307
那么 , 我们接着看源码 , 创建好了HttpStack之后,接下来又创建了一个Network对象,它是用于根据传入的HttpStack对象来处理网络请求的,紧接着new出一个RequestQueue对象,并调用它的start()方法进行启动,然后将RequestQueue返回,这样newRequestQueue()的方法就执行结束了。
我们看第22行 , new一个RequestQueue对象的同时 , 传入了两个参数 , 第一个参数传入一个DiskBasedCache对象 , 为磁盘缓存对象 , 该方法底层实质上又进行了方法的重载 , 如下 :
public RequestQueue(Cache cache, Network network) {
this(cache, network, 4);
}
可以看到 , 线程池的线程数默认为4条 , 接着重载
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
这时 , 创建了一个ExecutorDelivery对象 , 并传入Handler对象 , 做最后请求结果的处理
public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) {
this.mSequenceGenerator = new AtomicInteger();
this.mWaitingRequests = new HashMap();
this.mCurrentRequests = new HashSet();
this.mCacheQueue = new PriorityBlockingQueue();
this.mNetworkQueue = new PriorityBlockingQueue();
this.mCache = cache;
this.mNetwork = network;
this.mDispatchers = new NetworkDispatcher[threadPoolSize];
this.mDelivery = delivery;
}
最终 , 重载为上面的方法 , 第一个参数为缓存接口Cache , 第二个参数为网络请求接口Network , 第三个参数为线程池数threadPoolSize(4个) , 第四个参数为请求结果和错误的分发器接口ResponseDelivery , 处理请求结果
好 , 我们再返回去 , 接下来看请求队列的queue1.start()方法 :
public void start() {
this.stop();
this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
this.mCacheDispatcher.start();
for(int i = 0; i < this.mDispatchers.length; ++i) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
this.mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
这里先是创建了一个CacheDispatcher的实例,然后调用了它的start()方法,接着在一个for循环里去创建NetworkDispatcher的实例,并分别调用它们的start()方法。这里的CacheDispatcher和NetworkDispatcher都是继承自Thread的,而默认情况下for循环会执行四次,也就是说当调用了Volley.newRequestQueue(context)之后,就会有五个线程一直在后台运行,不断等待网络请求的到来(可以调run方法),其中CacheDispatcher是缓存线程,NetworkDispatcher是网络请求线程。
接下来 , 就是进行网络请求了 , 调用queue.add(request)方法 , 我们来看一下add()方法里的逻辑
public Request add(Request request) {
request.setRequestQueue(this);
Set var2 = this.mCurrentRequests;
synchronized(this.mCurrentRequests) {
this.mCurrentRequests.add(request);
}
request.setSequence(this.getSequenceNumber());
request.addMarker("add-to-queue");
if(!request.shouldCache()) {
this.mNetworkQueue.add(request);
return request;
} else {
Map var7 = this.mWaitingRequests;
synchronized(this.mWaitingRequests) {
String cacheKey = request.getCacheKey();
if(this.mWaitingRequests.containsKey(cacheKey)) {
Object stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);
if(stagedRequests == null) {
stagedRequests = new LinkedList();
}
((Queue)stagedRequests).add(request);
this.mWaitingRequests.put(cacheKey, stagedRequests);
if(VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[]{cacheKey});
}
} else {
this.mWaitingRequests.put(cacheKey, (Object)null);
this.mCacheQueue.add(request);
}
return request;
}
}
}
可以看到,在第10行的时候 , 会判断当前的请求是否可以缓存,如果不能缓存则在第11行直接将这条请求加入网络请求队列,可以缓存的话则在第30行将这条请求加入缓存队列。在默认情况下,每条请求都是可以缓存的,当然我们也可以调用Request的setShouldCache(false)方法来改变这一默认行为。
OK,那么既然默认每条请求都是可以缓存的,自然就被添加到了缓存队列中,于是一直在后台等待的缓存线程就要开始运行起来了,我们看下CacheDispatcher中的run()方法,代码如下所示:
public void run() {
if(DEBUG) {
VolleyLog.v("start new dispatcher", new Object[0]);
}
Process.setThreadPriority(10);
this.mCache.initialize();
while(true) {
while(true) {
while(true) {
while(true) {
try {
while(true) {
final Request e = (Request)this.mCacheQueue.take();
e.addMarker("cache-queue-take");
if(e.isCanceled()) {
e.finish("cache-discard-canceled");
} else {
Entry entry = this.mCache.get(e.getCacheKey());
if(entry == null) {
e.addMarker("cache-miss");
this.mNetworkQueue.put(e);
} else if(entry.isExpired()) {
e.addMarker("cache-hit-expired");
e.setCacheEntry(entry);
this.mNetworkQueue.put(e);
} else {
e.addMarker("cache-hit");
Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
e.addMarker("cache-hit-parsed");
if(entry.refreshNeeded()) {
e.addMarker("cache-hit-refresh-needed");
e.setCacheEntry(entry);
response.intermediate = true;
this.mDelivery.postResponse(e, response, new Runnable() {
public void run() {
try {
CacheDispatcher.this.mNetworkQueue.put(e);
} catch (InterruptedException var2) {
;
}
}
});
} else {
this.mDelivery.postResponse(e, response);
}
}
}
}
} catch (InterruptedException var4) {
if(this.mQuit) {
return;
}
}
}
}
}
}
}
代码有点长,我们只看重点 , 首先在9行可以看到一个while(true)循环,说明缓存线程始终是在运行的,接着在第20行会尝试从缓存当中取出响应结果,如何为空的话则把这条请求加入到网络请求队列中,如果不为空的话 , 第24行再判断该缓存是否已过期,如果已经过期了则同样把这条请求加入到网络请求队列中,否则就认为不需要重发网络请求,直接使用缓存中的数据即可。之后会在第30行调用Request的parseNetworkResponse()方法来对数据进行解析,再往后就是将解析出来的数据进行回调了 , 此处停一下 , mDelivery.postResponse()方法在后面讲解.
因为它的逻辑和NetworkDispatcher后半部分的逻辑是基本相同的,那么我们等下合并在一起看就好了,先来看一下NetworkDispatcher中是怎么处理网络请求队列的,代码如下所示:
public void run() {
Process.setThreadPriority(10);
while(true) {
Request request;
while(true) {
try {
request = (Request)this.mQueue.take();
break;
} catch (InterruptedException var4) {
if(this.mQuit) {
return;
}
}
}
try {
request.addMarker("network-queue-take");
if(request.isCanceled()) {
request.finish("network-discard-cancelled");
} else {
if(VERSION.SDK_INT >= 14) {
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
}
NetworkResponse e = this.mNetwork.performRequest(request);
request.addMarker("network-http-complete");
if(e.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
} else {
Response response = request.parseNetworkResponse(e);
request.addMarker("network-parse-complete");
if(request.shouldCache() && response.cacheEntry != null) {
this.mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
request.markDelivered();
this.mDelivery.postResponse(request, response);
}
}
} catch (VolleyError var5) {
this.parseAndDeliverNetworkError(request, var5);
} catch (Exception var6) {
VolleyLog.e(var6, "Unhandled exception %s", new Object[]{var6.toString()});
this.mDelivery.postError(request, new VolleyError(var6));
}
}
}
同样地,在第6行我们看到了类似的while(true)循环,说明网络请求线程也是在不断运行的。第21行 , 如果该请求没有被取消 , 那么在第26行的时候会调用Network的performRequest()方法来去发送网络请求,而Network是一个接口,这里具体的实现是BasicNetwork,我们来看下它的performRequest()方法 , 进行网络请求,如下所示:
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while(true) {
HttpResponse httpResponse = null;
byte[] responseContents = (byte[])null;
HashMap responseHeaders = new HashMap();
try {
HashMap e = new HashMap();
this.addCacheHeaders(e, request.getCacheEntry());
httpResponse = this.mHttpStack.performRequest(request, e);
StatusLine statusCode1 = httpResponse.getStatusLine();
int networkResponse1 = statusCode1.getStatusCode();
Map responseHeaders1 = convertHeaders(httpResponse.getAllHeaders());
if(networkResponse1 == 304) {
return new NetworkResponse(304, request.getCacheEntry().data, responseHeaders1, true);
}
if(httpResponse.getEntity() != null) {
responseContents = this.entityToBytes(httpResponse.getEntity());
} else {
responseContents = new byte[0];
}
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
this.logSlowRequests(requestLifetime, request, responseContents, statusCode1);
if(networkResponse1 >= 200 && networkResponse1 <= 299) {
return new NetworkResponse(networkResponse1, responseContents, responseHeaders1, false);
}
throw new IOException();
} catch (SocketTimeoutException var12) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException var13) {
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException var14) {
throw new RuntimeException("Bad URL " + request.getUrl(), var14);
} catch (IOException var15) {
boolean statusCode = false;
NetworkResponse networkResponse = null;
if(httpResponse == null) {
throw new NoConnectionError(var15);
}
int statusCode2 = httpResponse.getStatusLine().getStatusCode();
VolleyLog.e("Unexpected response code %d for %s", new Object[]{Integer.valueOf(statusCode2), request.getUrl()});
if(responseContents == null) {
throw new NetworkError(networkResponse);
}
networkResponse = new NetworkResponse(statusCode2, responseContents, responseHeaders, false);
if(statusCode2 != 401 && statusCode2 != 403) {
throw new ServerError(networkResponse);
}
attemptRetryOnException("auth", request, new AuthFailureError(networkResponse));
}
}
}
第12行 , 可以看到调用了HttpStack的performRequest()方法 , 这里的HttpStack就是在一开始调用newRequestQueue()方法是创建的实例 , 默认情况下如果系统版本号大于9就创建的HurlStack对象,否则创建HttpClientStack对象。前面已经说过,这两个对象的内部实际就是分别使用HttpURLConnection和HttpClient来发送网络请求的,我们就不再跟进去阅读了,之后会将服务器返回的数据组装成一个NetworkResponse对象进行返回。
走到这里也许头有点晕了吧 , 那么接下来 , 我们要回到NetworkDispatcher的run方法中 , 继续分析 , 拿到mNetwork.performRequest(request)返回的NetworkResponse对象后 , 在31行 , 又会调用request.parseNetworkResponse(e)方法来解析NetworkResponse中的数据,以及将数据写入到缓存,这个方法的实现是交给Request的子类来完成的,因为不同种类的Request解析的方式也肯定不同。
Response response = request.parseNetworkResponse(e);
在解析完了NetworkResponse中的数据之后,在39行 , 又会调用ExecutorDelivery的postResponse()方法来回调解析出的数据,代码如下所示:
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
this.mResponsePoster.execute(new ExecutorDelivery.ResponseDeliveryRunnable(request, response, runnable));
}
其中,在mResponsePoster的execute()方法中传入了一个ResponseDeliveryRunnable对象,我们看下run()方法中的代码是什么样的:
private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable;
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
this.mRequest = request;
this.mResponse = response;
this.mRunnable = runnable;
}
public void run() {
if(this.mRequest.isCanceled()) {
this.mRequest.finish("canceled-at-delivery");
} else {
if(this.mResponse.isSuccess()) {
this.mRequest.deliverResponse(this.mResponse.result);
} else {
this.mRequest.deliverError(this.mResponse.error);
}
if(this.mResponse.intermediate) {
this.mRequest.addMarker("intermediate-response");
} else {
this.mRequest.finish("done");
}
if(this.mRunnable != null) {
this.mRunnable.run();
}
}
}
}
其中在第17行调用了Request的deliverResponse()方法,有没有感觉很熟悉?没错,这个就是我们在自定义Request时需要重写的另外一个方法,每一条网络请求的响应都是回调到这个方法中,最后我们再在这个方法中将响应的数据回调到Response.Listener的onResponse()方法中就可以了。
那么 , 问题就来了 , deliverResponse()怎么就是在主线程中执行的呢?我们来看ExecutorDelivery对象是怎么实例化的 :
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
看见Handler对象了吧 , 而且最关键的就是Looper.getMainLooper() , 这是获取主线程的Looper对象 , 它意味着什么我就不在这里讲解了 , 大家不懂的可以看我的对Handler的源码解析 , 这对代码理解影响不大 , 接下来 , 继续看代码
private final Executor mResponsePoster;
public ExecutorDelivery(final Handler handler) {
this.mResponsePoster = new Executor() {
public void execute(Runnable command) {
handler.post(command);
}
};
}
这里 , 我们发现原来最终是调用了handler.post()方法 , 回到了主线程.