Volley库源码分析(下)

网络请求部分分析

关于volley的网络请求部分可以看博客: http://www.cnblogs.com/bvin/p/3291611.html



网络请求中有几个转换解析请求获取响应结果的地方:

1.HttpStack接口的performRequest()方法

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)  

2.Network接口的performRequest()方法

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public NetworkResponse performRequest(Request<?> request)  

3.Request类的parseNetworkResponse()抽象方法

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. abstract protected Response<T> parseNetworkResponse(NetworkResponse response);  

可以很鲜明得看出第一个是对原生Http请求的解析,解析出来是一个Apach的HttpResponse 实例,这个结果就是通过上述两个HttpStack的实现类HttpClientStack和HurlStack执行获取的获取的。

而第二个解析出来是框架自定的NetworkResponse,这是通过Network的实现类BasicNetwork来获取的。

第三个就是第二个得出来NetworkResponse解析成用户期望Response<T> 了,这个Response和Request是对应的,有String型,json型还有Image型的。然后在通过ResponseDelivery把解析好的结果发送到主线程。

从Request到Response就是这样一步步走来的。


 再详细看下上面第二个方法performRequest(),这个方法是NetworkDispatcher中主要步骤的第二步,其中真正网络请求的工作委托给mHttpStack实现,通过HttpStack实现类就是上图的两个HttpClientStack和HurlStack执行。这两个类的区别在于HttpClientStack是直接用HttpClient的execute()方法执行一个Http请求,而HurlStack就是直接用URL.openConnection()进行连接,这种方式在2.3以上是不能用的,所以分开了这两种方式。
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.      * @title performRequest执行各种Request请求并以NetworkResponse的形式返回结果 
  3.      * @param Request 
  4.      * @return NetworkResponse 
  5.      * @throws VolleyError 
  6.      * 定义:{@link Network#performRequest(Request)} 
  7.      * 被调:{@link NetworkDispatcher#run()} 
  8.      *  
  9.      */  
  10.     @Override//NetworkDispatcher的run()方法中调用  
  11.     public NetworkResponse performRequest(Request<?> request) throws VolleyError {  
  12.         long requestStart = SystemClock.elapsedRealtime();//开始请求时间  
  13.         while (true) {  
  14.             HttpResponse httpResponse = null;//apache的请求结果  
  15.             byte[] responseContents = null;//请求的内容  
  16.             Map<String, String> responseHeaders = new HashMap<String, String>();//响应结果头部信息  
  17.             try {  
  18.                 // Gather headers.  
  19.                 Map<String, String> headers = new HashMap<String, String>();//保存缓存数据  
  20.                 addCacheHeaders(headers, request.getCacheEntry());//先获取缓存数据  
  21.                 httpResponse = mHttpStack.performRequest(request, headers);//去调用mHttpStack的实现方法执行请求  
  22.                 StatusLine statusLine = httpResponse.getStatusLine();//获取http状态线  
  23.                 int statusCode = statusLine.getStatusCode();//获取状态码  
  24.   
  25.                 responseHeaders = convertHeaders(httpResponse.getAllHeaders());  
  26.                 // Handle cache validation.//处理缓存验证  
  27.                 if (statusCode == HttpStatus.SC_NOT_MODIFIED) {//返回缓存数据  
  28.                     return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,  
  29.                             request.getCacheEntry().data, responseHeaders, true);  
  30.                 }  
  31.   
  32.                 //把HttpEntity转化为byte[]数据  
  33.                 responseContents = entityToBytes(httpResponse.getEntity());  
  34.                 // if the request is slow, log it.//如果请求很慢,就打印出来看一下  
  35.                 long requestLifetime = SystemClock.elapsedRealtime() - requestStart;  
  36.                 logSlowRequests(requestLifetime, request, responseContents, statusLine);//打印  
  37.   
  38.                 //连接正常但是返回无内容,抛出IO异常  
  39.                 if (statusCode != HttpStatus.SC_OK && statusCode != HttpStatus.SC_NO_CONTENT) {  
  40.                     throw new IOException();  
  41.                 }  
  42.                 return new NetworkResponse(statusCode, responseContents, responseHeaders, false);  
  43.             } catch (SocketTimeoutException e) {//读取超时,重试  
  44.                 attemptRetryOnException("socket", request, new TimeoutError());  
  45.             } catch (ConnectTimeoutException e) {//连接超时,重试  
  46.                 attemptRetryOnException("connection", request, new TimeoutError());  
  47.             } catch (MalformedURLException e) {//Bad URL  
  48.                 throw new RuntimeException("Bad URL " + request.getUrl(), e);  
  49.             } catch (IOException e) {//IO异常  
  50.                 int statusCode = 0;  
  51.                 NetworkResponse networkResponse = null;  
  52.                 if (httpResponse != null) {  
  53.                     statusCode = httpResponse.getStatusLine().getStatusCode();  
  54.                 } else {//如果没有返回httpResponse,就说明没连接  
  55.                     throw new NoConnectionError(e);  
  56.                 }  
  57.                 VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());  
  58.                 if (responseContents != null) {//返回数据不为空  
  59.                     networkResponse = new NetworkResponse(statusCode, responseContents,  
  60.                             responseHeaders, false);//创建响应体  
  61.                     if (statusCode == HttpStatus.SC_UNAUTHORIZED ||  
  62.                             statusCode == HttpStatus.SC_FORBIDDEN) {//认证失败异常,重试  
  63.                         attemptRetryOnException("auth",  
  64.                                 request, new AuthFailureError(networkResponse));  
  65.                     } else {//服务器异常  
  66.                         // TODO: Only throw ServerError for 5xx status codes.  
  67.                         throw new ServerError(networkResponse);//只有状态码为5XX才抛出服务器异常  
  68.                     }  
  69.                 } else {//网络异常  
  70.                     throw new NetworkError(networkResponse);  
  71.                 }  
  72.             }  
  73.         }  
  74.     }  

