Volley源码解析之---一次完整的StringRequest请求(1)

wuliVolley请求框架出来已经许久许久,小白的我,以前只知其一,而不知其二。 现在才开始学习他的源码,惭愧惭愧,既然都落后了,当然得乘胜追击学起来,否则只能永远落后了(放了一波毒鸡汤)。希望能学习其中的编程思想以及了解它是如何完成整个请求以及实现磁盘缓存的。本文我将通过一个分析StringRequest的请求来完整的剖析一下其中涉及到的重要代码。我的初衷一直都没有变,那就是每写一篇文章是为了给自己的知识库添上一点,也是让自己坚持学习,同时给学习路上的你我他提供一点参考的资料。 本文将会有点长,别失去耐心,好好看下去。我相信肯定能让你对Volley更了解。 Are you Ready? Go!

要学习一个源码,我们可以首先从我们使用的地方出发去学习,这样更好理解,也不至于一下子迷失在源码的汪洋大海中。所以我们先看看我们怎么用它。当请求一个string的时候,我们会:

// 1.new一个RequestQueue
RequestQueue  mRequestQueue = Volley.newRequestQueue(context); 
//2.new一个StringRequest对象,传入Response.Listener 和Response.ErrorListener来监听请求成功和请求失败的时候的回调
StringRequest request = new StringRequest("http://www.baidu.com", new Response.Listener<String>() {
                 @Override
                 public void onResponse(String s) {
                     txt_response.setText(s);
                 }
             }, new Response.ErrorListener() {
                 @Override
                 public void onErrorResponse(VolleyError volleyError) {
                     Log.e("onError", volleyError.getMessage());
                 }
             });
 //将请求加入到请求队列中
mRequstQueue.add(request);

RequestQueue正如它的字面意思一样就是一个请求队列。每当有一个新的请求的时候就将请求添加进去。让我们看看Volley.newRequstQueue做了些什么。

 public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, (HttpStack)null);
    }

可以看到它调用了另外一个方法,传入了一个null的HttpStack,这个是什么呢?
Volley提供了HttpStack的两个实现子类,HttpClientStack 与 HurlStack HttpClientStack 和HurlStack是两种网络请求的封装类.当Android的SDK版本号小于9的时候,使用的是HttpStack来进行网络请求,而HttpClientStack是通过HttpClient来请求网络数据的。相反,当sdk大于9时,使用HurlStack来请求网络数据,HurlStack是通过HttpURLConnection来进行网络连接并请求数据的。具体是如何请求数据的这里我们就不讲解了,简单提一下就是通过HttpClient或者HttpUrlConnection 来请求数据获得Response. 感兴趣的可以自己在源码中找到对应的类查看一下。
接着往下看:

     /**
     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
     *
     * @param context A {@link Context} to use for creating the cache dir.
     * @param stack An {@link HttpStack} to use for the network, or null for default.
     * @return A started {@link RequestQueue} instance.
     */
    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        //定义一个缓存目录,默认是在应用的缓存目录下创建一个叫volley的目录用于存储缓存
        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) {
            //如果stack是null,则根据SDK的版本来创建对应的HttpStack的子类
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack(); //通过HttpUrlConnection请求
            } else {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                //通过HttpClient连接请求网络数据
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }
        //传入HttpStack,创建一个Network对象,在我看来Network类是对HttpStack的进一步封装,添加了对请求结果的一些处理,实际网络请求是通过HttpStack实现的。
        Network network = new BasicNetwork(stack);
        //创建并启动一个RequestQueue 
        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        queue.start();

        return queue;
    }

我已经在代码中添加了对应的注释,主要就是创建了HttpStack对象传递给Network的构造函数,然后用于网络请求,之后通过Network以及DiskBasedCache构造一个RequestQueuestart。在这里DiskBasedCache是一个默认的管理磁盘缓存的一个类,用于缓存请求的结果。Volley的缓存机制就是通过这个实现的,****需要注意的是因为Volley的缓存都是缓存在磁盘里面的所以缓存读取速度要比内存缓存更慢****。当然你也可以改成自己要的内存缓存,这也是我们学习源码的一个目的,不仅知道别人是怎么写的,更重要的是能够灵活运用并且将其改造成我们需要的样子。

