[置顶] Android网络通信Volley框架源码浅析(一)

 

[置顶] Android网络通信Volley框架源码浅析(一)

标签: Volley开源项目android开发框架

尊重原创http://blog.csdn.net/yuanzeyao/article/details/25837897


       从今天开始,我打算为大家呈现关于Volley框架的源码分析的文章,Volley框架是Google在2013年发布的,主要用于实现频繁而且粒度比较细小的Http请求,在此之前Android中进行Http请求通常是使用HttpUrlConnection和HttpClient进行,但是使用起来非常麻烦,而且效率比较地下,我想谷歌正式基于此种原因发布了Volley框架,其实出了Volley框架意外,也有一些http请求开源项目,比如使用比较广泛的有async-http,UniversImageLoader等等,其中async-http主要用来实现异步http请求,而后者主要用来请求图片。Volley具有以上两种框架的功能,并且是Google公司发布,我想作为Android开发者,很有必要研究一下该框架。


1、下载Volley框架
git clone https://android.googlesource.com/platform/frameworks/volley

2、引用Volley框架
引用该框架的方式主要有两种:
(1):直接导入Volley框架,作为lib使用
(2):编译Volley成jar包

3、Volley的功能:
前面已经提及了Volley是一个用于http请求的框架,其主要功能如下:
json,xml,String,Image等资源的请求,当然我们还可以根据自己的需要来改写Volley框架源码,从而实现自己的功能


4、Volley源码分析

温馨提醒:如果是第一次看Volley源码,第一遍没看懂没关系,将源码copy下来,跟着我的思路慢慢分析,将文章从头到后多看几遍就ok了,因为Volley的一些关键类都互相应用,我只能一个一个的分析了,等你看完我所有的文章,然后再从头看一变,相信你定有所收获


当然阅读次文章之前,最好学会知道Volley框架的基本使用,由于网络上很多类似的教程,我在此处就不再描述了,后期如果有时间我也会讲解一下Volley的应用
我们就从Volley这个类开始吧

(1) Volley.java

[java]   view plain  copy  print ? 在CODE上查看代码片 派生到我的代码片
  1. public class Volley {  
  2.   
  3.     //缓存目录  
  4.     private static final String DEFAULT_CACHE_DIR = "volley";  
  5.     //创建一个默认的请求队列,我们的请求创建好后,放入该队列即可  
  6.     public static RequestQueue newRequestQueue(Context context, HttpStack stack) {  
  7.         //缓存目录  
  8.         File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);  
  9.   
  10.         String userAgent = "volley/0";  
  11.         try {  
  12.             String packageName = context.getPackageName();  
  13.             PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);  
  14.             userAgent = packageName + "/" + info.versionCode;  
  15.         } catch (NameNotFoundException e) {  
  16.         }  
  17.   
  18.         if (stack == null) {  
  19.               
  20.             /** 
  21.                 如果我们没有传入stack,那么自己创建一个,如果sdk>9(就是2.3以上),那么使用 
  22.                 HttpURLConnection实现http请求,如果2.3以前使用HttpClient实现,因为在2.3以前httpURLConnection不稳定 
  23.             */  
  24.             if (Build.VERSION.SDK_INT >= 9) {  
  25.                 stack = new HurlStack();  
  26.             } else {  
  27.                 // Prior to Gingerbread, HttpUrlConnection was unreliable.  
  28.                 // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html  
  29.                 stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));  
  30.             }  
  31.         }  
  32.         //Network类是一个网络请求类,使用stack进行网络请求  
  33.         Network network = new BasicNetwork(stack);  
  34.         //真正创建一个请求队列,传入一个磁盘缓存和网络请求类  
  35.         RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);  
  36.         //启动请求队列,其实里%9��就是启动了一些线程,不断监听是否有请求  
  37.         queue.start();  
  38.   
  39.         return queue;  
  40.     }  
  41.   
  42.     /** 
  43.      * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it. 
  44.      * 
  45.      * @param context A {@link Context} to use for creating the cache dir. 
  46.      * @return A started {@link RequestQueue} instance. 
  47.      */  
  48.     public static RequestQueue newRequestQueue(Context context) {  
  49.         return newRequestQueue(context, null);  
  50.     }  
  51. }  


