首页 > 代码库 > Volley框架源码浅析(一)
Volley框架源码浅析(一)
尊重原创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
public class Volley { //缓存目录 private static final String DEFAULT_CACHE_DIR = "volley"; //创建一个默认的请求队列,我们的请求创建好后,放入该队列即可 public static RequestQueue newRequestQueue(Context context, HttpStack stack) { //缓存目录 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,那么自己创建一个,如果sdk>9(就是2.3以上),那么使用 HttpURLConnection实现http请求,如果2.3以前使用HttpClient实现,因为在2.3以前httpURLConnection不稳定 */ if (Build.VERSION.SDK_INT >= 9) { stack = new HurlStack(); } else { // Prior to Gingerbread, HttpUrlConnection was unreliable. // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); } } //Network类是一个网络请求类,使用stack进行网络请求 Network network = new BasicNetwork(stack); //真正创建一个请求队列,传入一个磁盘缓存和网络请求类 RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); //启动请求队列,其实里面就是启动了一些线程,不断监听是否有请求 queue.start(); return queue; } /** * 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. * @return A started {@link RequestQueue} instance. */ public static RequestQueue newRequestQueue(Context context) { return newRequestQueue(context, null); } }
(2) HttpStack.java
下面看看HttpStack是何方神圣
public interface HttpStack { /** 名字挺吓人的,呵呵,其实就是一个接口,它有两个实现,分别是HurlStack,HttpClientStack,通过名字大家 可以猜出来一个基于HttpClient,一个基于HttpURLConnection * @return the HTTP response */ public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError; }
直接查看它的子类方法吧
首先看 HurlStack.java类
这个类是基于HttpURLConnection实现的
由于这个类比较长,我就重点讲解一下
(3) HurlStack.java
/** 继承自HttpStack,我们暂时就把Request抽象成一个请求,包括url,method等信息,后期我们会重点分析这个类 第二个参数就是一些请求头信息 */ @Override public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { String url = request.getUrl(); HashMap<String, String> map = new HashMap<String, String>(); map.putAll(request.getHeaders()); map.putAll(additionalHeaders); //此处一般为空,我们直接忽略掉 if (mUrlRewriter != null) { String rewritten = mUrlRewriter.rewriteUrl(url); if (rewritten == null) { throw new IOException("URL blocked by rewriter: " + url); } url = rewritten; } URL parsedUrl = new URL(url); HttpURLConnection connection = openConnection(parsedUrl, request); //添加请求头 for (String headerName : map.keySet()) { connection.addRequestProperty(headerName, map.get(headerName)); } //这个方法名字很长,其实功能很简单,就是为connection设置请求方法 如get post等等 setConnectionParametersForRequest(connection, request); // Initialize HttpResponse with data from the HttpURLConnection. ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1); int responseCode = connection.getResponseCode(); if (responseCode == -1) { // -1 is returned by getResponseCode() if the response code could not be retrieved. // Signal to the caller that something was wrong with the connection. throw new IOException("Could not retrieve response code from HttpUrlConnection."); } StatusLine responseStatus = new BasicStatusLine(protocolVersion, connection.getResponseCode(), connection.getResponseMessage()); BasicHttpResponse response = new BasicHttpResponse(responseStatus); response.setEntity(entityFromConnection(connection)); for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) { if (header.getKey() != null) { Header h = new BasicHeader(header.getKey(), header.getValue().get(0)); response.addHeader(h); } } //http的返回结果 return response; }
(4) HttpClientStack.java
/** 相对比上一个方法简单,相信使用过httpClient的同学一看就明白 */ @Override public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { //这个方法见名知意,就是创建一个HttpGet或者HttpPost HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders); //添加头信息 addHeaders(httpRequest, additionalHeaders); addHeaders(httpRequest, request.getHeaders()); //在请求之前进行准备工作,其实是个空方法,很想AsyncTask的onPreExecute onPrepareRequest(httpRequest); HttpParams httpParams = httpRequest.getParams(); int timeoutMs = request.getTimeoutMs(); // TODO: Reevaluate this connection timeout based on more wide-scale // data collection and possibly different for wifi vs. 3G. HttpConnectionParams.setConnectionTimeout(httpParams, 5000); HttpConnectionParams.setSoTimeout(httpParams, timeoutMs); //执行请求并返回结果 return mClient.execute(httpRequest); }
看到这里大家肯定觉得这个框架也没有什么了不起嘛,和使用HttpURLConnection和HttpClient差不多嘛,如果你真的这样觉得那么你就大错特错了,其实这个框架的核心在于线程的调度和缓存上面,后期我们会介绍的
回到Volley类,我们看下一个陌生的类就是Network,其实Network不过是个接口而已,它的实现类是BaskNetwork
(5) BaskicNetwork.java
从名字我们就可以看出来,这个类就是进行网络请求的,其实他就是对HttpurlStack或者HttpClientStack的一个封装,真正实现请求的还是上面两个类。
最核心的方法:
@Override public NetworkResponse performRequest(Request<?> request) throws VolleyError { long requestStart = SystemClock.elapsedRealtime(); while (true) { HttpResponse httpResponse = null; byte[] responseContents = null; Map<String, String> responseHeaders = new HashMap<String, String>(); try { // Gather headers. Map<String, String> headers = new HashMap<String, String>(); addCacheHeaders(headers, request.getCacheEntry()); //调用mHttpStack执行http请求 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) { //将返回结果封装成一个NetworkResponse return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, request.getCacheEntry() == null ? null : request.getCacheEntry().data, responseHeaders, true); } // Some responses such as 204s do not have content. We must check. if (httpResponse.getEntity() != null) { 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); } 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 = 0; NetworkResponse networkResponse = null; if (httpResponse != null) { statusCode = httpResponse.getStatusLine().getStatusCode(); } else { throw new NoConnectionError(e); } VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl()); if (responseContents != null) { networkResponse = new NetworkResponse(statusCode, responseContents, responseHeaders, false); if (statusCode == HttpStatus.SC_UNAUTHORIZED || statusCode == HttpStatus.SC_FORBIDDEN) { attemptRetryOnException("auth", request, new AuthFailureError(networkResponse)); } else { // TODO: Only throw ServerError for 5xx status codes. throw new ServerError(networkResponse); } } else { throw new NetworkError(networkResponse); } } } }
这个方法调用了mHttpStack的同名方法,只不过在mHttpStack中返回的是HttpResponse,在这里返回的是NetworkResponse。
然后再看看本篇文章的最后一个类:
RequestQueue.java
我保留了一些关键字段,删除不影响理解的字段
public class RequestQueue { ... //本地缓存队列,如果一个请求能够缓存,那么先放到这个队列中,如果本地缓存没有命中,则加入网络队列,见后面 private final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<?>>(); //网络请求队列 private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?>>(); //默认的网络请求线程个数 默认四个,这个我们可以改动 private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4; //本地缓存的接口 private final Cache mCache; //这个类相信大家并不陌生 private final Network mNetwork; //由于网络请求在子线程中执行,这个对象将请求结果发送到ui线程,功能很像Handler private final ResponseDelivery mDelivery; //网络线程数组 private NetworkDispatcher[] mDispatchers; //本地线程数组 只有一条 private CacheDispatcher mCacheDispatcher; /** 创建一个请求队列 参数1:本地缓存 参数2: network 进行网络进行的包装类 参数3:网络请求线程池大小 参数4:就是一个将子线程的数据发送到ui线程的功能类,先可以不用关心 */ public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) { mCache = cache; mNetwork = network; mDispatchers = new NetworkDispatcher[threadPoolSize]; mDelivery = delivery; } public RequestQueue(Cache cache, Network network, int threadPoolSize) { this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper()))); } /** * 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 */ public RequestQueue(Cache cache, Network network) { this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE); } //启动本地和网络线程 public void start() { stop(); // Make sure any currently running dispatchers are stopped. // 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(); } } //关闭本地和网络线程 public void stop() { if (mCacheDispatcher != null) { mCacheDispatcher.quit(); } for (int i = 0; i < mDispatchers.length; i++) { if (mDispatchers[i] != null) { mDispatchers[i].quit(); } } } //相当于一个过滤器,对于apply方法返回true的Request可以从Queue中删除 public interface RequestFilter { public boolean apply(Request<?> request); } //借助上面的方法实现对没有执行的Request进行删除 public void cancelAll(RequestFilter filter) { synchronized (mCurrentRequests) { for (Request<?> request : mCurrentRequests) { if (filter.apply(request)) { request.cancel(); } } } } //取消所有的请求 public void cancelAll(final Object tag) { if (tag == null) { throw new IllegalArgumentException("Cannot cancelAll with a null tag"); } cancelAll(new RequestFilter() { @Override public boolean apply(Request<?> request) { return request.getTag() == tag; } }); } /** 将Request放入两个队列中的一个 */ public <T> Request<T> add(Request<T> request) { // Tag the request as belonging to this queue and add it to the set of current requests. request.setRequestQueue(this); synchronized (mCurrentRequests) { mCurrentRequests.add(request); } // Process requests in the order they are added. request.setSequence(getSequenceNumber()); request.addMarker("add-to-queue"); // 如果该Request不能缓存,那么直接放入网络队列 if (!request.shouldCache()) { mNetworkQueue.add(request); return request; } // 把Request放入具有相同CacheKey的链表中,如果没有相同的CacheKey的Request请求存在,则放入本地队列 synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); if (mWaitingRequests.containsKey(cacheKey)) { // There is already a request in flight. Queue up. Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey); if (stagedRequests == null) { stagedRequests = new LinkedList<Request<?>>(); } stagedRequests.add(request); mWaitingRequests.put(cacheKey, stagedRequests); if (VolleyLog.DEBUG) { VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey); } } else { // Insert ‘null‘ queue for this cacheKey, indicating there is now a request in // flight. mWaitingRequests.put(cacheKey, null); mCacheQueue.add(request); } return request; } } /** * 结束一个Request */ void finish(Request<?> request) { // Remove from the set of requests currently being processed. synchronized (mCurrentRequests) { mCurrentRequests.remove(request); } if (request.shouldCache()) { synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey); if (waitingRequests != null) { if (VolleyLog.DEBUG) { VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", waitingRequests.size(), cacheKey); } // Process all queued up requests. They won‘t be considered as in flight, but // that‘s not a problem as the cache has been primed by ‘request‘. mCacheQueue.addAll(waitingRequests); } } } } }
写到这里先高一段落吧,来个小小的总结:Volley中有一个RequestQueue(包含本地队列和网络队列),就是请求队列,每一个http请求都被封装成了一个Request,通过队列的add方法加入队列,如果一个Request可以缓存,那么先加入本地队列,如果不能缓存则加入网络队列
待续。。。。。。。
欢迎留言讨论。。。