接下来我们来看一下RequestQueue的构造函数和start都做了些什么,为什么start之后,然后只需要将Request加入队列就开始请求数据并且得到对应的Response

  通过追踪代码,可以知道最终调用了这个构造函数。
  /**
  创建一个工作池,只有start方法调用了之后,进程才会开始
  * Creates the worker pool. Processing will not begin until {@link #start()} is called.
     *
     * @param cache A Cache to use for persisting responses to disk
     * @param network A Network interface for performing HTTP requests
     * @param threadPoolSize Number of network dispatcher threads to create
     * @param delivery A ResponseDelivery interface for posting responses and errors
     */
    public RequestQueue(Cache cache, Network network, int threadPoolSize,
            ResponseDelivery delivery) {
        mCache = cache;
        mNetwork = network;
        mDispatchers = new NetworkDispatcher[threadPoolSize];
        mDelivery = delivery;
    }

在上面的英文注释当中已经说得很清楚了,Cache就是用于将Http请求的Response存储到磁盘,Network用于执行Http请求。NetworkDispatcher是一个线程,用于网络请求的分发。这里我们默认是开启了4个NetworkDispatcher.ResponseDelivery是用于分发请求的错误和响应的。
看完了构造函数我们就来看看start()方法吧。

 /**
     * Starts the dispatchers in this queue.
     */
    public void start() {
        //首先将确保所有CacheDispatcher和NetworkDispatcher都停止
        stop();  // Make sure any currently running dispatchers are stopped.
        //接下来开启CacheDispatcher和NetworkDispatcher线程
        // 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();
        }
    }

这里面的代码虽然不多,但是确是万物之源。 因为它开启了CacheDispatcherNetworkDispatcher线程,然后开始了监听网络请求的工作。那么Cache DispatcherNetworkDispatcher分别做了哪些工作呢。
这里我们先来看看NetworkDispatcher,上面我们说到过它是一个线程,所以我们先看看它的构造方法,然后直击他的run方法。

 /**
     * Creates a new network dispatcher thread.  You must call {@link #start()}
     * in order to begin processing.
     *
     * @param queue Queue of incoming requests for triage
     * @param network Network interface to use for performing requests
     * @param cache Cache interface to use for writing responses to cache
     * @param delivery Delivery interface to use for posting responses
     */
    public NetworkDispatcher(BlockingQueue<Request<?>> queue,
            Network network, Cache cache,
            ResponseDelivery delivery) {
        mQueue = queue;
        mNetwork = network;
        mCache = cache;
        mDelivery = delivery;
    }

从上面可以知道,NetworkDispathcer拥有一个BlockingQueue阻塞式队列的一个引用。用于存储请求,还有Network, cache, ResponseDelivery这几个对象的作用,我在上面有提到过,不记得了可以向上滑动一下回忆一下。接下来我们看看NetworkDispatcher的run方法。

    @Override
    public void run() {
        //设置线程的优先级为后台线程
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            //获得开机到现在的时间
            long startTimeMs = SystemClock.elapsedRealtime();
            Request<?> request;
            try {
                // Take a request from the queue.
                //从队列中取出一个请求,如果没有请求,就阻塞线程等待请求。
                request = mQueue.take();
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }

            try {
                request.addMarker("network-queue-take");

                // If the request was cancelled already, do not perform the
                // network request.判断请求是否被取消了,取消了的话直接finish,下一次请求处理
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }

                addTrafficStatsTag(request);

                // Perform the network request. 调用mNetwork的performRequest,请求数据
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");
                 
                //对返回的NetworkResponse进行检测,如果notModified并且已经将结果分发过了(返回给用户了),直接finish
                // If the server returned 304 AND we delivered a response already,
                // we're done -- don't deliver a second identical response.
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

                // Parse the response here on the worker thread.
                //将请求转换成Response对象,parseNetworkResponse时对应的Request的子类实现的
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

                // Write to cache if applicable.
                // TODO: Only update cache metadata instead of entire record for 304s.
                if (request.shouldCache() && response.cacheEntry != null) {
                    //如果需要缓存并且cacheEntry不等于空就将本次请求,添加到缓存中
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

                // Post the response back. 标记已经将Response分发给Delivery(即回调给Request了)
                request.markDelivered();
                mDelivery.postResponse(request, response);
            } catch (VolleyError volleyError) {
                //如果出错了则回调onError方法
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                parseAndDeliverNetworkError(request, volleyError);
            } catch (Exception e) {
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
                VolleyError volleyError = new VolleyError(e);
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                mDelivery.postError(request, volleyError);
            }
        }
    }

