//创建请求队列 google推荐写一个单例类 获取唯一一个队列
public class VolleyApplication extends Application {
private static RequestQueue requestQueue;
@Override
public void onCreate() {
super.onCreate();
requestQueue = Volley.newRequestQueue(getApplicationContext());
}
public static RequestQueue getHttpQueues(){
return requestQueue;
}
}
//这里以StringRequest为例,Volley还提供了JsonObjectRequest、ImageRequeest
String url = "https://www.baidu.com";
StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
@Override
public void onResponse(String s) {
Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Toast.makeText(MainActivity.this, volleyError.toString(), Toast.LENGTH_SHORT).show();
}
});
// 设置tag当调用queue.cancelAll(tag)会取消所有拥有这个tag的request,一般用于在onStop()中关闭网络连接
request.setTag(this);
VolleyApplication.getHttpQueues().add(request);
使用Volley 的主要步骤主要包括 创建RequestQueue、 创建Request、 将Request加入到RequestQueue 三步,我们来一一说明。
public class Volley {
private static final String DEFAULT_CACHE_DIR = "volley";
public Volley() {
}
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
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 var6) {
;
}
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 = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, (HttpStack)null);
}
}
可以看到,Volley的newRequestQueue()方法里面,根据版本号不同创建了stack(HurStack 和 HttpClientStack)。
然后创建了网络类 BssicNetwoek,缓存类DiskBasedCache,然后使用这两个来创建RequestQueue对象。
接下来看下 queue.start()
做了什么
public void start() {
stop();
// PriorityBlockingQueue> mCacheQueue = new PriorityBlockingQueue<>(); //1
// PriorityBlockingQueue> mNetworkQueue = new PriorityBlockingQueue<>();
// 这里的mCacheQueue、mNetWorkQueue都是优先级阻塞队列mCache是传入的DiskBaseCache
// mDeliver则是在构造器里面创建的ExecutorDelivery其拥有一个Looper是主线程的Handler
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start(); //2
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher =
new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start(); //3
}
RequestQueue对象创建的时候,会初始化一个缓存调度线程(CacheDispatcher),和4个网络调度线程(NetworkDispatcher),所以Volley默认会在后台开启5个线程。线程都初始化之后,分别调用其start方法开启线程。
可以把RequestQueue当做缓存队列和网络请求队列的生产者,NetworkDispatcher、CacheDispatcher当做消费者,当然有时候CacheDispatcher也会被当做网络请求队列的生产者(缓存过期、没命中等情况),Request内有一个枚举类定义了4种优先级,默认情况是NORMAL,我们可以重写Request.getPriority从而改变优先级,PriorityBlockingQueue会调用Request.compaeTo来判断顺序(优先级越高越先出去,优先级相等则按照FIFO)
public enum Priority {
LOW,
NORMAL,
HIGH,
IMMEDIATE
}
@Override
public int compareTo(Request<T> other) {
Priority left = this.getPriority();
Priority right = other.getPriority();
return left == right ? this.mSequence - other.mSequence : right.ordinal() - left.ordinal();
}
因为CacheDispatcher继承自Thread所以直接看其run方法
public void run() {
if (DEBUG) {
VolleyLog.v("start new dispatcher", new Object[0]);
}
Process.setThreadPriority(10);
//初始化缓存
this.mCache.initialize(); //2.1
while(true) {
while(true) {
while(true) {
while(true) {
try {
while(true) {
// Request没有添加到缓存队列前会被阻塞
final Request request = (Request)this.mCacheQueue.take();
request.addMarker("cache-queue-take");
// 如果在被添加到缓存队列前就已经取消了直接结束该请求
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
} else {
//尝试从缓存中获取key
Entry entry = this.mCache.get(request.getCacheKey()); //2.2
if (entry == null) {
request.addMarker("cache-miss");
this.mNetworkQueue.put(request); //添加到网络请求队列
}
// 如果缓存的内容已经完全过期了那么只能再请求网络,内部判断ttl与System.currentTimeMillis
// 满足ttl < System.currentTimeMillis()
else if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
// 设置一下过期的缓存内容,内部的etag,last_modify将会在下次请求时带给服务端
request.setCacheEntry(entry);
this.mNetworkQueue.put(request);
} else {
request.addMarker("cache-hit");
// 组装NetWorkResponse对象,给Request解析
Response<?> response = request.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders)); //2.3
request.addMarker("cache-hit-parsed");
// 判断softTtl < System.currentTimeMillis()
if (entry.refreshNeeded()) {
// softTtl已经大于当前时间,也可以直接分发这个响应,但是分发后还要再去请求一下网络
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// 标记当前的响应是中间态,可能该请求会触发两次响应
response.intermediate = true;
// 分发响应然后再把请求放入网络请求队列中
//2.4
this.mDelivery.postResponse(request, response, new Runnable() {
public void run() {
try {
CacheDispatcher.this.mNetworkQueue.put(request);
} catch (InterruptedException var2) {
;
}
}
});
} else {
// softTtl小于当前时间,那么缓存有效,直接分发响应
this.mDelivery.postResponse(request, response);
}
}
}
}
} catch (InterruptedException var4) {
//退出循环
if (this.mQuit) {
return;
}
}
}
}
}
}
}
this.mCache.initialize()
,在这里mCache对应于DiskBasedCachepublic synchronized void initialize() {
// 目录不存在就创建目录,由于目录都没有,所以缓存文件肯定也没有,直接返回
if (!this.mRootDirectory.exists()) {
if (!this.mRootDirectory.mkdirs()) {
VolleyLog.e("Unable to create cache dir %s", new Object[]{this.mRootDirectory.getAbsolutePath()});
}
} else {
//获取目录下所有文件
File[] files = this.mRootDirectory.listFiles();
if (files != null) {
File[] var5 = files;
int var4 = files.length;
for(int var3 = 0; var3 < var4; ++var3) {
File file = var5[var3];
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
DiskBasedCache.CacheHeader entry = DiskBasedCache.CacheHeader.readHeader(fis); //2.1.1
entry.size = file.length();
// 保存到mEntries注意这里LinedHashMap是有序的按照LRU的顺序排序
this.putEntry(entry.key, entry); //2.1.2
} catch (IOException var16) {
if (file != null) {
// readHeader抛出的IOException在这里被处理掉了,删除了文件
file.delete();
}
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException var15) {
;
}
}
}
}
}
}
public static DiskBasedCache.CacheHeader readHeader(InputStream is) throws IOException {
DiskBasedCache.CacheHeader entry = new DiskBasedCache.CacheHeader();
//读取4个字节组成一个int,如果该值不等于所谓的魔法数字就抛出异常
int magic = DiskBasedCache.readInt(is); //2.1.1.1
if (magic != 538051844) {
// 这里不用去删除它,最终会被外界捕获到异常将其删除
throw new IOException();
} else {
// 读取六个属性
//缓存键
entry.key = DiskBasedCache.readString(is); //2.1.1.3
entry.etag = DiskBasedCache.readString(is);
if (entry.etag.equals("")) {
entry.etag = null;
}
//保存时间
entry.serverDate = DiskBasedCache.readLong(is); //2.1.1.2
//生存时间
entry.ttl = DiskBasedCache.readLong(is);
entry.softTtl = DiskBasedCache.readLong(is);
//响应头
entry.responseHeaders = DiskBasedCache.readStringStringMap(is); //2.1.1.4
return entry;
}
}
//读取4个字节组成的一个int
static int readInt(InputStream is) throws IOException {
int n = 0;
int n = n | read(is) << 0;
n |= read(is) << 8;
n |= read(is) << 16;
n |= read(is) << 24;
return n;
}
//读取8个字节组成的一个long
static long readLong(InputStream is) throws IOException {
long n = 0L;
n |= ((long)read(is) & 255L) << 0;
n |= ((long)read(is) & 255L) << 8;
n |= ((long)read(is) & 255L) << 16;
n |= ((long)read(is) & 255L) << 24;
n |= ((long)read(is) & 255L) << 32;
n |= ((long)read(is) & 255L) << 40;
n |= ((long)read(is) & 255L) << 48;
n |= ((long)read(is) & 255L) << 56;
return n;
}
// 首先读取8个字节以用来确定后面那个字符串的长度,然后再读取该长度将其转化为字符串
static String readString(InputStream is) throws IOException {
int n = (int)readLong(is);
byte[] b = streamToBytes(is, n); //2.1.1.3.1
return new String(b, "UTF-8");
}
//从流中读取指定长度的字节,然后返回
private static byte[] streamToBytes(InputStream in, int length) throws IOException {
byte[] bytes = new byte[length];
int count;
int pos;
for(pos = 0; pos < length && (count = in.read(bytes, pos, length - pos)) != -1; pos += count) {
;
}
if (pos != length) {
throw new IOException("Expected " + length + " bytes, read " + pos + " bytes");
} else {
return bytes;
}
}
static Map<String, String> readStringStringMap(InputStream is) throws IOException {
//先读取有几个响应头
int size = readInt(is);
Map<String, String> result = size == 0 ? Collections.emptyMap() : new HashMap(size);
// 然后一个个读取出来放到result中
for(int i = 0; i < size; ++i) {
String key = readString(is).intern();
String value = readString(is).intern();
((Map)result).put(key, value);
}
return (Map)result;
}
private void putEntry(String key, DiskBasedCache.CacheHeader entry) {
if (!this.mEntries.containsKey(key)) {
this.mTotalSize += entry.size;
} else {
DiskBasedCache.CacheHeader oldEntry = (DiskBasedCache.CacheHeader)this.mEntries.get(key);
this.mTotalSize += entry.size - oldEntry.size;
}
this.mEntries.put(key, entry);
}
public synchronized Entry get(String key) {
DiskBasedCache.CacheHeader entry = (DiskBasedCache.CacheHeader)this.mEntries.get(key);
//缓存内容不存在返回null
if (entry == null) {
return null;
} else {
//根据key获取文件名然后创建一个File对象,创建文件名的规则是把key平分成两部分,分别计算hash然后拼接在一起
File file = this.getFileForKey(key); //2.2.1
DiskBasedCache.CountingInputStream cis = null;
try {
cis = new DiskBasedCache.CountingInputStream(new FileInputStream(file), (DiskBasedCache.CountingInputStream)null);
DiskBasedCache.CacheHeader.readHeader(cis);
//读取响应体
byte[] data = streamToBytes(cis, (int)(file.length() - (long)cis.bytesRead));
//将响应头与响应体进行组合返回一个Entry
Entry var7 = entry.toCacheEntry(data); //2.2.2
return var7;
} catch (IOException var15) {
VolleyLog.d("%s: %s", new Object[]{file.getAbsolutePath(), var15.toString()});
//读取失败也从Map中移除,并且将缓存文件删除,返回null
this.remove(key);
} finally {
if (cis != null) {
try {
cis.close();
} catch (IOException var14) {
return null;
}
}
}
return null;
}
}
public File getFileForKey(String key) {
return new File(mRootDirectory, getFilenameForKey(key));
}
private String getFilenameForKey(String key) {
int firstHalfLength = key.length() / 2;
String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode());
localFilename += String.valueOf(key.substring(firstHalfLength).hashCode());
return localFilename;
}
public Entry toCacheEntry(byte[] data) {
Entry e = new Entry();
e.data = data;
e.etag = this.etag;
e.serverDate = this.serverDate;
e.ttl = this.ttl;
e.softTtl = this.softTtl;
e.responseHeaders = this.responseHeaders;
return e;
}
//从请求头中读出charset然后把响应体转化为一个字符串
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException var4) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response)); //2.3.1
}
public static Entry parseCacheHeaders(NetworkResponse response) {
long now = System.currentTimeMillis();
Map<String, String> headers = response.headers;
long serverDate = 0L;
long serverExpires = 0L;
long softExpire = 0L;
long maxAge = 0L;
boolean hasCacheControl = false;
String serverEtag = null;
String headerValue = (String)headers.get("Date");
if (headerValue != null) {
// 将服务器返回的处理请求的时间转化为long
serverDate = parseDateAsEpoch(headerValue);
}
headerValue = (String)headers.get("Cache-Control");
if (headerValue != null) {
hasCacheControl = true;
String[] tokens = headerValue.split(",");
for(int i = 0; i < tokens.length; ++i) {
String token = tokens[i].trim();
//不用缓存直接返回null
if (token.equals("no-cache") || token.equals("no-store")) {
return null;
}
if (token.startsWith("max-age=")) {
try {
// 缓存的内容将在maxAge秒后失效
maxAge = Long.parseLong(token.substring(8));
} catch (Exception var19) {
;
}
// 本地副本过期前,可以使用本地副本;本地副本一旦过期,必须服务器进行有效性校验。
} else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
maxAge = 0L;
}
}
}
headerValue = (String)headers.get("Expires");
if (headerValue != null) {
// 获取服务器设置的缓存过期时间
serverExpires = parseDateAsEpoch(headerValue);
}
serverEtag = (String)headers.get("ETag");
//缓存控制头优先于Expires,因为前者限制性更强
if (hasCacheControl) {
softExpire = now + maxAge * 1000L;
} else if (serverDate > 0L && serverExpires >= serverDate) {
// 如果没有缓存控制头那么根据expires决定
softExpire = now + (serverExpires - serverDate);
}
Entry entry = new Entry();
entry.data = response.data;
entry.etag = serverEtag;
entry.softTtl = softExpire;
entry.ttl = entry.softTtl;
entry.serverDate = serverDate;
entry.responseHeaders = headers;
return entry;
}
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
// 也就是在主线程调用ResponseDeliveryRunnable.run
@Override
public void run() {
// If this request has canceled, finish it and don't deliver.
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// 判断发布是否成功
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result); //2.4.1
} else {
mRequest.deliverError(mResponse.error); //2.4.2
}
//softTtl过期Ttl不过期那么还需要执行mRunnable其实就是把Request加入到网络请求队列中去
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
// If we have been provided a post-delivery runnable, run it.
if (mRunnable != null) {
mRunnable.run();
}
}
protected void deliverResponse(String response) {
Response.Listener<String> listener;
synchronized (mLock) {
listener = mListener;
}
if (listener != null) {
listener.onResponse(response);
}
}
public void deliverError(VolleyError error) {
Response.ErrorListener listener;
synchronized (mLock) {
listener = mErrorListener;
}
if (listener != null) {
listener.onErrorResponse(error);
}
}
public void run() {
Process.setThreadPriority(10);
while(true) {
Request request;
while(true) {
try {
//当request没有添加到网络请求队列中会阻塞
request = (Request)this.mQueue.take();
break;
} catch (InterruptedException var4) {
if (this.mQuit) {
return;
}
}
}
}
可以继承Request 也可以直接使用StringRequest、JsonObjectRequest等,下面重点说几个方法
public String getCacheKey() {
return this.getUrl();
}
public Map<String, String> getHeaders() throws AuthFailureError {
return Collections.emptyMap();
}
protected Map<String, String> getParams() throws AuthFailureError {
return null;
}
RequestQueue.add 请求是否需要缓存决定把其放到缓存队列还是网络请求队列中
public Request add(Request request) {
// 标志着当前request属于哪个RequestQueue
request.setRequestQueue(this);
Set var2 = this.mCurrentRequests;
synchronized(this.mCurrentRequests) {
this.mCurrentRequests.add(request);
}
// 设置序列号从零开始0、1、2
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 {
this.mWaitingRequests.put(cacheKey, (Object)null);
this.mCacheQueue.add(request);
}
return request;
}
}
}
RequestQueue的add方法并没有进行网络或对缓存的操作,当请求添加金网络请求队列或者缓存队列时,在后台的网络调度线程和缓存调度线程会轮询各自的请求队列,如果发现请求任务需要处理则开始执行。下面分别来分析CacheDispatcher和NetworkDispatcher的源码
Volley源码解析二