Volley类主要用来创建一个请求队列,我们的任何请求(请求字符串,json,xml)都放入到这个队列中(其实里面有两个队列,后面我们慢慢学习,暂时简单理解为一个)。创建完队列后,调用start方法,就会启动一些线程(暂时不关注多少条线程),不断监听队里里面是否有请求,如果有请求则执行http请求,在2.3之前的版本中,Http请求是通过httpClient实现,在2.3以后的版本中是通过HttpURLConnection实现,因为在2.3之前的版本中HttpRlConnection非常不稳定


(2) HttpStack.java
下面看看HttpStack是何方神圣

[java]   view plain  copy  print ? 在CODE上查看代码片 派生到我的代码片
  1. public interface HttpStack {  
  2.     /** 
  3.      名字挺吓人的,呵呵,其实就是一个接口,它有两个实现,分别是HurlStack,HttpClientStack,通过名字大家 
  4.      可以猜出来一个基于HttpClient,一个基于HttpURLConnection 
  5.      * @return the HTTP response 
  6.      */  
  7.     public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)  
  8.         throws IOException, AuthFailureError;  
  9.   
  10. }  

直接查看它的子类方法吧
首先看  HurlStack.java类
这个类是基于HttpURLConnection实现的
由于这个类比较长,我就重点讲解一下


(3) HurlStack.java

[java]   view plain  copy  print ? 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.     继承自HttpStack,我们暂时就把Request抽象成一个请求,包括url,method等信息,后期我们会重点分析这个类 
  3.     第二个参数就是一些请求头信息 
  4.     */  
  5.     @Override  
  6.     public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)  
  7.             throws IOException, AuthFailureError {  
  8.         String url = request.getUrl();  
  9.         HashMap<String, String> map = new HashMap<String, String>();  
  10.         map.putAll(request.getHeaders());  
  11.         map.putAll(additionalHeaders);  
  12.         //此处一般为空,我们直接忽略掉  
  13.         if (mUrlRewriter != null) {  
  14.             String rewritten = mUrlRewriter.rewriteUrl(url);  
  15.             if (rewritten == null) {  
  16.                 throw new IOException("URL blocked by rewriter: " + url);  
  17.             }  
  18.             url = rewritten;  
  19.         }  
  20.         URL parsedUrl = new URL(url);  
  21.         HttpURLConnection connection = openConnection(parsedUrl, request);  
  22.         //添加请求头  
  23.         for (String headerName : map.keySet()) {  
  24.             connection.addRequestProperty(headerName, map.get(headerName));  
  25.         }  
  26.         //这个方法名字很长,其实功能很简单,就是为connection设置请求方法 如get post等等  
  27.         setConnectionParametersForRequest(connection, request);  
  28.         // Initialize HttpResponse with data from the HttpURLConnection.  
  29.         ProtocolVersion protocolVersion = new ProtocolVersion("HTTP"11);  
  30.         int responseCode = connection.getResponseCode();  
  31.         if (responseCode == -1) {  
  32.             // -1 is returned by getResponseCode() if the response code could not be retrieved.  
  33.             // Signal to the caller that something was wrong with the connection.  
  34.             throw new IOException("Could not retrieve response code from HttpUrlConnection.");  
  35.         }  
  36.         StatusLine responseStatus = new BasicStatusLine(protocolVersion,  
  37.                 connection.getResponseCode(), connection.getResponseMessage());  
  38.         BasicHttpResponse response = new BasicHttpResponse(responseStatus);  
  39.         response.setEntity(entityFromConnection(connection));  
  40.         for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {  
  41.             if (header.getKey() != null) {  
  42.                 Header h = new BasicHeader(header.getKey(), header.getValue().get(0));  
  43.                 response.addHeader(h);  
  44.             }  
  45.         }  
  46.         //http的返回结果  
  47.         return response;  
  48.     }  

(4) HttpClientStack.java

[java]   view plain  copy  print ? 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.     相对比上一个方法简单,相信使用过httpClient的同学一看就明白 
  3.     */  
  4.     @Override  
  5.     public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)  
  6.             throws IOException, AuthFailureError {  
  7.             //这个方法见名知意,就是创建一个HttpGet或者HttpPost  
  8.         HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);  
  9.         //添加头信息  
  10.         addHeaders(httpRequest, additionalHeaders);  
  11.         addHeaders(httpRequest, request.getHeaders());  
  12.         //在请求之前进行准备工作,其实是个空方法,很想AsyncTask的onPreExecute  
  13.         onPrepareRequest(httpRequest);  
  14.         HttpParams httpParams = httpRequest.getParams();  
  15.         int timeoutMs = request.getTimeoutMs();  
  16.         // TODO: Reevaluate this connection timeout based on more wide-scale  
  17.         // data collection and possibly different for wifi vs. 3G.  
  18.         HttpConnectionParams.setConnectionTimeout(httpParams, 5000);  
  19.         HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);  
  20.         //执行请求并返回结果  
  21.         return mClient.execute(httpRequest);  
  22.     }  

