Volley第二部分
Volley 第二部分
4.2 核心类功能介绍
4.2.1 Volley.java
和volley框架同名的类,其实是个工具类,作用是构建一个可用于添加网络请求的RequestQueue对象
1. 主要函数
Volley.java有两个重载的静态方法:
public static RequestQueue newRequestQueue(Context context);
public static RequestQueue newRequestQueue(Context context, HttpStack stack);
第一个方法的实现调用了第二个方法,传的HeepStack的参数为null。
第二个方法中,如果HttpStack的参数为null,则如果系统在Gingerbread及之后(即API Level>=9),
采用基于HttpURLConnection的HurlStack,如果小于9,采用基于HttpClient的HttpClientStack.
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
得到了HttpStack,然后通过它构造一个代表网络(Network)的具体实现BasicNetwork.
接着构造一个代表缓存(Cache)的基于Disk的具体实现DiskBasedCache.
最后将网络(Network)对象和缓存(Cache)对象传入构建一个RequestQueue,启动这个RequestQueue,并返回。
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCahce(cacheDir),network);
queue.start();
return queue;
大多采用volley.newRequestQueue(context)的默认实现,构建RequestQueue.
通过上面可以看到,可以抛开Volley工具类构造自定义的RequestQueue,采用自定义HttpStack,
采用自定义的Network实现,采用自定义的Cache实现来构建RequestQueue.
2. HttpURLConnection和AndroidHttpClient 如何选择及原因:
在Froyo(2.2)之前,HttpURLConnection有个大bug,调用close()会影响连接池,导致连接复用失败,
所以在Froyo之前,使用HttpURLConnection需要关闭keepAlive.
另外在 Gingerbread(2.3) HttpURLConnection 默认开启了 gzip 压缩,提高了 HTTPS 的性能,
Ice Cream Sandwich(4.0) HttpURLConnection 支持了请求结果缓存。
再加上 HttpURLConnection 本身 API 相对简单,所以对 Android 来说,在 2.3 之后建议使用
HttpURLConnection,之前建议使用 AndroidHttpClient。
3. 关于User Agent
通过代码,发现如果使用AndroidHttpClient,Volley还会将请求中的UserAgent字段设置为App的
${packageName}/${versionCode},如果异常则使用 “volley/0”,不过这个获取UserAgent的操作
应该放到if else 内部更合适,而对于HttpURLConnection却没有任何操作,为什么?如果使用Fiddler
或Charles对数据抓包,会发现,HttpURLConnection默认是对UserAgent,类似:
Dalvik/1.6.0 (Linux; U; Android 4.1.1; Google Nexus 4 - 4.1.1 - API 16 - 768x1280_1 Build/JRO03S)
经常用 WebView 的同学会也许会发现似曾相识,是,WebView 默认的 User-Agent 也是这个。
实际在请求发出之前,会检测 User-Agent 是否为空,如果不为空,则加上系统默认UserAgent.
在Android2.1之后,可以通过:
String userAgent = System.getProperty("http.agent");
得到系统默认的User-Agent,Volley如果希望自定义User-Agent,可在自定义Request中重写getHeaders()函数
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headerMap = new HashMao<String, String>();
headerMap.put("User-Agent", "android-open-project-analysis/1.0");
return headerMap;
}
# 4.2.2 Request.java
代表一个网络请求的抽象类,通过构建一个Reqest类的非抽象子类(StringRequest,JsonRequest,
ImageRequest或自定义)对象,并将其加入到RequestQueue中来完成一次网络请求操作,
Volley支持8种Http请求方式,GET,POST,PUT,DELETE,HEAD,OPTIONS,TRACE,PATCH
Request类中包含了请求url,请求方式,请求Header,请求Body,请求的优先级等信息。
因为是抽象类,子类必须重写的两个方法。
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
子类重写此方法,将网络返回的原生字节内容转换为合适的类型,此方法会在工作线程中被调用。
abstract protected void deliverResponse(T response);
子类重写此方法,将解析成合适类型的内容传递给它们的监听回调。
一下两个方法也经常被重写
public byte[] getBody()
重写此方法,可以构建用于POST,PUT,PATCH, 请求方式的body内容。
projected Map<String, String> getParams()
在上面getBody()函数没有被重写情况下,此方法的返回值会被key、value分别编码后拼装起来
转换为字节码作为Body的内容。
RequestQueue.java
volley 框架的核心类,将请求request加入到一个运行的RequestQueue中,来完成请求操作。
主要成员变量
RequestQueue 中维持了两个基于优先级的Request队列,缓存请求队列和网络请求队列。
在缓存请求队列中的Request,将通过缓存获取数据;放在网络请求队列的Request,将通过网络获取数据private final PriorityBlockingQueue
> mCacheQueue = new PriorityBlockingQueue<
Request<?>>();
private final PriorityBlockingQueue> mNetworkQueue = new PriorityBlockingQueue
>();
维护了一个正在进行中,尚未完成的请求集合。
private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
维护了一个等待请求的集合,如果一个请求正在被处理并且可以被缓存,后续有相同的url的时候,
将进入此队列:
private final Map<String,Queue<Request<?>>> mWaitingRequests = new HashMap<String,
Queue<Request<?>>>();
- 启动队列
创建出一个RequestQueue以后,调用start方法,启动队列。
/**
* Starts the dispathers in this queue.
*/
public void start() {
stop();
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();
}
}
start()方法中,开启了缓存调度线程CacheDispatcher和n个网路调度线程NetworkDispatcher,
默认线程数为4,即n默认为4,存在优化的余地,比如可以根据CPU核数以及网络类型计算更合适的并发数
缓存调度线程不断的从缓存请求队列中取出request去处理,网络调度线程不断的从网络请求
队列中取出Request去处理
加入请求
public
Request add(Request request);
流程图如下:
请求完成
void finish(Request<?> request)
Request 请求结束
- 首先在正在进行中请求集合mCurrentRequests中移除该请求。
- 然后查找请求等待集合 mWaitingRequests中是否存在等待的请求,如果存在则将等待队列移除
并将等待队列所有的请求添加到缓存请求队列中,让缓存请求处理线程CacheDispoatcher自动处理。
请求取消
public void canceAll(RequestFilter filter) public void cancelAll(final Object tag)
取消当前请求集合所有符合条件的请求。
filter参数表示可以按照自定义的过滤器过滤需要取消的请求。
tag表示按照Request.setTag 设置好的tag取消请求,比如属于某个activity的。
4.2.4 CacheDispatcher.java
一个线程用于调度处理走缓存的请求,启动后会不断从缓存请求队列中取请求处理,
队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理。
当结果未缓存过、缓存失效或缓存需要刷新的情况下,该请求都需要重新进入NetworkDispatcher
去调度处理。
- 成员变量
BlockingQueueRequest<?>> mCacheQueue 缓存请求队列
BlockingQueue> mNetworkQueue 网络请求队列
Cache mCache 缓存类, 代表一个可以获取请求结果,存储请求结果的缓存
ResponseDelivery mDelivery 请求结果传递类。 - 处理流程图
NetworkDispatcher.java
一个线程,用来调度处理走网络的请求。启动后会不断的从网络请求队列中取得请求处理,
队列为空则等待,请求处理结束后则将结果传递给ResponseDelivery去执行后续结果,
并判断结果是否需要缓存。
1.成员变量
BlockingQueue
Network mNetwork 网络类,代表了一个可以执行请求的网络
Cache mCache 缓存类,代表了一个可以获取请求结果,存储请求结果的缓存
ResponseDelivery mDelivery 请求结果传递类,可以传递请求的结果或者错误的调用者
2.处理流程图
4.2.6 Cache.java
缓存接口,代表了一个可以获取请求结果,存储请求结果的缓存。
1.主要方法
public Entry get(String key); 通过key获取请求的缓存实体
public void put(String key, Entry entry); 存入一个请求的缓存实体
public void remove(String key, Entry entry); 移除指定的缓存实体
public void clear(); 情况缓存
2.代表缓存实体的内部类Entry
成员变量和方法
byte[] data 请求返回的数据 (body实体)
String etag Http相应首部中用于缓存新鲜度验证的ETag
long serverDate Http 响应首部中的响应产生时间
long ttl 缓存的过期时间
long softTtl 缓存的新鲜时间
Map<String, String> responseHeaders 响应的headers
boolean isExpired()判断缓存是否过期,过期缓存不能继续使用
boolean refreshNeeded() 判断缓存是否新鲜,不新鲜的缓存需要发送到服务器做新鲜度的检测
4.2.7 DiskBasedCache.java
继承与Cache类,基于Disk的缓存实现类
1.主要方法
//初始化,扫描缓存目录得到所有缓存数据摘要信息放入内存
public synchronized void initialize()
//从缓存中得到数据,先从摘要信息中得到摘要信息,然后读取缓存数据文件得到内容
public synchronized Entry get(String key)
//将数据存入缓存内。先检查缓存是否会满,会则先删除缓存中部分数据,然后再新建缓存文件
public synchronized void put(String key, Entry entry)
// 检查是否能再分配 neededSpace 字节的空间,如果不能则删除缓存中部分数据
private void pruneIfNeeded(int neededSpace)
//清空缓存
public synchronized void clear()
//删除缓存中的某个元素
public synchronized void remove(String key)
2.CacheHeadler类
CacheHeadler是缓存文件摘要信息,存贮在缓存文件的头部,与上面的Cache.Entry相似