Volley解析
Volley是Gooogle I/O 2013发布的一个处理和缓存网络请求的库,能使网络通信更快,更简单,更健壮。Volley名称的由来: a burst or emission of many things or a large amount at once。
2013年volley就有了,分析Volley的文章网上也是一堆一堆的,不过Volley的设计还是值得研究的。
Volley feature
- Volley automatically schedule all network requests. It means that Volley will be taking care of all the network requests your app executes for fetching response or image from web.
- Volley provides transparent disk and memory caching.
- Volley provides powerful cancellation request API. It means that you can cancel a single request or you can set blocks or scopes of requests to cancel.
- Volley provides powerful customization abilities.
- Volley provides Debugging and tracing tools
请求调度
在Android Developer上有Volley的请求调度流程图
从入口分析源码
我们从使用开始分析源码(一般来说,分析别人的源码都是这样的)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31public class MyApplication extends Application{
public static RequestQueue queues;
@Override
public void onCreate() {
super.onCreate();
queues = Volley.newRequestQueue(getApplicationContext());
}
public static RequestQueue getHttpQueues() {
return queues;
}
}
private void volley_get(){
String url = "http://ip.taobao.com/service/getIpInfo.php?ip=202.96.128.1";
StringRequest request = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>(){
@Override
public void onResponse(String response) {}
},
new Response.ErrorListener(){
@Override
public void onErrorResponse(VolleyError error)
{}
}
);
requestTag = "volley_get";
request.setTag(requestTag);
MyApplication.getHttpQueues().add(request);
}
使用起来非常简单,Volley.newRequestQueue(getApplicationContext());新建一个RequestQueue单例(为什么是一个?因为方便维护RequestQueue,下面看源码说),然后使用RequestQueue.add(Request request)就可以等待返回结果了。
Volley–>工具类
首先,新创建了一个RequestQueue,我们看下RequestQueue创建的源码。
Volley.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
//1、默认设置缓存的路径为/data/data/packagename/cache/volley
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
//2、设置userAgent为包名+版本号
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
//3、根据android sdk版本,使用不同的stack,9以上使用HttpURLConnection,9以下使用HttpClient
if (stack == null) {
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));
}
}
//4、根据HttpStack设置Network,这个是真正访问网络请求数据的。
Network network = new BasicNetwork(stack);
//5、maxDiskCacheBytes默认为-1,这个是用来设置最大的缓存byte
RequestQueue queue;
if (maxDiskCacheBytes <= -1)
{
// No maximum size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
}
else
{
// Disk cache size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
}
//最后启动线程,并且返回queue
queue.start();
return queue;
}
RequestQueue.java1
2
3
4
5
6
7
8
9
10
11
12
13
14public 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();
}
}
这里默认启动了1个CacheDispatcher线程和4个NetworkDispatcher线程不断的获取Request。因为是BlockingQueue所以如果Queue里没有Request则阻塞住。
Request –>请求封装类
Volley中每一次请求被封装为一个Request对象,Request是个抽象类,通过设置Request的属性,来自定义请求时的方式。如:
- url– 请求的url
- method– 请求的方式:GET/POST/PUT/DELETE等等
- mRequestQueue– 该request和它被放入的mRequestQueue进行了绑定,用于在Request请求结束后,告诉mRequestQueue把该request从请求等待队列中移除。
- mRetryPolicy– 失败的重试策略
- mErrorListener– 请求失败的监听器
- mIdentifier– 该request的唯一标示
当然还有一个重要的parseNetworkResponse抽象方法。Request有多个子类,如StringRequest,ImageRequest,JsonRequest等。在parseNetworkResponse方法中进行各自的解析。1
2
3
4
5
6
7
8
9/**
* Subclasses must implement this to parse the raw network response
* and return an appropriate response type. This method will be
* called from a worker thread. The response will not be delivered
* if you return null.
* @param response Response from the network
* @return The parsed response, or null in the case of an error
*/
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
RequestQueue –>核心调度类
1 | /** |
先总结下:
1、每个request都设置requestQueue,方便request请求完成时的回调
2、如果request不需要缓存,则直接放入mNetworkQueue,NetworkDispatcher进行网络请求
3、如果request已经在请求队列里了,同一个请求的判断标准是method+url,则不进行请求,暂时放到mWaitingRequests里。等待第一个请求完成后,统一的进行返回。
否则,放入mCacheQueue和mWaitingRequests。由CacheDispatcher线程进行处理。
接下来扫一眼CacheDispatcher
CacheDispatcher –> request缓存处理器
1 | @Override |
NetworkDispatcher –> request网络请求
1 | @Override |
还有网络请求那块没有说明,这个比较简单了。大家还是看下源码理解比较清晰。
还有遗留的那个为什么要使用一个RequestQueue?原因也很明确了,这样方便Request的管理。如果一个请求就新建一个RequestQueue,不是不可以,而是太浪费了。因为每个RequestQueue都会默认开启5个线程。
##Volley的缺陷
参考自codekk:
1、缓存的再验证方面,在构建If-Modified-Since请求首部时,Volley 使用了服务端响应的Date首部,没有使用Last-Modified首部。整个框架没有使用Last-Modified首部。这与 Http 语义不符。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {
// If there's no cache entry, we're done.
if (entry == null) {
return;
}
if (entry.etag != null) {
headers.put("If-None-Match", entry.etag);
}
if (entry.serverDate > 0) {
Date refTime = new Date(entry.serverDate);
headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
}
}
2、RequestQueue使用PriorityQueue保存request队列,但是PriorityQueue默认最多只能保存11个请求,超出则报错。所以生成request的速度不能大于消费request的速度,这应该是个坑。使用的时候可以设置稍微大点的capacity。
3、初始化RequestQueue时候,使用Volley.newRequestQueue(Context context);操作失误的话容易造成内存泄露。这里应该使用context.getApplicationContext()。应该在源码里进行控制。
4、Volley加载大图容易OOM。不建议使用Volley来显示图片。
总结
最后,理一下Volley的值得学习的点:
1、设置统一的框架访问入口(Volley)
2、使用组合和继承
1)封装了Request、Response
2)对Request进行抽象,不同的请求实现各自的解析方法即可
3)对于网络请求抽象HttpStack,不同的sdk使用不同的请求方法
3)Request的添加和处理分离。Request只需要放入RequestQueue,处理的时候,由各自的线程来处理,很好的利用了BlockingQueue的优势:阻塞和线程安全。