看到这里大家肯定觉得这个框架也没有什么了不起嘛,和使用HttpURLConnection和HttpClient差不多嘛,如果你真的这样觉得那么你就大错特错了,其实这个框架的核心在于线程的调度和缓存上面,后期我们会介绍的


回到Volley类,我们看下一个陌生的类就是Network,其实Network不过是个接口而已,它的实现类是BaskNetwork


(5) BaskicNetwork.java

从名字我们就可以看出来,这个类就是进行网络请求的,其实他就是对HttpurlStack或者HttpClientStack的一个封装,真正实现请求的还是上面两个类。
最核心的方法:

[java]   view plain  copy  print ? 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.     public NetworkResponse performRequest(Request<?> request) throws VolleyError {  
  3.         long requestStart = SystemClock.elapsedRealtime();  
  4.         while (true) {  
  5.             HttpResponse httpResponse = null;  
  6.             byte[] responseContents = null;  
  7.             Map<String, String> responseHeaders = new HashMap<String, String>();  
  8.             try {  
  9.                 // Gather headers.  
  10.                 Map<String, String> headers = new HashMap<String, String>();  
  11.                 addCacheHeaders(headers, request.getCacheEntry());  
  12.                 //调用mHttpStack执行http请求  
  13.                 httpResponse = mHttpStack.performRequest(request, headers);  
  14.                 StatusLine statusLine = httpResponse.getStatusLine();  
  15.                 int statusCode = statusLine.getStatusCode();  
  16.   
  17.                 responseHeaders = convertHeaders(httpResponse.getAllHeaders());  
  18.                 // Handle cache validation.  
  19.                 if (statusCode == HttpStatus.SC_NOT_MODIFIED) {  
  20.                     //将返回结果封装成一个NetworkResponse  
  21.                     return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,  
  22.                             request.getCacheEntry() == null ? null : request.getCacheEntry().data,  
  23.                             responseHeaders, true);  
  24.                 }  
  25.   
  26.                 // Some responses such as 204s do not have content.  We must check.  
  27.                 if (httpResponse.getEntity() != null) {  
  28.                   responseContents = entityToBytes(httpResponse.getEntity());  
  29.                 } else {  
  30.                   // Add 0 byte response as a way of honestly representing a  
  31.                   // no-content request.  
  32.                   responseContents = new byte[0];  
  33.                 }  
  34.   
  35.                 // if the request is slow, log it.  
  36.                 long requestLifetime = SystemClock.elapsedRealtime() - requestStart;  
  37.                 logSlowRequests(requestLifetime, request, responseContents, statusLine);  
  38.   
  39.                 if (statusCode < 200 || statusCode > 299) {  
  40.                     throw new IOException();  
  41.                 }  
  42.                 return new NetworkResponse(statusCode, responseContents, responseHeaders, false);  
  43.             } catch (SocketTimeoutException e) {  
  44.                 //超时重新请求  
  45.                 attemptRetryOnException("socket", request, new TimeoutError());  
  46.             } catch (ConnectTimeoutException e) {  
  47.                 //超时重新请求  
  48.                 attemptRetryOnException("connection", request, new TimeoutError());  
  49.             } catch (MalformedURLException e) {  
  50.                 throw new RuntimeException("Bad URL " + request.getUrl(), e);  
  51.             } catch (IOException e) {  
  52.                 int statusCode = 0;  
  53.                 NetworkResponse networkResponse = null;  
  54.                 if (httpResponse != null) {  
  55.                     statusCode = httpResponse.getStatusLine().getStatusCode();  
  56.                 } else {  
  57.                     throw new NoConnectionError(e);  
  58.                 }  
  59.                 VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());  
  60.                 if (responseContents != null) {  
  61.                     networkResponse = new NetworkResponse(statusCode, responseContents,  
  62.                             responseHeaders, false);  
  63.                     if (statusCode == HttpStatus.SC_UNAUTHORIZED ||  
  64.                             statusCode == HttpStatus.SC_FORBIDDEN) {  
  65.                         attemptRetryOnException("auth",  
  66.                                 request, new AuthFailureError(networkResponse));  
  67.                     } else {  
  68.                         // TODO: Only throw ServerError for 5xx status codes.  
  69.                         throw new ServerError(networkResponse);  
  70.                     }  
  71.                 } else {  
  72.                     throw new NetworkError(networkResponse);  
  73.                 }  
  74.             }  
  75.         }  
  76.     }  

