Volley源码解析(二)

Volley源码解析 (一)中简单的说明了volley的执行流程:从Request到生成NetWorkResponse的过程,本文就上篇博客的基础上继续讲解最后一步——>生成Response<>的过程(本篇的读者建议先大致看下《Volley 源码解析(一)》。

简单了解下Network Response:

NetworkResponse包含了网络返回的主要数据:比如原始数据的字节数组,服务器header信息等,代码如下:

  /** http状态码. */
    public final int statusCode;

    /** 服务器响应的原始数据 */
    public final byte[] data;

    /** 响应http首部*/
    public final Map<String, String> headers;

也就是说我们通过操作BasicNetwork返回的NetworkResponse来对原始数据进行处理;换句话说我们可以不通过volley提供的Response《String》,Response《JsonObject》,Response《JsonArray》等对象,而我们自己对原始数据进行转换和使用。

比如下面代码,发起一个请求,然后将数据转换成字符串:

  //创建一个请求对象
  StringRequest request = new StringRequest("http://www.baidu.com",null,null);

  //利用HurlStack创建一个BasicNetwork对象
  BasicNetwork basicNetwork = new BasicNetwork(new HurlStack());

  //发起请求,返回NetWorkResponse
  NetworkResponse networkResponse = basicNetwork.performRequest(request);

 //将原始数据转换成字符串       
 String str = new String(networkResponse.data);

 //对Str进行打印。输出结果如下:

这里写图片描述
通过上面几行代码我们就可以获取想要的字符串了,那么volley对是怎么获取String的呢?在StringRequest的基类Request提供了一个抽象方法:

//注意该方法返回的是Response对象
 abstract protected Response<T> parseNetworkResponse(NetworkResponse response);

所以我们可以看看StringRequest类对此抽象方法的具体实现:

protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException e) {
            parsed = new String(response.data);
        }
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }

代码很简单,也就是讲NetworkRespone的data和header转换成parsed 字符串,然后交给Response的success方法,最终返回一个Response《String》对象的过程:

 public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
        //将处理的结果交给result来持有
        return new Response<T>(result, cacheEntry);
    }

所以如果读者想简单了解ImageRequest的Bitmap是怎么生成的,就可以参看ImageRequest的parseNetworkResponse方法,同样的道理可以了解volley的JsonReqeust对象JsonObject构建原理,此处不在赘述。

我们可以通过Response的result来获取我们需要的数据对象Bitmap,JsonObject,String等

简单对上文的说明做下总结,也是volley工作流程大致过程:
1、构建相应的Request对象
2、Volley使用BasicNetwork对象的performRequest(request)发起数据请求,获得NetworkResponse对象
3、将NetworkResponse交给Request的parseNetworkResponse方法,将networkResponse持有的原始数据data(byte[])转换成具体的对象:比如String、Bitmap、JsonObject等。
4、将步骤3生成对象数据交给Response对象的result对象持有,从而交给客户端使用。

不知道读者发现没有,到此为止,扯了这么多,博主使用没有说明上述的这些过程在Volley时怎么发起的!!!比如BasicNetwork的performRequest是什么时候调用的?parseNetworkResponse又是什么时候调用的,Response对象又是怎么交给客户端的呢?下面就来讲解下这部分。

通常一些耗时的操作都需要在线程中去处理,网络请求也不例外,Volley亦然!!Volley提供了一个NetworkDispatcher的线程类(Thread的子类),这个线程类的主要作用就是执行上面讲解的四个步骤!所以看看其run方法都做了些什么:

  public void run() {
        while (true) {//一个while循环
            Request<?> request;
            try {
                //从队列里获取一个请求
                request = mQueue.take();
            } catch (InterruptedException e) {
                if (mQuit) {
                    return;
                }
                continue;
            }

            try {
              //如果客户端取消了请求,则调用请求回调接口通知客户端
              if (request.isCanceled()) {
               request.notifyListenerResponseNotUsable();
                    //继续循环获取队列里的下一个请求
                    continue;
               }

                //执行BasicNetwork的performReqeust方法
               NetworkResponse networkResponse = mNetwork.performRequest(request);

         //如果服务服返回304状态吗,并且我们已经将请求交付给客户端
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {

                    //告诉服务端没有有效的响应数据
               request.notifyListenerResponseNotUsable();
                    //继续循环,获取队列中的下一个请求
                    continue;
                }

                //在工作线程将数据转换成Response对象
                Response<?> response = request.parseNetworkResponse(networkResponse);

                //开始缓存
                if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);

                }

                //标记当前响应已经交给客户端
                request.markDelivered();
                //发送响应数据给客户端
                //主要是回调Listener接口,通知客户端获取数据
                mDelivery.postResponse(request, response);

                                              request.notifyListenerResponseReceived(response);
            } catch (VolleyError volleyError) {
            } catch (Exception e) {
            }
        }
    }

上面的代码执行流程也清晰,简单来说就是不断请求队列中获取一个请求,拿到一个Request对象之后其核心逻辑也就是调用BasicNetwork对象的performRequest获取NetworkResponse对象;然后调用Request的parseNetworkResponse方法来构架自己所需要的响应对象。

RequestQueue简单描述

注意上面说到了请求队列的东西,在volley中用RequestQueue来表示volley的请求队列,该类有如下属性:

//记录当前请求对象
 private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();

    /** 缓存相关(本篇博文暂且不谈) */
    private final PriorityBlockingQueue<Request<?>> mCacheQueue =
            new PriorityBlockingQueue<>();

    /** 网络请求队列(支持线程优先级的) */
    private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
            new PriorityBlockingQueue<>();

可以看出RequestQueue一个优先级阻塞队列mNetworkQueue来添加请求。本文暂且不谈缓存功能。先来分析分析volley怎么向该队列添加请求对象,在RequestQueue中有add方法:

 public <T> Request<T> add(Request<T> request) {
        //设置request所属的队列
        request.setRequestQueue(this);
        //此处省略部分代码
        if (!request.shouldCache()) {
            mNetworkQueue.add(request);
            return request;
        }
         //此处省略部分代码
        return request;
     }

抛开缓存等功能(省略的代码)不说,add方法也就是向队列中不断添加Request对象,然后各个请求对象与请求队列进行绑定,简单的关系如图所示:
这里写图片描述
然后这些队列中的请求对象又被NetworkDispatcher不断获取发起网络请求。在RequestQueue中提供了start方法来开启NetworkDispatcher线程:

 public void start() {
        // 默认 mDispatchers.length=4,开启四个
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

在volley中一共开启了四个NetworkDispatcher线程对象来从同一个队列中获取请求对象,然后不断发起http请求,结合以上说明,volley的请求过程可以入下图表示:

这里写图片描述

那么客户端是怎么拿到这些数据的呢?就且听下回分解吧。

猜你喜欢

转载自blog.csdn.net/chunqiuwei/article/details/78030443