码迷,mamicode.com
首页 > 其他好文 > 详细

Volley源码(2):执行网络请求的流程

时间:2016-05-12 23:51:32      阅读:306      评论:0      收藏:0      [点我收藏+]

标签:

      上一篇(http://blog.csdn.net/szxgg/article/details/51345859)讲述了当我们调用Volley.newRequest()时,Volley内部这个类做了什么,其实Volley这个类就做了一件事情,就是实例化了RequesQueue,这也符合设计模式中的单一职责,其实主要的处理都在其他类中,有三个类最重要,HttpStack/Network/RequestQueue,之后会讲解这些类的关系及作用,那首先还是结合我们使用Volley时的情形来看看源码内部执行流程吧。

   我们在实例化RequestQueue后,如果要请求,那么得做两件事

1)实例化Request对象

StringRequest request = new StringRequest(Request.Method.GET, "www.58.com", new Response.Listener<String>() {
            @Override
            public void onResponse(String s) {

            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                
            }
        });

2)将Request对象加入到RequestQueue中

   mQueue.add(request);
    首先Request有很多种,我们这一篇先不细究,只用StringRequest来进行演示.

    当这么做了后,会发现结果的回调回到生成requset时传入的参数Response.Listener中,失败的结果会回调到Response.ErrorListener中,那么这一切是怎么发生的呢?需要看源码来进行解释了。

   首先生成request对象,然后把request对象加入到requestQueue中,那一定是requestQueue对象得到request后进行的处理,那么就看看requestQueue,add(request)这个方法,总共做了如下几件事情:

1)给传入的request设置requestQueue,就是当前的实例,这样 对于request就与requestQueue产生了联系,那么如果有多个requestQueue时,具体的request属于哪个requestQueue就能知道了

  request.setRequestQueue(this);
  

      2)把传入的request加入到当前队列中

 synchronized(this.mCurrentRequests) {
            this.mCurrentRequests.add(request);
        }
  然后给requset设置了一些标记,无关紧要

    3)判断request是不是应该缓存,---这个标志是在生成request后可以手动设置的,默认是应该缓存

如果不应该缓存,直接把request加入到networkQueue中,返回

if(!request.shouldCache()) {
            this.mNetworkQueue.add(request);
            return request;
        } 
如果应该缓存,则

A.得到request的缓存key

String cacheKey = request.getCacheKey();
默认是method:url

B.如果waitingRequest中包含这个缓存key,则根据缓存key去waitingRequest中去找value

 if(this.mWaitingRequests.containsKey(cacheKey)) {
                    Object stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);

                                      B1.如果stagedRequests为空,则实例化为一个LinkedList   

<span style="white-space:pre">			</span>stagedRequests = new LinkedList();
     B2.把request加入到这个stagedRequests中             

 <span style="white-space:pre">			</span>((Queue)stagedRequests).add(request);
    B3.waitingRequest根据cacheKey存入这个stagedRequests值

 <span style="white-space:pre">			</span>this.mWaitingRequests.put(cacheKey, stagedRequests);
C.如果waitingRequest不包含这个缓存key,则存入waitingRequests中,并且value为null,也存入cacheQueue

<span style="white-space:pre">	</span>this.mWaitingRequests.put(cacheKey, (Object)null);
        this.mCacheQueue.add(request);
   按照上面的逻辑当我们生成cacheKey为get:www.baidu.com的request时,第一次请求时会把这个request放入waitingRequest和cacheQueue中

第二次再请求时,会给他的waitingQueue中的value生成LinkedList并把request放入其中,请求两次后waitingRequest才圆满,至于这样做有啥用还有几个request作用先不管。

    到这一步,requestQueue.add(request)方法就结束了,主要干了下面几件事:

1.给request绑定

2.将request加入到currentRequests(HashSet)中

3.判断request需不需要缓存(默认需要)

如果不需要缓存,直接把request加入到networkQueue中

如果需要缓存,先从waitingRequest找,然后设置加入到ccheQueue中(两次)

      requestQueue有几个和request产生关联的数据结构

1.

private final Set<Request<?>> mCurrentRequests;
2.
 private final Map<String, Queue<Request<?>>> mWaitingRequests;
3.

private final PriorityBlockingQueue<Request<?>> mCacheQueue;
4.

private final PriorityBlockingQueue<Request<?>> mNetworkQueue;


那么到底在哪发生的请求呢,好像add方法中并没有啊,上一篇中生成requestQueue时,requestQueue最后调用了,queue.start(),去看看那个方法。

  1.调用了stop方法可以看做在start前进行的一个清空操做

public void stop() {
        if(this.mCacheDispatcher != null) {
            this.mCacheDispatcher.quit();//thread.interrupt
        }

        for(int i = 0; i < this.mDispatchers.length; ++i) {
            if(this.mDispatchers[i] != null) {
                this.mDispatchers[i].quit();//thread.interrupt
            }
        }

    }
2.生成cacheDispatcher,并且start

 this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
 this.mCacheDispatcher.start();
cacheDispatcher是一个Thread,就是执行了CacheDispatcher的run方法

