Volley源码解析
说点题外话,将近三个半月没有写博客了,年初换工作,在新的公司,上班第三天开始干活,花了二十来天弄了一个项目,上线后,接着又迭代了几个版本,不知不觉,试用期过完,才稍微有几天闲时。在年初的时候,就一直在想,
将圈内的几个流行的网络框架的源码分析分析,但是又但是水平不够,有些分析的不好,那就尴尬了....所以花了点时间好好看了一下,走了一遍这些源码,决定试一试,相当于做个笔记吧。今天就从一个相对轻量级的网络请求框架下手--Volley。
Volley
提起这个Volley,很多同学应该都很熟悉,但是我面试过蛮多人,问起,对volley的了解,基本上就说,里面对图片做了缓存,在2.3之前用的是HTTPClient,
2.3后用的是HttpURLConnection,然后就没了.... 虽然这没什么错,但是对于一个有经验的开发人员来说,这样的认识,太过于表面了。我们知道Volley是
在2013年Google I/O大会上推出了一个新的网络通信框架,他的设计目的就是为了那些请求数据量不是特别大,但是又是特别频繁的网络操作非常适合。但是对于
数据量较大的请求,比如说下载一个较大的文件,Volley可能相比于其他的框架,就有点不足了....
Volley简单使用
我这里是以依赖架包的形式 ,大家也可以以gradle的形式进行依赖。
好了,接下来上代码了.....
//获取volley的请求对象
RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
StringRequest stringRequest = new StringRequest(StringRequest.Method.GET, "http://www.baidu.com", new Response.Listener<String>() {
@Override
public void onResponse(String s) {
Log.d("MainActivity", "----->" + s); }
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Log.d("MainActivity", "---volleyError-->" + volleyError);
}
});
requestQueue.add(stringRequest);
从代码可以看出,首先newRequestQueue来获取到一个请求队列,然后在将StringRequest这个请求添加到请求队列中,就可以了,就是这么简单。当然请求不值
StringRequest,还有JsonObjectRequest ,ImageRequest等等但是用法都是一样的,这里就不贴代码了。接着...就没了,Volley的简单使用就这样可以进行请
求了。是不是很简单....
但是这个不是本篇的重点,重点是分析一下这些是怎么执行的。先上一张图,这张图是在网上拿过来用的...
我们先看看newRequestQueue这个内部是怎么执行的,代码一开始连续执行了几个重载方法,最后走到newRequestQueue
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
File cacheDir = new File(context.getCacheDir(), "volley");
String userAgent = "volley/0"; try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException var7) {
;
} //这里进行了一个版本的判断 2.3之前用的是HTTPClient,2.3之后使用的是HttpURLConnection
if (stack == null) {
if (VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
} Network network = new BasicNetwork((HttpStack)stack);
RequestQueue queue;
if (maxDiskCacheBytes <= -1) {
queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
} else {
queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
} queue.start();
return queue;
}
在这里,我们看到了一个版本判断,是不是瞬间感觉有点熟悉,没错,我们前面说的,volley2.3之前用的是HTTPClient,2.3之后使用的是HttpURLConnection
就是在这里进行判断的。接着看queue.start();
public void start() {
this.stop();
this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
this.mCacheDispatcher.start(); for(int i = 0; i < this.mDispatchers.length; ++i) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
this.mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
} }
mCacheDispatcher是缓存调度线程,NetworkDispatcher是网络调度线程,而这个this.mDispatchers.length系统默认的大小为4,也就是说,在这里总共启动了5个线程在后台运行。
好了,到这里,就可以了,看源码不要每一行都弄懂,不然,出不来了。到这里就拿到了这个RequestQueue对象。回过头来看前面使用的代码
//获取volley的请求对象
RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
StringRequest stringRequest = new StringRequest(StringRequest.Method.GET, "http://www.baidu.com", new Response.Listener<String>() {
@Override
public void onResponse(String s) {
Log.d("MainActivity", "----->" + s); }
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Log.d("MainActivity", "---volleyError-->" + volleyError);
}
});
requestQueue.add(stringRequest);
我们拿到这个RequestQueue对象以后,然后就把这个请求通过add方法添加到队列中,我们看看这个add()方法是怎么执行的。
public <T> Request<T> add(Request<T> request) {
request.setRequestQueue(this);
Set var2 = this.mCurrentRequests;
synchronized(this.mCurrentRequests) {
this.mCurrentRequests.add(request);
} request.setSequence(this.getSequenceNumber());
request.addMarker("add-to-queue");
if (!request.shouldCache()) { //如果不能缓存
this.mNetworkQueue.add(request);
return request;
} else {
Map var7 = this.mWaitingRequests;
synchronized(this.mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (this.mWaitingRequests.containsKey(cacheKey)) { //判断之前是否执行过,但是还没有返回结果
Queue<Request<?>> stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList();
} ((Queue)stagedRequests).add(request);
this.mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[]{cacheKey});
}
} else {
//没有的话就将请求加入缓存队列mCacheQueue,同时加入mWaitingRequests中用来做下次同样请求来时的重复判断依据
this.mWaitingRequests.put(cacheKey, (Object)null);
this.mCacheQueue.add(request);
} return request;
}
}
}
从代码中可以看出,首先判断是否可以缓存,当然,默认是可以缓存的。如果不能缓存的话,则通过this.mNetworkQueue.add(request);将请求添加到网络请求队列中。如果可以缓存
则还会判断一次这个请求是否请求,如果执行过就就通过this.mWaitingRequests.put(cacheKey, stagedRequests);添加到mWaitingRequests队列,不在重复请求。否则就加入到缓存队列。
大体的流程是这样。现在我们看看缓存的,和网络的是怎么执行的。我们找到start()方法
public void start() {
this.stop();
this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
this.mCacheDispatcher.start(); for(int i = 0; i < this.mDispatchers.length; ++i) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
this.mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
} }
先看CacheDispatcher,找到run()方法
public void run() {
if (DEBUG) {
VolleyLog.v("start new dispatcher", new Object[0]);
} Process.setThreadPriority(10);
this.mCache.initialize(); while(true) {
while(true) {
while(true) {
while(true) {
try {
while(true) {
final Request<?> request = (Request)this.mCacheQueue.take(); //从缓存队列中获取到一个请求
request.addMarker("cache-queue-take");
if (request.isCanceled()) { //判断请求是否取消,如果取消了,那就将该请求finish掉
request.finish("cache-discard-canceled");
} else {
Entry entry = this.mCache.get(request.getCacheKey());
if (entry == null) {//如果从缓存中取出来的内容为空,则将请求加入到网络线程中再次请求
request.addMarker("cache-miss");
this.mNetworkQueue.put(request);
} else if (entry.isExpired()) { //如果请求过期了,则将请求加入到网络线程中再次请求
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
this.mNetworkQueue.put(request);
} else { //将数据回调到主线程
request.addMarker("cache-hit");
Response<?> response = request.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (entry.refreshNeeded()) {
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
response.intermediate = true;
this.mDelivery.postResponse(request, response, new Runnable() {
public void run() {
try {
CacheDispatcher.this.mNetworkQueue.put(request);
} catch (InterruptedException var2) {
;
} }
});
} else {
this.mDelivery.postResponse(request, response);
}
}
}
}
} catch (InterruptedException var4) {
if (this.mQuit) {
return;
}
}
}
}
}
}
}
这里嵌套了几个循环,有点凌乱啊,但是慢慢分析的话,就会发现,其实很清晰。我在注释上面写了,这里就不重复了
我们在看看NetworkDispatcher,看看网络线程是怎么执行的。一样找到run()方法
public void run() {
Process.setThreadPriority(10); while(true) {
long startTimeMs;
Request request;
while(true) {
startTimeMs = SystemClock.elapsedRealtime(); try {
request = (Request)this.mQueue.take(); //获取到一个请求
break;
} catch (InterruptedException var6) {
if (this.mQuit) {
return;
}
}
} try {
request.addMarker("network-queue-take");
if (request.isCanceled()) { //如果请求取消了,则将请求finish掉
request.finish("network-discard-cancelled");
} else {//进行网络请求
this.addTrafficStatsTag(request);
NetworkResponse networkResponse = this.mNetwork.performRequest(request);
request.addMarker("network-http-complete");
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
} else {
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
if (request.shouldCache() && response.cacheEntry != null) {
this.mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
} request.markDelivered();
this.mDelivery.postResponse(request, response);
}
}
} catch (VolleyError var7) {
var7.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
this.parseAndDeliverNetworkError(request, var7);
} catch (Exception var8) {
VolleyLog.e(var8, "Unhandled exception %s", new Object[]{var8.toString()});
VolleyError volleyError = new VolleyError(var8);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
this.mDelivery.postError(request, volleyError);
}
}
}
代码比较多,我们直接找到NetworkResponse networkResponse = this.mNetwork.performRequest(request);这句代码,这句代码就是请求网络的代码,最核心的。performRequest是一个接口,我们看看这个
performRequest()方法。Network在最开始说版本判断的时候里面有一句代码Network network = new BasicNetwork((HttpStack)stack); 从这句代码,我们可以知道BasicNetwork才是最终
实现网络请求的类,我们找到performRequest方法
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime(); while(true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map responseHeaders = Collections.emptyMap(); try {
Map<String, String> headers = new HashMap();
this.addCacheHeaders(headers, request.getCacheEntry());
httpResponse = this.mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
if (statusCode == 304) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(304, (byte[])null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
} entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(304, entry.data, entry.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
} if (statusCode == 301 || statusCode == 302) {
String newUrl = (String)responseHeaders.get("Location");
request.setRedirectUrl(newUrl);
} byte[] responseContents;
if (httpResponse.getEntity() != null) {
responseContents = this.entityToBytes(httpResponse.getEntity());
} else {
responseContents = new byte[0];
} long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
this.logSlowRequests(requestLifetime, request, responseContents, statusLine);
if (statusCode >= 200 && statusCode <= 299) {
return new NetworkResponse(statusCode, responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
} throw new IOException();
} catch (SocketTimeoutException var12) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException var13) {
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException var14) {
throw new RuntimeException("Bad URL " + request.getUrl(), var14);
} catch (IOException var15) {
int statusCode = false;
NetworkResponse networkResponse = null;
if (httpResponse == null) {
throw new NoConnectionError(var15);
} int statusCode = httpResponse.getStatusLine().getStatusCode();
if (statusCode != 301 && statusCode != 302) {
VolleyLog.e("Unexpected response code %d for %s", new Object[]{statusCode, request.getUrl()});
} else {
VolleyLog.e("Request at %s has been redirected to %s", new Object[]{request.getOriginUrl(), request.getUrl()});
} if (responseContents == null) {
throw new NetworkError(networkResponse);
} networkResponse = new NetworkResponse(statusCode, (byte[])responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
if (statusCode != 401 && statusCode != 403) {
if (statusCode != 301 && statusCode != 302) {
throw new ServerError(networkResponse);
} attemptRetryOnException("redirect", request, new AuthFailureError(networkResponse));
} else {
attemptRetryOnException("auth", request, new AuthFailureError(networkResponse));
}
}
}
}
代码比较多,但是大多数代码是判断状态返回码的,不需要理会。我们直接看httpResponse = this.mHttpStack.performRequest(request, headers);这一句代码,HttpStack这个有没有很熟悉。没有??没关系我在复制一次代码
if (stack == null) {
if (VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
还是在这个版本判断这里,这里就是HurlStack就是真正的网络请求的类了,网络请求,就是写在这个类里面的。好了,volley整个流程大概就是这样了。现在大家回过头看最初的哪一张图,是不是明了很多。
Volley源码解析的更多相关文章
- Volley 源码解析
Volley 源码解析 1. 功能介绍 1.1. Volley Volley 是 Google 推出的 Android 异步网络请求框架和图片加载框架.在 Google I/O 2013 大会上发布. ...
- 【安卓网络请求开源框架Volley源码解析系列】定制自己的Request请求及Volley框架源码剖析
通过前面的学习我们已经掌握了Volley的基本用法,没看过的建议大家先去阅读我的博文[安卓网络请求开源框架Volley源码解析系列]初识Volley及其基本用法.如StringRequest用来请求一 ...
- Volley源码解析(三) 有缓存机制的情况走缓存请求的源码分析
Volley源码解析(三) 有缓存机制的情况走缓存请求的源码分析 Volley之所以高效好用,一个在于请求重试策略,一个就在于请求结果缓存. 通过上一篇文章http://www.cnblogs.com ...
- # Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析#
Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析 Volley源码一共40多个类和接口.除去一些工具类的实现,核心代码只有20多个类.所以相对来说分析起来没有那么吃力.但是要想分析透 ...
- Volley 源码解析(转)
项目:Volley,分析者:grumoon,校对者:Trinea 本文为 Android 开源项目源码解析 中 Volley 部分项目地址:Volley,分析的版本:35ce778,Demo 地址:V ...
- Android开发学习之路-Volley源码解析
从简单的StringRequest入手看看Volley的工作机制. 先简单说下Volley的用法: ① 获取一个RequestQueue mRequestQueue = Volley.newReque ...
- Volley 源码解析 StringRequest解析
Android Vollety是一个很有用的框架,所以想借鉴前人思想,分析这个源代码. 参考: http://blog.csdn.net/crazy__chen/article/details/464 ...
- 【安卓网络请求开源框架Volley源码解析系列】初识Volley及其基本用法
在安卓中当涉及到网络请求时,我们通常使用的是HttpUrlConnection与HttpClient这两个类,网络请求一般是比较耗时,因此我们通常会在一个线程中来使用,但是在线程中使用这两个类时就要考 ...
- volley源码解析-Throwable类源码解析
前提知识点: 1.Serializable接口 作用:表示可序列化的语义.就是Java提供的通用数据保存和读取接口.任何类型实现了Serializeable接口,就可以被保存到文件中,或者作为数据流通 ...
随机推荐
- binlog怎样参与mysql recover的
转自 Louis Hust's Blog MySQL两阶段提交 29 July 2015 参数介绍 两阶段提交 什么情况下会出现binlog写入了,但是实际这条数据不存在库中? 参数介绍 innod ...
- 201621123008 《Java程序设计》第五周学习总结
1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词 关键词:接口,内部类. 1.2 尝试使用思维导图将这些关键词组织起来.注:思维导图一般不需要出现过多的字. 1.3 可选:使用常 ...
- Codeforces 665A. Buses Between Cities 模拟
A. Buses Between Cities time limit per test: 1 second memory limit per test: 256 megabytes input: s ...
- bluez蓝牙测试工具
http://blog.csdn.net/talkxin/article/details/50610984
- apt-get clean 清除 apt 的缓存
#apt-get clean 再 df -h 看看,是不是可用空间增加了几个 G ?
- Mint UI 之 Swipe 组件
#为什么不显示内容? 一定要指定 mt-swipe 元素的宽和高. <mt-swipe :auto="4000" class="swipe"> &l ...
- LINUX网络编程 IO 复用
参考<linux高性能服务器编程> LINUX下处理多个连接时候,仅仅使用多线程和原始socket函数,效率十分低下 于是就出现了selelct poll epoll等IO复用函数. 这 ...
- [K8S]污点调度
如果不希望某个节点被调度可以使用以下命令进行设置 kubectl taint node master01 node-role.kubernetes.io/master="":No ...
- js之create()
语法: Object.create(proto, [propertiesObject]) 返回一个新的对象的指针 proto:对象会被作为新创建的对象的原型 [propertiesObject]:对象 ...
- python 求最大子序列
动态规划的本质,是对问题状态的定义和状态转移方程的定义.dynamic programming is a method for solving a complex problem by breaki ...