Volley源码解析(二)

来源:互联网 发布:java web菜单权限控制 编辑:程序博客网 时间:2024/06/08 11:52

在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的请求过程可以入下图表示:

这里写图片描述

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

原创粉丝点击