这个方法调用了mHttpStack的同名方法,只不过在mHttpStack中返回的是HttpResponse,在这里返回的是NetworkResponse。



然后再看看本篇文章的最后一个类:
RequestQueue.java
我保留了一些关键字段,删除不影响理解的字段


[java]   view plain  copy  print ? 在CODE上查看代码片 派生到我的代码片
  1. public class RequestQueue {  
  2.   
  3.     ...  
  4.   
  5.     //本地缓存队列,如果一个请求能够缓存,那么先放到这个队列中,如果本地缓存没有命中,则加入网络队列,见后面  
  6.     private final PriorityBlockingQueue<Request<?>> mCacheQueue =  
  7.         new PriorityBlockingQueue<Request<?>>();  
  8.   
  9.     //网络请求队列  
  10.     private final PriorityBlockingQueue<Request<?>> mNetworkQueue =  
  11.         new PriorityBlockingQueue<Request<?>>();  
  12.   
  13.     //默认的网络请求线程个数 默认四个,这个我们可以改动  
  14.     private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;  
  15.   
  16.     //本地缓存的接口  
  17.     private final Cache mCache;  
  18.   
  19.     //这个类相信大家并不陌生  
  20.     private final Network mNetwork;  
  21.   
  22.     //由于网络请求在子线程中执行,这个对象将请求结果发送到ui线程,功能很像Handler  
  23.     private final ResponseDelivery mDelivery;  
  24.   
  25.     //网络线程数组  
  26.     private NetworkDispatcher[] mDispatchers;  
  27.   
  28.     //本地线程数组 只有一条  
  29.     private CacheDispatcher mCacheDispatcher;  
  30.   
  31.     /** 
  32.      创建一个请求队列 
  33.      参数1:本地缓存 
  34.      参数2: network 进行网络进行的包装类 
  35.      参数3:网络请求线程池大小 
  36.      参数4:就是一个将子线程的数据发送到ui线程的功能类,先可以不用关心 
  37.     */  
  38.     public RequestQueue(Cache cache, Network network, int threadPoolSize,  
  39.             ResponseDelivery delivery) {  
  40.         mCache = cache;  
  41.         mNetwork = network;  
  42.         mDispatchers = new NetworkDispatcher[threadPoolSize];  
  43.         mDelivery = delivery;  
  44.     }  
  45.   
  46.      
  47.     public RequestQueue(Cache cache, Network network, int threadPoolSize) {  
  48.         this(cache, network, threadPoolSize,  
  49.                 new ExecutorDelivery(new Handler(Looper.getMainLooper())));  
  50.     }  
  51.   
  52.     /** 
  53.      * Creates the worker pool. Processing will not begin until {@link #start()} is called. 
  54.      * 
  55.      * @param cache A Cache to use for persisting responses to disk 
  56.      * @param network A Network interface for performing HTTP requests 
  57.      */  
  58.     public RequestQueue(Cache cache, Network network) {  
  59.         this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);  
  60.     }  
  61.   
  62.     //启动本地和网络线程  
  63.     public void start() {  
  64.         stop();  // Make sure any currently running dispatchers are stopped.  
  65.         // Create the cache dispatcher and start it.  
  66.         mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);  
  67.         mCacheDispatcher.start();  
  68.   
  69.         // Create network dispatchers (and corresponding threads) up to the pool size.  
  70.         for (int i = 0; i < mDispatchers.length; i++) {  
  71.             NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,  
  72.                     mCache, mDelivery);  
  73.             mDispatchers[i] = networkDispatcher;  
  74.             networkDispatcher.start();  
  75.         }  
  76.     }  
  77.   
  78.     //关闭本地和网络线程  
  79.     public void stop() {  
  80.         if (mCacheDispatcher != null) {  
  81.             mCacheDispatcher.quit();  
  82.         }  
  83.         for (int i = 0; i < mDispatchers.length; i++) {  
  84.             if (mDispatchers[i] != null) {  
  85.                 mDispatchers[i].quit();  
  86.             }  
  87.         }  
  88.     }  
  89.   
  90.   
  91.   
  92.   
  93.     //相当于一个过滤器,对于apply方法返回true的Request可以从Queue中删除  
  94.     public interface RequestFilter {  
  95.         public boolean apply(Request<?> request);  
  96.     }  
  97.   
  98.     //借助上面的方法实现对没有执行的Request进行删除  
  99.     public void cancelAll(RequestFilter filter) {  
  100.         synchronized (mCurrentRequests) {  
  101.             for (Request<?> request : mCurrentRequests) {  
  102.                 if (filter.apply(request)) {  
  103.                     request.cancel();  
  104.                 }  
  105.             }  
  106.         }  
  107.     }  
  108.   
  109.     //取消所有的请求  
  110.     public void cancelAll(final Object tag) {  
  111.         if (tag == null) {  
  112.             throw new IllegalArgumentException("Cannot cancelAll with a null tag");  
  113.         }  
  114.         cancelAll(new RequestFilter() {  
  115.             @Override  
  116.             public boolean apply(Request<?> request) {  
  117.                 return request.getTag() == tag;  
  118.             }  
  119.         });  
  120.     }  
  121.   
  122.     /** 
  123.         将Request放入两个队列中的一个 
  124.     */  
  125.     public <T> Request<T> add(Request<T> request) {  
  126.         // Tag the request as belonging to this queue and add it to the set of current requests.  
  127.         request.setRequestQueue(this);  
  128.         synchronized (mCurrentRequests) {  
  129.             mCurrentRequests.add(request);  
  130.         }  
  131.   
  132.         // Process requests in the order they are added.  
  133.         request.setSequence(getSequenceNumber());  
  134.         request.addMarker("add-to-queue");  
  135.   
  136.         // 如果该Request不能缓存,那么直接放入网络队列  
  137.         if (!request.shouldCache()) {  
  138.             mNetworkQueue.add(request);  
  139.             return request;  
  140.         }  
  141.   
  142.         // 把Request放入具有相同CacheKey的链表中,如果没有相同的CacheKey的Request请求存在,则放入本地队列  
  143.         synchronized (mWaitingRequests) {  
  144.             String cacheKey = request.getCacheKey();  
  145.             if (mWaitingRequests.containsKey(cacheKey)) {  
  146.                 // There is already a request in flight. Queue up.  
  147.                 Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);  
  148.                 if (stagedRequests == null) {  
  149.                     stagedRequests = new LinkedList<Request<?>>();  
  150.                 }  
  151.                 stagedRequests.add(request);  
  152.                 mWaitingRequests.put(cacheKey, stagedRequests);  
  153.                 if (VolleyLog.DEBUG) {  
  154.                     VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);  
  155.                 }  
  156.             } else {  
  157.                 // Insert 'null' queue for this cacheKey, indicating there is now a request in  
  158.                 // flight.  
  159.                 mWaitingRequests.put(cacheKey, null);  
  160.                 mCacheQueue.add(request);  
  161.             }  
  162.             return request;  
  163.         }  
  164.     }  
  165.   
  166.     /** 
  167.      * 结束一个Request 
  168.      */  
  169.     void finish(Request<?> request) {  
  170.         // Remove from the set of requests currently being processed.  
  171.         synchronized (mCurrentRequests) {  
  172.             mCurrentRequests.remove(request);  
  173.         }  
  174.   
  175.         if (request.shouldCache()) {  
  176.             synchronized (mWaitingRequests) {  
  177.                 String cacheKey = request.getCacheKey();  
  178.                 Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);  
  179.                 if (waitingRequests != null) {  
  180.                     if (VolleyLog.DEBUG) {  
  181.                         VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",  
  182.                                 waitingRequests.size(), cacheKey);  
  183.                     }  
  184.                     // Process all queued up requests. They won't be considered as in flight, but  
  185.                     // that's not a problem as the cache has been primed by 'request'.  
  186.                     mCacheQueue.addAll(waitingRequests);  
  187.                 }  
  188.             }  
  189.         }  
  190.     }  
  191. }  

写到这里先高一段落吧,来个小小的总结:Volley中有一个RequestQueue(包含本地队列和网络队列),就是请求队列,每一个http请求都被封装成了一个Request,通过队列的add方法加入队列,如果一个Request可以缓存,那么先加入本地队列,如果不能缓存则加入网络队列

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值