尊重原创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);
}
}

Volley类主要用来创建一个请求队列,我们的不论什么请求(请求字符串,json,xml)都放入到这个队列中(事实上里面有两个队列,后面我们慢慢学习,临时简单理解为一个)。

创建完队列后。调用start方法,就会启动一些线程(临时不关注多少条线程),不断监听队里里面是否有请求,假设有请求则运行http请求,在2.3之前的版本号中。Http请求是通过httpClient实现。在2.3以后的版本号中是通过HttpURLConnection实现,由于在2.3之前的版本号中HttpRlConnection非常不稳定

(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));
}
//这种方法名字非常长。事实上功能非常easy,就是为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
我保留了一些keyword段,删除不影响理解的字段

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能够缓存,那么先增加本地队列。假设不能缓存则增加网络队列

欢迎继续阅读Volley框架浅析(二)

欢迎留言讨论。。。

Android网络通信Volley框架源代码浅析(一)的更多相关文章

  1. Android网络通信Volley框架源代码浅析(三)

    尊重原创 http://write.blog.csdn.net/postedit/26002961 通过前面浅析(一)和浅析(二)的分析.相信大家对于Volley有了初步的认识,可是假设想更深入的理解 ...

  2. Android网络通信Volley框架源代码浅析(二)

    尊重原创 http://write.blog.csdn.net/postedit/25921795 在前面的一片文章Volley框架浅析(一)中我们知道在RequestQueue这个类中,有两个队列: ...

  3. Volley框架源代码分析

    Volley框架分析Github链接 Volley框架分析 Volley源代码解析 为了学习Volley的网络框架,我在AS中将Volley代码又一次撸了一遍,感觉这样的照抄代码也是一种挺好的学习方式 ...

  4. Android 网络通信框架Volley简介(Google IO 2013)

    1. 什么是Volley 在这之前,我们在程序中需要和网络通信的时候,大体使用的东西莫过于AsyncTaskLoader,HttpURLConnection,AsyncTask,HTTPClient( ...

  5. Android 网络通信框架Volley(一)

    转自:http://blog.csdn.net/t12x3456/article/details/9221611 1. 什么是Volley 在这之前,我们在程序中需要和网络通信的时候,大体使用的东西莫 ...

  6. [转]Android 网络通信框架Volley简介(Google IO 2013)

    Volley主页 https://android.googlesource.com/platform/frameworks/volley http://www.youtube.com/watch?v= ...

  7. 【转】Android 网络通信框架Volley简介(Google IO 2013)

    Volley主页 https://android.googlesource.com/platform/frameworks/volley http://www.youtube.com/watch?v= ...

  8. Android 网络通信框架Volley简介

    1.1. Volley引入的背景在以前,我们可能面临如下很多麻烦的问题. 比如以前从网上下载图片的步骤可能是这样的流程: 在ListAdapter#getView()里开始图像的读取. 通过Async ...

  9. Android 网络通信框架Volley基本介绍

    Volley主页 https://android.googlesource.com/platform/frameworks/volley http://www.youtube.com/watch?v= ...

随机推荐

  1. notepad++调用python3中文乱码

    使用notepad++,配置好快捷键调用python3,一切就绪,仿佛就差代码了,结果一使用, 中文乱码,一直没有好的解决办法. 最后只能在代码中增加一行重写向输出解决,示例如下: #!/usr/bi ...

  2. shell判断变量是字符还是数字

    ok,以后最好是每天一个shell小脚本吧,这样以后工作时还可以直接套用,嗯,比较不错,顺便还可以带给刚入门shell的朋友一些帮助,好了,废话不多说,下面是我两种判断的实现方式: 1.通过grep去 ...

  3. 【CS Round #36 (Div. 2 only) A】Bicycle Rental

    [题目链接]:https://csacademy.com/contest/round-36/task/bicycle-rental/ [题意] 让你从n辆车中选一辆车; 每一辆车有3个属性 1.到达车 ...

  4. ios 绘图,绘制坐标系,画坐标系

    先来看个效果: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/d ...

  5. [React] Make Controlled React Components with Control Props

    Sometimes users of your component want to have more control over what the internal state is. In this ...

  6. 独立python环境之virtualenv和virtualenvwrapper

    介绍 如果有一台測试机,多个人使用,有多个项目,不同项目可能python版本号不一样.须要的库不一样. 我们须要一个独立干净的python环境,互相隔离,互不影响. virtualenv能够帮我们解决 ...

  7. vue4 属性 class style

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. 学习 shell —— 编写基本脚本

    set:查看环境变量: 0. 简单说明 一般而言,shell 会通过 PATH 变量来查找命令,如果要执行用户编写的脚本(未添加进 PATH 路径),还需两步操作: 需要 $ ./xx 为需执行该脚本 ...

  9. vue --- cli build 后的项目,图片路径出错

    今天在插入背景图片过程中,遇到了路径错误的问题,通过网上查询,找到了解决的办法,但是大部分都没有讲造成这种问题的原因,故我简单地总结了一下,并加入了一些自己的理解,欢迎共同探讨~ 当用vue-cli自 ...

  10. SCOPE_IDENTITY()和 SELECT @@IDENTITY 的用法

    这俩个,是在插入数据的时候使用,返回刚刚插入数据行的ID 大家慎用@@IDENTITY,而尽量采用 SCOPE_IDENTITY() 函数替换之. SCOPE_IDENTITY()  也是得到最后一条 ...