A:先是通过mHttpStack把请求执行并且获取它的响应结果,根据HttpStatus做出各种判断。

B:然后再把httpResponse的Entity转化为ByteArray,并处理各种发生的异常。

C:最后的过程是这样的:通过Volley创建一个RequestQueue请求队列,当这个队列开始运作的时候会启动NetworkDispatcher这个工作线程,而BasicNetwork的performRequest()的方法就在NetworkDispatcher线程run()方法中调用,然后通过mHttpStack的performRequest()方法获取一个networkResponse,在NetworkDispatcher线程把这个networkResponse转化为期望的数据类型,比如Response<String>,Response<Json>,Response<Bitmap>。

图片下载部分分析

这部分主要集中在将网络请求转化为图片,以及在未有本地图片时显示空图等。
开发者主要和NetworkImageView打交道,NetworkImageView继承ImageView,可以直接在xml布局中使用NetworkImageView,但是该类具有自动下载图片、缓存图片和在无本地图片时显示空图的功能。
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class NetworkImageView extends ImageView {  
  2.     /** The URL of the network image to load */  
  3.     private String mUrl;  
  4.   
  5.     /** 
  6.      * Resource ID of the image to be used as a placeholder until the network image is loaded. 
  7.      */  
  8.     private int mDefaultImageId;//无本地图片时显示的空图  
  9.   
  10.     /** 
  11.      * Resource ID of the image to be used if the network response fails. 
  12.      */  
  13.     private int mErrorImageId;  
  14.   
  15.     /** Local copy of the ImageLoader. */  
  16.     private ImageLoader mImageLoader;  
  17.   
  18.     /** Current ImageContainer. (either in-flight or finished) */  
  19.     private ImageContainer mImageContainer;  
  20.     ......  
  21.     /** 
  22.      * Loads the image for the view if it isn't already loaded. 
  23.      * @param isInLayoutPass True if this was invoked from a layout pass, false otherwise. 
  24.      */  
  25.     private void loadImageIfNecessary(final boolean isInLayoutPass) {  
  26.         int width = getWidth();  
  27.         int height = getHeight();  
  28.   
  29.         ......  
  30.   
  31.         // The pre-existing content of this view didn't match the current URL. Load the new image  
  32.         // from the network.  
  33.         ImageContainer newContainer = mImageLoader.get(mUrl,  
  34.                 new ImageListener() {  
  35.                     @Override  
  36.                     public void onErrorResponse(VolleyError error) {  
  37.                         if (mErrorImageId != 0) {  
  38.                             setImageResource(mErrorImageId);  
  39.                         }  
  40.                     }  
  41.   
  42.                     @Override  
  43.                     public void onResponse(final ImageContainer response, boolean isImmediate) {  
  44.                         // If this was an immediate response that was delivered inside of a layout  
  45.                         // pass do not set the image immediately as it will trigger a requestLayout  
  46.                         // inside of a layout. Instead, defer setting the image by posting back to  
  47.                         // the main thread.  
  48.                         if (isImmediate && isInLayoutPass) {  
  49.                             post(new Runnable() {  
  50.                                 @Override  
  51.                                 public void run() {  
  52.                                     onResponse(response, false);  
  53.                                 }  
  54.                             });  
  55.                             return;  
  56.                         }  
  57.   
  58.                         if (response.getBitmap() != null) {  
  59.                             setImageBitmap(response.getBitmap());  
  60.                         } else if (mDefaultImageId != 0) {  
  61.                             setImageResource(mDefaultImageId);  
  62.                         }  
  63.                     }  
  64.                 });  
  65.   
  66.         // update the ImageContainer to be the new bitmap container.  
  67.         mImageContainer = newContainer;  
  68.     }  
  69.       
  70.     ......  
  71. }  

