(4.2.10.3)【android开源组件】Android Volley完全解析(四),带你从源码的角度理解Volley
来源:互联网 发布:鹰眼行车记录仪软件 编辑:程序博客网 时间:2024/06/01 09:14
- 一概述
- 二解析
- 1 newRequestQueue
- 11 RequestQueuestart
- 2 RequestQueue的add
- 3 缓存调度线程CacheDispatcherrun
- 3 网络调度线程NetworkDispatcherrun
- 31 请求网络任务BasicNetworkperformRequest
- 32 回调主线程DeliverypostResponse
- 1 newRequestQueue
一、概述
从上图可以看到Volley分为三个线程,分别是主线程、缓存调度线程、和网络调度线程。
- 在主线程中调用RequestQueue的add()方法来添加一条网络请求
- 首先请求会加入缓存队列,如果发现可以找到相应的缓存结果就直接读取缓存并解析,然后回调给主线程;
- 如果在缓存中没有找到结果,则将这条请求加入到网络队列中,然后发送HTTP请求,解析响应并写入缓存,并回调给主线程
二、解析
2.1 newRequestQueue
说起分析源码,那么应该从哪儿开始看起呢?这就要回顾一下Volley的用法了,还记得吗,使用Volley的第一步,首先要调用Volley.newRequestQueue(context)方法来获取一个RequestQueue对象,那么我们自然要从这个方法开始看起了,代码如下所示:
public static RequestQueue newRequestQueue(Context context) { return newRequestQueue(context, null); }
这个方法仅仅只有一行代码,只是调用了newRequestQueue()的方法重载,并给第二个参数传入null。那我们看下带有两个参数的newRequestQueue()方法中的代码,如下所示:
public static RequestQueue newRequestQueue(Context context, HttpStack stack) { File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR); String userAgent = "volley/0"; try { String packageName = context.getPackageName(); PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); userAgent = packageName + "/" + info.versionCode; } catch (NameNotFoundException e) { } if (stack == null) { if (Build.VERSION.SDK_INT >= 9) { stack = new HurlStack(); } else { stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); } } Network network = new BasicNetwork(stack); RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); queue.start(); return queue; }
在第10行判断如果stack是等于null的,则去创建一个HttpStack对象
这里会判断如果手机系统版本号是大于9的,则创建一个HurlStack的实例,否则就创建一个HttpClientStack的实例。
实际上HurlStack的内部就是使用HttpURLConnection进行网络通讯的,而HttpClientStack的内部则是使用HttpClient进行网络通讯的,这里为什么这样选择呢?可以参考Android访问网络,使用HttpURLConnection还是HttpClient?
创建好了HttpStack之后,接下来又创建了一个Network对象,它是用于根据传入的HttpStack对象来处理网络请求的
紧接着new出一个RequestQueue对象,并调用它的start()方法进行启动,然后将RequestQueue返回,这样newRequestQueue()的方法就执行结束了
那么RequestQueue的start()方法内部到底执行了什么东西呢?我们跟进去瞧一瞧:
2.1.1 RequestQueue.start()
public void start() { stop(); // Make sure any currently running dispatchers are stopped. // Create the cache dispatcher and start it. mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery); mCacheDispatcher.start(); // Create network dispatchers (and corresponding threads) up to the pool size. for (int i = 0; i < mDispatchers.length; i++) { NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery); mDispatchers[i] = networkDispatcher; networkDispatcher.start(); } }
- 先是创建了一个CacheDispatcher的实例,然后调用了它的start()方法
- 在一个for循环里去创建NetworkDispatcher的实例,并分别调用它们的start()方法
这里的CacheDispatcher和NetworkDispatcher都是继承自Thread的,而默认情况下for循环会执行四次,也就是说当调用了Volley.newRequestQueue(context)之后,就会有五个线程一直在后台运行,不断等待网络请求的到来,其中CacheDispatcher是缓存线程,NetworkDispatcher是网络请求线程。
2.2 RequestQueue的add()
得到了RequestQueue之后,我们只需要构建出相应的Request,然后调用RequestQueue的add()方法将Request传入就可以完成网络请求操作了,那么不用说,add()方法的内部肯定有着非常复杂的逻辑,我们来一起看一下:
public <T> Request<T> add(Request<T> request) { // Tag the request as belonging to this queue and add it to the set of current requests. request.setRequestQueue(this); synchronized (mCurrentRequests) { mCurrentRequests.add(request); } // Process requests in the order they are added. request.setSequence(getSequenceNumber()); request.addMarker("add-to-queue"); // If the request is uncacheable, skip the cache queue and go straight to the network. if (!request.shouldCache()) {//如果不能缓存,则将请求添加到网络请求队列中 mNetworkQueue.add(request); return request; } // Insert request into stage if there's already a request with the same cache key in flight. synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); //之前是否有执行相同的请求且还没有返回结果的,如果有的话将此请求加入mWaitingRequests队列,不再重复请求 if (mWaitingRequests.containsKey(cacheKey)) { // There is already a request in flight. Queue up. Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey); if (stagedRequests == null) { stagedRequests = new LinkedList<Request<?>>(); } stagedRequests.add(request); mWaitingRequests.put(cacheKey, stagedRequests); if (VolleyLog.DEBUG) { VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey); } } else { //没有的话就将请求加入缓存队列mCacheQueue,同时加入mWaitingRequests中用来做下次同样请求来时的重复判断依据 // Insert 'null' queue for this cacheKey, indicating there is now a request in // flight. mWaitingRequests.put(cacheKey, null); mCacheQueue.add(request); } return request; } }
通过判断request.shouldCache(),来判断是否可以缓存,默认是可以缓存的:
- 如果不能缓存,则将请求添加到网络请求队列中
- 如果能缓存就判断之前是否有执行相同的请求且还没有返回结果的
- 如果有的话将此请求加入mWaitingRequests队列,不再重复请求;
- 没有的话就将请求加入缓存队列mCacheQueue,同时加入mWaitingRequests中用来做下次同样请求来时的重复判断依据。
在默认情况下,每条请求都是可以缓存的,当然我们也可以调用Request的setShouldCache(false)方法来改变这一默认行为。
从上面可以看出RequestQueue的add()方法并没有做什么请求网络或者对缓存进行操作。当将请求添加到网络请求队列或者缓存队列时,这时在后台的网络调度线程和缓存调度线程轮询各自的请求队列发现有请求任务则开始执行,我们先看看缓存调度线程CacheDispatcher中的run()方法
2.3 缓存调度线程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 { //获取缓存队列中的一个请求 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"); //对数据进行解析并回调给主线程 Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders)); e.addMarker("cache-hit-parsed"); if(!entry.refreshNeeded()) { this.mDelivery.postResponse(e, response); } else { 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 { e.addMarker("cache-hit-expired"); e.setCacheEntry(entry); this.mNetworkQueue.put(e); } } } catch (InterruptedException var4) { if(this.mQuit) { return; } } } } } }}static { DEBUG = VolleyLog.DEBUG;}
- 可以看到一个while(true)循环,说明缓存线程始终是在运行的
- 1.判断是否请求是否被取消了
1.1如果请求取消了则将请求停止掉
1.2如果请求未取消,接着会尝试从缓存当中取出响应结果- 1.2.1如果缓存响应为空,则将请求加入网络请求队列
- 1.2.2如果不为空的话,再判断该缓存是否已过期
1.2.2.1如果已经过期了则同样把这条请求加入到网络请求队列中
1.2.2.2如果未过期就认为不需要重发网络请求,直接使用缓存中的数据即可。之后会调用Request的parseNetworkResponse()方法来对数据进行解析,再往后就是将解析出来的数据进行回调了
“调用Request的parseNetworkResponse()方法来对数据进行解析并回调”部分代码我们先跳过,因为它的逻辑和NetworkDispatcher后半部分的逻辑是基本相同的,那么我们等下合并在一起看就好了,先来看一下NetworkDispatcher中是怎么处理网络请求队列的
2.3 网络调度线程NetworkDispatcher.run()
public void run() { Process.setThreadPriority(10); while(true) { long startTimeMs; Request request; while(true) { startTimeMs = SystemClock.elapsedRealtime(); try { //从队列中取出请求 request = (Request)this.mQueue.take(); break; } catch (InterruptedException var6) { if(this.mQuit) { return; } } } try { request.addMarker("network-queue-take"); if(request.isCanceled()) { request.finish("network-discard-cancelled"); } else { this.addTrafficStatsTag(request); //请求网络 NetworkResponse e = this.mNetwork.performRequest(request); request.addMarker("network-http-complete"); if(e.notModified && request.hasHadResponseDelivered()) { request.finish("not-modified"); } else { Response volleyError1 = request.parseNetworkResponse(e); request.addMarker("network-parse-complete"); if(request.shouldCache() && volleyError1.cacheEntry != null) { //将响应结果存入缓存 this.mCache.put(request.getCacheKey(), volleyError1.cacheEntry); request.addMarker("network-cache-written"); } request.markDelivered(); this.mDelivery.postResponse(request, volleyError1); } } } catch (VolleyError var7) { var7.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); this.parseAndDeliverNetworkError(request, var7); } catch (Exception var8) { VolleyLog.e(var8, "Unhandled exception %s", new Object[]{var8.toString()}); VolleyError volleyError = new VolleyError(var8); volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); this.mDelivery.postError(request, volleyError); } } }
- 可以看到一个while(true)循环,说明缓存线程始终是在运行的
- 1.判断是否请求是否被取消了
- 1.1如果请求取消了则将请求停止掉
- 1.2如果请求未取消,去请求网络得到响应并回调给主线程
1.2.1 请求网络任务BasicNetwork.performRequest()
1.2.2 请求网络后,会将响应结果存在缓存中,如果响应结果成功则调用this.mDelivery.postResponse(request, volleyError1)来回调给主线程
请求网络时调用this.mNetwork.performRequest(request),这个mNetwork是一个接口,实现它的类是BasicNetwork,具体看2.3.1
请求网络后Delivery的postResponse()回调给主线程,具体看2.3.2
2.3.1 请求网络任务BasicNetwork.performRequest()
public NetworkResponse performRequest(Request<?> request) throws VolleyError { long requestStart = SystemClock.elapsedRealtime(); while(true) { HttpResponse httpResponse = null; Object responseContents = null; Map responseHeaders = Collections.emptyMap(); try { HashMap e = new HashMap(); this.addCacheHeaders(e, request.getCacheEntry()); httpResponse = this.mHttpStack.performRequest(request, e); StatusLine statusCode1 = httpResponse.getStatusLine(); int networkResponse1 = statusCode1.getStatusCode(); responseHeaders = convertHeaders(httpResponse.getAllHeaders()); if(networkResponse1 == 304) { Entry requestLifetime2 = request.getCacheEntry(); if(requestLifetime2 == null) { return new NetworkResponse(304, (byte[])null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart); } requestLifetime2.responseHeaders.putAll(responseHeaders); return new NetworkResponse(304, requestLifetime2.data, requestLifetime2.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart); }...省略
从上面可以看到在12行调用的是HttpStack的performRequest()方法请求网络,接下来根据不同的响应状态码来返回不同的NetworkResponse。另外HttpStack也是一个接口,实现它的两个类我们在前面已经提到了就是HurlStack和HttpClientStack
2.3.2 回调主线程Delivery.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)); }
来看看ResponseDeliveryRunnable里面做了什么:
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行调用了this.mRequest.deliverResponse(this.mResponse.result),这个就是实现Request抽象类必须要实现的方法,我们来看看StringRequest的源码:
public class StringRequest extends Request<String> { private final Listener<String> mListener; public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener) { super(method, url, errorListener); this.mListener = listener; } public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) { this(0, url, listener, errorListener); } protected void deliverResponse(String response) { this.mListener.onResponse(response); } ...省略}
在deliverResponse方法中调用了this.mListener.onResponse(response),最终将response回调给了Response.Listener的onResponse()方法。我们用StringRequest请求网络的写法是这样的:
RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());StringRequest mStringRequest = new StringRequest(Request.Method.GET, "http://www.baidu.com",new Response.Listener<String>() { @Override public void onResponse(String response) { Log.i("wangshu", response); }}, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.e("wangshu", error.getMessage(), error); } });//将请求添加在请求队列中mQueue.add(mStringRequest);
- (4.2.10.3)【android开源组件】Android Volley完全解析(四),带你从源码的角度理解Volley
- (4.2.10.3)【android开源组件】Android Volley完全解析(四),带你从源码的角度理解Volley
- Android Volley完全解析(四), 带你从源码的角度理解Volley
- Android Volley完全解析(四),带你从源码的角度理解Volley
- Android Volley完全解析(四),带你从源码的角度理解Volley
- [原]Android Volley完全解析(四),带你从源码的角度理解Volley
- Android Volley完全解析(四),带你从源码的角度理解Volley
- Android Volley完全解析(四),带你从源码的角度理解Volley
- Android Volley完全解析(四),带你从源码的角度理解Volley
- Android Volley完全解析(四),带你从源码的角度理解Volley
- Android Volley完全解析(四),带你从源码的角度理解Volley
- Android Volley完全解析(四),带你从源码的角度理解Volley
- Android Volley完全解析(四),带你从源码的角度理解Volley
- Android Volley完全解析(四),带你从源码的角度理解Volley
- Android Volley完全解析(四),带你从源码的角度理解Volley
- Android Volley完全解析(四),带你从源码的角度理解Volley
- Android Volley完全解析(四),带你从源码的角度理解Volley
- Android Volley完全解析(四),带你从源码的角度理解Volley
- 你不知道的JavaScript--Item6 var预解析与函数声明提升(hoist )
- [笔记]Eclipse使用及快捷键
- 欢迎使用CSDN-markdown编辑器
- 笨方法学python——ex18及课后习题
- js 事件绑定的方法
- (4.2.10.3)【android开源组件】Android Volley完全解析(四),带你从源码的角度理解Volley
- css3实现一些炫酷的东西
- 进程间通信——共享内存
- 类图作业
- 考研英语
- hive学习计划
- 运行ogg director客户端时面板为空白
- keras optimizers 默认学习率
- Unity UGUI 鼠标穿透UI问题