在代码中,我已经在需要注释的地方,添加了详细的注释了。所以我们讲一下大概的,NetworkDispatcherrun方法在一个无限循环中不断的从请求队列中获取Request,然后调用mNetwork.performRequest请求网络数据。这里的mNetwork是一个BasicNetwork对象,那么我们就插播一个BasicNetwork的源码分析,从而看看mNetwork.performRequest(request)究竟都干了什么。

BasicNetwork

这部分主要是对BasicNetwork的源码的解析。那BasicNetwork是用来干什么的呢,BasicNetwork在我的理解中就是通过HttpStack来执行一个Volley request的。我们挑重点的代码来讲解一下,BasicNetwork主要干了些什么。

首先我们看BasicNetwork的定义和构造函数:

 
 /**
* A network performing Volley requests over an {@link HttpStack}.
*/
public class BasicNetwork implements Network {
/**
  * @param httpStack HTTP stack to be used
  */
 public BasicNetwork(HttpStack httpStack) {
     // If a pool isn't passed in, then build a small default pool that will give us a lot of
     // benefit and not use too much memory.
     this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
 }

 /**
  * @param httpStack HTTP stack to be used
  * @param pool a buffer pool that improves GC performance in copy operations
  */
 public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
     mHttpStack = httpStack;
     mPool = pool;
 }  

让我一起来看一下,BasicNetwork继承自Network,那么Network里面定义了什么呢?


/**
* An interface for performing requests.
*/
public interface Network {
  /**
   * Performs the specified request.
   * @param request Request to process
   * @return A {@link NetworkResponse} with data and caching metadata; will never be null
   * @throws VolleyError on errors
   */
  NetworkResponse performRequest(Request<?> request) throws VolleyError;
}

可以看到Network只是定义了一个抽象方法提供给子类实现,从方法的注释来看performRequest就是执行了一个请求并且返回NetworkResponse即网络请求的响应,具体怎么请求的由其子类的实现决定。好,从这里我们就可以知道performRequest是我们请求网络最关键的一个方法。
继续看BasicNetwork的构造方法,传入了两个参数,第一个参数HttpStack是一个封装了网络请求的类,其实BasicNetwork的网络请求的真正的执行是调用了HttpStack来实现的,在上一小节我们提到过。
第二个参数是一个ByteArrayPool,这个类是android提供的用于避免重复创建byte[]的一个缓存机制,类似于LruCache。减少堆内存的消耗,提高性能。

PerformRqeust