所以关键在于mImageLoader.get()方法。图片下载中最关键的类是ImageLoader,负责帮助从远端URL下载图片和缓存图片。注意,这个类的实例必须从主线程创建,所有的responses也必须分发到主线程。
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class ImageLoader {  
  2.     /** RequestQueue for dispatching ImageRequests onto. */  
  3.     private final RequestQueue mRequestQueue;  
  4.   
  5.     /** Amount of time to wait after first response arrives before delivering all responses. */  
  6.     private int mBatchResponseDelayMs = 100;  
  7.   
  8.     /** The cache implementation to be used as an L1 cache before calling into volley. */  
  9.     private final ImageCache mCache;  
  10.   
  11.     /** 
  12.      * HashMap of Cache keys -> BatchedImageRequest used to track in-flight requests so 
  13.      * that we can coalesce multiple requests to the same URL into a single network request. 
  14.      */  
  15.     private final HashMap<String, BatchedImageRequest> mInFlightRequests =  
  16.             new HashMap<String, BatchedImageRequest>();  
  17.   
  18.     /** HashMap of the currently pending responses (waiting to be delivered). */  
  19.     //BatchedImageRequest用于匹配一个Request和一组对该Request的返回结果感兴趣的ImageContainer,这样可以把相同的Request合并  
  20.     private final HashMap<String, BatchedImageRequest> mBatchedResponses =  
  21.             new HashMap<String, BatchedImageRequest>();  
  22.   
  23.     /** Handler to the main thread. */  
  24.     private final Handler mHandler = new Handler(Looper.getMainLooper());  
  25.   
  26.     /** Runnable for in-flight response delivery. */  
  27.     private Runnable mRunnable;  
  28.       
  29.     ......  
  30.       
  31.     /** 
  32.      * Issues a bitmap request with the given URL if that image is not available 
  33.      * in the cache, and returns a bitmap container that contains all of the data 
  34.      * relating to the request (as well as the default image if the requested 
  35.      * image is not available). 
  36.      * @param requestUrl The url of the remote image 
  37.      * @param imageListener The listener to call when the remote image is loaded 
  38.      * @param maxWidth The maximum width of the returned image. 
  39.      * @param maxHeight The maximum height of the returned image. 
  40.      * @return A container object that contains all of the properties of the request, as well as 
  41.      *     the currently available image (default if remote is not loaded). 
  42.      */  
  43.     public ImageContainer get(String requestUrl, ImageListener imageListener,  
  44.             int maxWidth, int maxHeight) {  
  45.         // only fulfill requests that were initiated from the main thread.  
  46.         throwIfNotOnMainThread();//必须在主线程创建requests  
  47.   
  48.         final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);  
  49.   
  50.         // Try to look up the request in the cache of remote images.  
  51.         Bitmap cachedBitmap = mCache.getBitmap(cacheKey);//先查找缓存是否命中  
  52.         if (cachedBitmap != null) {//缓存命中  
  53.             // Return the cached bitmap.  
  54.             ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, nullnull);  
  55.             imageListener.onResponse(container, true);  
  56.             return container;  
  57.         }  
  58.   
  59.         // The bitmap did not exist in the cache, fetch it!  
  60.         ImageContainer imageContainer =  
  61.                 new ImageContainer(null, requestUrl, cacheKey, imageListener);//ImageContainer是一个数据类  
  62.   
  63.         // Update the caller to let them know that they should use the default bitmap.  
  64.         imageListener.onResponse(imageContainer, true);  
  65.   
  66.         // Check to see if a request is already in-flight.  
  67.         BatchedImageRequest request = mInFlightRequests.get(cacheKey);  
  68.         if (request != null) {  
  69.             // If it is, add this request to the list of listeners.  
  70.             request.addContainer(imageContainer);  
  71.             return imageContainer;  
  72.         }  
  73.   
  74.         // The request is not already in flight. Send the new request to the network and  
  75.         // track it.  
  76.         Request<?> newRequest =  
  77.             new ImageRequest(requestUrl, new Listener<Bitmap>() {  
  78.                 @Override  
  79.                 public void onResponse(Bitmap response) {  
  80.                     onGetImageSuccess(cacheKey, response);  
  81.                 }  
  82.             }, maxWidth, maxHeight,  
  83.             Config.RGB_565, new ErrorListener() {  
  84.                 @Override  
  85.                 public void onErrorResponse(VolleyError error) {  
  86.                     onGetImageError(cacheKey, error);  
  87.                 }  
  88.             });//ImageRequest具有将原始网络请求编码成Bitmap的功能  
  89.   
  90.         mRequestQueue.add(newRequest);//向mRequestQueue增加ImageRequest,注意各个线程都只和同步队列打交道,各个线程因此解耦  
  91.         mInFlightRequests.put(cacheKey,  
  92.                 new BatchedImageRequest(newRequest, imageContainer));  
  93.         return imageContainer;  
  94.     }  
  95.       
  96.     ......  
  97. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