实例化时,传入了cacheQueue和networkQueue,这两个就是上文提到的add中的两个,还传入了cache和Delivery,Cache是缓存用的,Delivery主要进行回调操作

然后根据构造RequestQueue时的threadPoolSize,实例化NetworkDispatcher的个数,并调用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,NetworkDispatcher实例化时少传了一个CacheQueue,多传了一个Network,这个network就是Volley中实例化RequestQueue时生成的


RequestQueue和CacheDispatcher以及NetworkDispatcher产生联系是通过cacheQueue和networkQueue生成的,那么接下来分析下这两个thread的run方法。


CacheDispatcher.run():

1)首先初始化缓存Cache---目前是DefaultBaseCache

 this.mCache.initialize();
然后无限的循环while(true)

2)从cacheQueue中寻找临近的Request

 while(true) {
                  final Request e = (Request)this.mCacheQueue.take();
3)根据request的cacheKey,去Cache中寻找Entry

Entry entry = this.mCache.get(e.getCacheKey());
4)如果entry为空或者过期了,就把这个request加入到networkQueue中

 

 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);
                                    }
5)如果entry没有过期,那么就不去网络取了,直接调用这个request.parseNetworkResponse

 Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
数据就是从Cache中得到的Entry的值

6)判断是否需要刷新,如果需要,就当这次请求回来后再把这个request再次放到networkQueue中,再次去请求新的数据下次用

 if(entry.refreshNeeded()) {
                                          ...
                                            this.mDelivery.postResponse(e, response, new Runnable() {
                                                public void run() {
                                                    try {
                                                        CacheDispatcher.this.mNetworkQueue.put(e);
                                                    } catch (InterruptedException var2) {
                                                        ;
                                                    }

                                                }
                                            });

7)如果不需要刷新,就只执行Delievery的postResponse操作

this.mDelivery.postResponse(e, response);

上述是CacheDispatcher的run方法,主要做了几件事情,首先从CacheQueue中寻找这个request,如果存在且没过期就直接通过这个request得到缓存起来的结果,返回

否则就把它加入到NetworkQueue中,而这个NetworkQueue的实例同样也是NetworkDispatcher中的,那么就看看NetworkDispatcher,这里面就是网络访问的东西

NetworkDispatcher.run();

1)循环的从mQueue也就是NetworkQueue中寻找Request(就是CacheDispatcher有的存入操作/addQueue中也有)

 while(true) {
              ...

                try {
                    request = (Request)this.mQueue.take();
                    break;
                } catch (InterruptedException var6) {
                    if(this.mQuit) {
                        return;
                    }
                }
            }
2)调用network方法去处理这个request

 NetworkResponse e = this.mNetwork.performRequest(request);
网络请求就在这里了!!!!


3)调用request的请求把networkResponse转化成对外暴露的Response,并通过Delievery返回

 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);
                    }

基本上大致的流程就这样。

首先将request加入到requestQueue中,如果request不需要缓存,那么直接将这个request添加到networkQueue中,否则加入到cacheQueue中,cacheQueue在CacheDispatcher中,CacheDispatcher无限循环的去在cacheQueue中拿request,如果request满足条件,那直接不请求,转成需要的response回调回去,否则再把这个request放到networkQueue中,而netWorkDispatcher中有这个networkQueue的实例,则会找到后通过network.perfromRequest来找结果,解析回调


因此网络访问就是在Network的performRequest中去的,那这个Network我们目前用的是BasicNetwork,所以需要看看这里面的代码

BasicNetwork.performRequest()

1)调用其内部的HttpStack.performRequest().得到HttpResponse

 httpResponse = this.mHttpStack.performRequest(request, e);
2)把HttpResponse转化成NetworkResponse

...
if(httpResponse.getEntity() != null) {
                    responseContents1 = this.entityToBytes(httpResponse.getEntity());
                } else {
                    responseContents1 = new byte[0];
                }

                long requestLifetime1 = SystemClock.elapsedRealtime() - requestStart;
                this.logSlowRequests(requestLifetime1, request, responseContents1, statusCode1);
                if(networkResponse1 >= 200 && networkResponse1 <= 299) {
                    return new NetworkResponse(networkResponse1, responseContents1, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
                }

其实质还是HttpStack.performRequest

SDK>=9,用的是HurlStack,其他用的是HttpClientStack

这里面才是真正的调用网络,connection什么的


因此访问网络:

NetworkQueue中有,取出,调用Network.performRequest()-------HttpStack.performRequest()

得到HttpResponse----转成NetworkResponse------用request自己转成Response


调用Delievery----最后回调到自己的request.delieveryResponse()/request.delieverError()

  整体流程就是这样,那么还有些细节性的问题需要研究

比如Cache到底是怎样的

Delievery是怎样从子线程切换到主线程回调给调研者的

还有HttpStack类,Network类,Delievery类,Request类的类图和作用,请看后续文章





 



Volley源码(2):执行网络请求的流程

标签:

原文地址:http://blog.csdn.net/szxgg/article/details/51346077

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!