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

上一篇(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类的类图和作用,请看后续文章

时间: 2024-10-20 00:52:41

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

OKHttp源码解析之网络请求

OKHttp是square公司的开源项目,当前android开发中最常用的轻量级框架.本文中主要是解析OKHttp是如何建立网络连接,即HttpEngine,Connection中的部分代码.(注:解析的版本是2.5.0版本) 在开始前我们先要确定以下几个问题,这将助于对源码的理解(如果已经清楚的大神可以跳过),问题如下: 1.http同tcp有什么关系? http是应用层协议,依赖于传输层的tcp协议.通俗的讲http就是一个tcp连接,只不过它是以一种"短连接"的形式存在. 2.h

Android 网络框架 volley源码剖析

转载请注明出处:  http://blog.csdn.net/guolin_blog/article/details/17656437 经过前三篇文章的学习,Volley的用法我们已经掌握的差不多了,但是对于Volley的工作原理,恐怕有很多朋友还不是很清楚.因此,本篇文章中我们就来一起阅读一下Volley的源码,将它的工作流程整体地梳理一遍.同时,这也是Volley系列的最后一篇文章了. 其实,Volley的官方文档中本身就附有了一张Volley的工作流程图,如下图所示. 多数朋友突然看到一张

[Android]Volley源码分析(四)

上篇中有提到NetworkDispatcher是通过mNetwork(Network类型)来进行网络访问的,现在来看一下关于Network是如何进行网络访问的. Network部分的类图: Network有一个实现类BasicNetwork,它有一个mHttpStack的属性,实际的网络请求是由这个mHttpStack来进行的,看BasicNetwork的performRequest()方法, 1 @Override 2 public NetworkResponse performRequest

Volley 源码解析(转)

项目:Volley,分析者:grumoon,校对者:Trinea 本文为 Android 开源项目源码解析 中 Volley 部分项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo分析者:grumoon,校对者:huxian99.Trinea,校对状态:完成 1. 功能介绍 1.1. Volley Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架.在 Google I/O 2013 大会上发布. 名字由来:a burs

Volley源码分析

Volley源码分析 Volley简介 volley官方地址 在Google I/0 2013中发布了Volley.Volley是Android平台上的网络通信库,能使网络通信更快,更简单,更健壮. 这是Volley名称的由来:a burst or emission of many things or a large amount at once.Volley特别适合数据量不大但是通信频繁的场景. Github上面已经有大神做了镜像,使用Gradle更方便.Volley On Github Vo

Volley 源码解析&lt;转&gt;

Volley 源码解析 1. 功能介绍 1.1. Volley Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架.在 Google I/O 2013 大会上发布. 名字由来:a burst or emission of many things or a large amount at once发布演讲时候的配图 从名字由来和配图中无数急促的火箭可以看出 Volley 的特点:特别适合数据量小,通信频繁的网络操作.(个人认为 Android 应用中绝大多数的网

Volley源码解析

Volley源码分为两部分 1.核心类库(主要以接口为主)   2.各接口实现类.工具类          第2部分以实现接口.提供工具为主,不影响数据流转,所以我们可以暂时先不管.看第1部分,很多Error.Log暂时先不看,比较外围.剩下的类,贴个UML如下 各接口&类解释Network:通过网络(HttpURLConnection或者HttpClient),发送请求并得到应答Cache:提供缓存功能,主要为get.put两个方法设置和获取缓存NetworkDispatcher:获取网络侧阻

Volley源码解读(上)

Volley框架的使用 Volley网络框架的使用方式绝大部分人都已经很熟悉了. 最简单的就是通过Volley提供的静态方法newRequestQueue(Context context)来返回一个消息队列MessageQueue,然后在需要使用时将构造的网络请求消息添加到队列中去,这样就可以完成网络请求 //定义全局的请求队列 requestQueue=Volley.newRequestQueue(getApplicationContext()); //实例化一个请求,并添加到请求队列中去:

Volley 源码解析(转自codeKK)

Volley 源码解析 本文为 Android 开源项目源码解析 中 Volley 部分项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo分析者:grumoon,校对者:huxian99.Trinea,校对状态:完成 1. 功能介绍 1.1. Volley Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架.在 Google I/O 2013 大会上发布. 名字由来:a burst or emission of many