项目:使用 JavaScript 编写的杀死幽灵游戏(附源代码) 杀死鬼魂游戏是使用 Vanilla JavaScript、CSS 和 HTML 画布开发的简单项目。这款游戏很有趣。玩家必须触摸/杀死游荡的鬼魂才能得分。您必须将鼠标悬停在鬼魂上 - 尽量得分。鬼魂在眨眼间不断从一个地方移动到另一个地方。您必须在 1 分钟内尽可能多地杀死鬼魂。 游戏制作 这个游戏项目只是用 HTML 画布、CSS 和 JavaScript 编写的。说到这个游戏的特点,用户必须触摸/杀死游荡的幽灵才能得分。游戏会根据你杀死的幽灵数量来记录你的总分。你必须将鼠标悬停在幽灵上——尽量得分。你必须在 1 分钟内尽可能多地杀死幽灵。游戏还会显示最高排名分数,如果你成功击败它,该分数会在游戏结束屏幕上更新。 该游戏包含大量的 javascript 以确保游戏正常运行。 如何运行该项目? 要运行此游戏,您不需要任何类型的本地服务器,但需要浏览器。我们建议您使用现代浏览器,如 Google Chrome 和 Mozilla Firefox。要玩游戏,首先,单击 index.html 文件在浏览器中打开游戏。 演示: 该项目为国外大神项目,可以作为毕业设计的项目,也可以作为大作业项目,不用担心代码重复,设计重复等,如果需要对项目进行修改,需要具备一定基础知识。 注意:如果装有360等杀毒软件,可能会出现误报的情况,源码本身并无病毒,使用源码时可以关闭360,或者添加信任。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值