接下来就来看看BasicNetworkperformRequest方法是如何请求网络数据并且返回响应,以及如何处理的。

 @Override
   public NetworkResponse performRequest(Request<?> request) throws VolleyError {
       //获取开机到现在的时间
       long requestStart = SystemClock.elapsedRealtime();
       while (true) {
           HttpResponse httpResponse = null;
           byte[] responseContents = null;
           //创建一个新的空的集合
           Map<String, String> responseHeaders = Collections.emptyMap();
           try {
               // Gather headers.
               Map<String, String> headers = new HashMap<String, String>();
               //根据缓存来设置请求头的一些值 request.getCacheEntry() 返回一个request的缓存实体
               addCacheHeaders(headers, request.getCacheEntry());
               //发送一个请求并且得到相应的HttpResponse
               httpResponse = mHttpStack.performRequest(request, headers);
               StatusLine statusLine = httpResponse.getStatusLine();
               int statusCode = statusLine.getStatusCode(); //获取返回的状态码
               //获取响应的头部信息
               responseHeaders = convertHeaders(httpResponse.getAllHeaders());
               // Handle cache validation.
               if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
                   //当一个网络请求返回的响应码是304的时候,说明该url对应的资源没有改变,所以可以直接从缓存中获取上次缓存的数据
                   Entry entry = request.getCacheEntry();
                   if (entry == null) {
                       //如果entry为空的话,直接将响应头传入NetworkResponse
                       return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
                               responseHeaders, true,
                               SystemClock.elapsedRealtime() - requestStart);
                   }
                   //当响应码是304时,服务器的响应头里面会有一些新的信息,所以需要更新缓存中的响应头信息,比如新的过期时间等
                   // A HTTP 304 response does not have all header fields. We
                   // have to use the header fields from the cache entry plus
                   // the new ones from the response.
                   // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
                   entry.responseHeaders.putAll(responseHeaders);
                   return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                           entry.responseHeaders, true,
                           SystemClock.elapsedRealtime() - requestStart);
               }

               // Some responses such as 204s do not have content.  We must check.
               if (httpResponse.getEntity() != null) {
                   //将HttpEntity转换成对应的byte[]数组
                 responseContents = entityToBytes(httpResponse.getEntity());
               } else {
                 // Add 0 byte response as a way of honestly representing a
                 // no-content request.
                 responseContents = new byte[0];
               }

               // if the request is slow, log it.
               long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
               logSlowRequests(requestLifetime, request, responseContents, statusLine);

               if (statusCode < 200 || statusCode > 299) {
                   throw new IOException();
               }
               return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
                       SystemClock.elapsedRealtime() - requestStart);
           } catch (SocketTimeoutException e) {
               attemptRetryOnException("socket", request, new TimeoutError());
           } catch (ConnectTimeoutException e) {
               attemptRetryOnException("connection", request, new TimeoutError());
           } catch (MalformedURLException e) {
               throw new RuntimeException("Bad URL " + request.getUrl(), e);
           } catch (IOException e) {
               int statusCode;
               if (httpResponse != null) {
                   statusCode = httpResponse.getStatusLine().getStatusCode();
               } else {
                   throw new NoConnectionError(e);
               }
               VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
               NetworkResponse networkResponse;
               if (responseContents != null) {
                   networkResponse = new NetworkResponse(statusCode, responseContents,
                           responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
                   if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                           statusCode == HttpStatus.SC_FORBIDDEN) {
                       attemptRetryOnException("auth",
                               request, new AuthFailureError(networkResponse));
                   } else if (statusCode >= 400 && statusCode <= 499) {
                       // Don't retry other client errors.
                       throw new ClientError(networkResponse);
                   } else if (statusCode >= 500 && statusCode <= 599) {
                       if (request.shouldRetryServerErrors()) {
                           attemptRetryOnException("server",
                                   request, new ServerError(networkResponse));
                       } else {
                           throw new ServerError(networkResponse);
                       }
                   } else {
                       // 3xx? No reason to retry.
                       throw new ServerError(networkResponse);
                   }
               } else {
                   attemptRetryOnException("network", request, new NetworkError());
               }
           }
       }
   }

在上面的代码中我自己添加了比较详细的注释了。我们两一起看看,我觉得比较重要的几个方法。 在我们请求之前我们调用了addCacheHeaders(headers, request.getCacheEntry());来给请求头添加了一些参数。 跳进addCacheHeaders方法来看看它做了什么!!

 private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {
       // If there's no cache entry, we're done.
       if (entry == null) {
           return;
       }
       //初始化"If-None-Match"以及"If-Modified-Since"两个变量可以用于之后 判断是否服务器上的数据是否改变了
       if (entry.etag != null) {
           headers.put("If-None-Match", entry.etag);
       }

       if (entry.lastModified > 0) {
           Date refTime = new Date(entry.lastModified);
           headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
       }
   }

方法很简单首先判断一下缓存是否是空的,空的直接返回,不是空的话,取出entry.etagentry.lastModified分别赋值给请求头的If-None-MatchIf-Modified-Since.为什么要这样做呢,在网络请求的时候,一般当我们的请求有缓存的时候并且我们本地缓存过期的时候,因为缓存期过了,并不代表服务器上面的资源就一定变了。所以我们需要判断一下服务器上面的资源是否变了。那么如何判断呢?
这个时候就通过在请求中添加上一次缓存响应中的响应头的中的If-Modified-SinceIf-None-Match到请求响应头中,并传递给服务器。服务器接收到这个请求头的时候就会根据这两个变量判断一下资源是否改变。
那么If-Modified-SinceIf-None-Match分别代表了什么呢。
Last-Modified标记了资源文件在服务器的最后修改时间,当客户端由于缓存过期发起请求时,请求头要使用If-Modified-Since头部,它的值就是第一次服务器返回的Last-Modified。服务器收到这个时间后,跟当前的资源文件最后修改时间进行对比,如果服务器中资源文件的最后修改时间新与Last-Modified的值,那么说明资源文件进行修改过。
Etag又是什么呢,它是一个资源文件的标示,同样的当客户端由于缓存过期发送请求时, 请求头把上一次服务器返回的Etag传入请求头,服务器接收到之后,与当前资源的Etag对比,如果不一样了,则说明资源改变了。所以通过这两个变量就可以判断服务器的资源到底有没有变。
服务器通过上面两个变量判断资源是否改变了。如果没有改变则返回304,并返回新的响应头里面包含新的过期时间。如果改变了则返回200响应码.
addCacheHeaders之后就可以执行请求了,通过httpStack获得HttpResponse,根据返回响应码,做相应的处理。
如果StatusCode是304,则说明服务器上面的资源没有改变,则直接从缓存中拿数据,如果entry为空则直接返回一个data为null,headers==Response的headers的NetworkResponse。 但是如果entry不是空的话,则需要更新entryheaders更新过期时间等。
如果返回的响应码statusCode > 200 && statusCode < 299,则 将HttpEntity转换成bytes数组作为响应内容,并直接返回NetworkResponse,否则抛出IOException然后做相应的处理,最终的结果要么返回NetworkRespons,要么抛出VolleyError类型的异常,然后调用出根据返回值,来回调对应listener的方法。接下来我就不继续讲了。相信聪明的你们都能把剩下的看懂的。_!!
接着我们就返回到NetworkDispatcher的run方法继续向下看,现在我们知道mNetwork.performRequest(request)这句话都干了些什么,虽然代码有点多,但是其实就是获得了一个NetworkResponse。吓往下看。为了不用来回拉上去看,我把剩下的代码再贴一次。
```
//对返回的NetworkResponse进行检测,如果notModified并且已经将结果分发过了(返回给用户了),直接finish
// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}

            // Parse the response here on the worker thread.
            //将请求转换成Response对象,parseNetworkResponse时对应的Request的子类实现的
            Response<?> response = request.parseNetworkResponse(networkResponse);
            request.addMarker("network-parse-complete");

            // Write to cache if applicable.
            // TODO: Only update cache metadata instead of entire record for 304s.
            if (request.shouldCache() && response.cacheEntry != null) {
                //如果需要缓存并且cacheEntry不等于空就将本次请求,添加到缓存中
                mCache.put(request.getCacheKey(), response.cacheEntry);
                request.addMarker("network-cache-written");
            }

            // Post the response back. 标记已经将Response分发给Delivery(即回调给Request了)
            request.markDelivered();
            mDelivery.postResponse(request, response);
        } catch (VolleyError volleyError) {
            //如果出错了则回调onError方法
            volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
            parseAndDeliverNetworkError(request, volleyError);
        } catch (Exception e) {
            VolleyLog.e(e, "Unhandled exception %s", e.toString());
            VolleyError volleyError = new VolleyError(e);
            volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
            mDelivery.postError(request, volleyError);
        }
 ```

得到了NetwokResponse之后,我们需要对返回的请求结果检测和处理之后,传递给mDelivery,最后反馈掉调用的请求的Listener中。
首先判断一下networkResponse.notModified是否为true,如果为true,则说明服务器上的资源没有改变,并且request.hasHadResponseDelivered()这个请求的请求结果已经分发过了,那么就将这个请求finish掉,进行下一次请求。上面两个条件不同时符合,那么调用request.parseNetworkResponse(networkResponse);NetworkResonse转换成Response那么parseNetworkResponse这里方法对Response做了什么呢。看看Request的代码我们可以知道,parseNetworkResponse是一个由子类继承实现的一个方法,终于我们又可以看回StringRequest了,我们看看StringRequest方法做了些什么。

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

可以看到它将response转换成了String类型的Response,所以其实这个方法就是将NetworkResponse转换成了对应数据类型。拿到了Respnse<String>之后我们就可以通过mDelivery.postResponse(request, response);或mDelivery.postError(request, volleyError);将这个响应,通过StringRequst的deliverResponse方法,然后通过mListener将结果回调出去。 至此一次StringRequst请求我们就分析完了一半了。一起来看看mDelivery做了什么。

这里mDelivery是一个ExecutorDelivery对象,为什么?见下:

  public RequestQueue(Cache cache, Network network, int threadPoolSize) {
       this(cache, network, threadPoolSize,
               new ExecutorDelivery(new Handler(Looper.getMainLooper())));
   }

并且handler的消息处理都是在主线程哦。因为new Handler(Looper.getMainLooper()).

那就让我们一起来看看 ExecutorDelivery这个类吧。
首先PO一个构造函数。

    /** Used for posting responses, typically to the main thread. */
  private final Executor mResponsePoster;

  /**
   * Creates a new response delivery interface.
   * @param handler {@link Handler} to post responses on
   */
  public ExecutorDelivery(final Handler handler) {
      // Make an Executor that just wraps the handler.
      mResponsePoster = new Executor() {
          @Override
          public void execute(Runnable command) {
              handler.post(command);
          }
      };
  }

用了一个Executor异步执行框架用来封装handler。然后看看postResponsepostError方法。

   @Override
   public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
       request.markDelivered();
       request.addMarker("post-response");
       mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
   }

   @Override
   public void postError(Request<?> request, VolleyError error) {
       request.addMarker("post-error");
       Response<?> response = Response.error(error);
       mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
   }

可以看到最终都将Response和Reqeust传递到了一个ResponseDeliveryRunable里面然后执行。快马加鞭,我们看看ResponseDeliveryRunable的run方法。

  public void run() {
           // If this request has canceled, finish it and don't deliver.
           
           if (mRequest.isCanceled()) {
               mRequest.finish("canceled-at-delivery");
               return;
           }

           // Deliver a normal response or error, depending.
           if (mResponse.isSuccess()) {
               mRequest.deliverResponse(mResponse.result);
           } else {
               mRequest.deliverError(mResponse.error);
           }

           // If this is an intermediate response, add a marker, otherwise we're done
           // and the request can be finished.
           if (mResponse.intermediate) {
               mRequest.addMarker("intermediate-response");
           } else {
               mRequest.finish("done");
           }

           // If we have been provided a post-delivery runnable, run it.
           if (mRunnable != null) {
               mRunnable.run();
           }
      }

注意在它处理请求之前它又一次判断了请求是否被取消了,果然官方的代码有一些地方就是比较谨慎。记得在NetworkDispatcher的run方法的时候就已经判断过一次了,这样多次判断可以避免在请求返回之前用户取消了。我们应该也学着这么谨慎,从而避免一些不必要的处理。如果Request没有被取消掉那么就通过mResponse.isSuccess()判断一下请求是否成功,那么它如何判断请求是否成功的呢。

    /**
    * Returns whether this response is considered successful.
    */
   public boolean isSuccess() {
       return error == null;
   }

原来他只是简单的根据error是否为空来判断请求成功了没有,你可以跟踪一下代码,就知道当请求成功时候error是null的,失败的时候error是一个VolleyError对象buweinull。如果成功了就调用mRequest.deliverResponse(mResponse.result);,然后就成功的请求到数据啦。RequestdeliverResponse方法如下。

@Override
 protected void deliverResponse(String response) {
     mListener.onResponse(response);
 }   
 

至此我们就将请求结果回调到一开始new Request的listener里面啦。我们对StringRequest的请求过程的分析,也分析到一半了。为什么是一半呢。因为CacheDispatcherRequestQueue.add还没有分析呢,这也是很重要的知识点。 由于篇幅原因,我分两篇文章来写。下一篇,马上就会发布的,敬请期待!!
一口气写了这么多,累了。闪了。 有问题欢迎留言。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,271评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,725评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,252评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,634评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,549评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,985评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,471评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,128评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,257评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,233评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,235评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,940评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,528评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,623评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,858评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,245评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,790评论 2 339

推荐阅读更多精彩内容