Volley的使用参考郭大神的博客和鸿洋大神的博客,最近几天一直在学习Volley框架,这个是google推荐的。已经封装的相当不错了,至少对于一些轻量级的网络请求是很有优势的,比如接口请求。我们只要直接去使用就好了。
首先我们知道的就是Volley的核心
RequestQueue
Request
我们先来看看创建RequestQueue对象:
要创建一个RequestQueue对象,
RequestQueue mQueue = Volley.newRequestQueue(context);
那么为什么不是通过new的方法呢?我们也可以顺便跟着去看看源码,
我们首先进入的是一个Volley类,我们可以看到我们真正调用的是这个方法:
/** 创建一个线程池,并且通过RequestQueue#start()来启动 * * @param context A {@link Context} to use for creating the cache dir. * @param stack An {@link HttpStack} to use for the network, or null for default. * @return A started {@link RequestQueue} instance. */
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getFilesDir(), "volley");
String userAgent = "volley/0";
try {
String network = context.getPackageName();
PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
userAgent = network + "/" + queue.versionCode;
} catch (NameNotFoundException var6) {
;
}
if(stack == null) {
if(VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
BasicNetwork network1 = new BasicNetwork((HttpStack)stack);
RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
queue1.start();
return queue1;
}
/** * 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的作用就是简单的创建RequestQueue对象,它需要传一个context用来创建缓存文件。还有传一个HttpStack 实例参数,这个实例只是一个方法performRequest,通过网络请求后返回HttpResponse对象。我们在newRequestQueue的时候,直接传这个参数为null,那么它就会通过判断API版本,来决定是使用HurlStack还是HttpClientStack。至于这两个概念,我们只需知道HurlStack内部使用HttpURLConnection执行网络请求,HttpClientStack内部使用HttpClient执行网络请求,详细参考博客。然后通过stack,创建一个BasicNetwork ,它是实现Network的一个实例,也是和网络请求打交道的实例。Network的performRequest和HttpStack的performRequest区别NetWork返回的NetworkResponse是通过封装HttpStack.performRequest得到的。我们先来看下BasicNetwork做了哪些事。先从performRequest来看
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
//系统启动到现在的时间,
long requestStart = SystemClock.elapsedRealtime();
while(true) {
HttpResponse httpResponse = null;
Object responseContents = null;
Map responseHeaders = Collections.emptyMap();
try {
HashMap e = new HashMap();
//1、缓存请求的时候,添加Cache接口Entry类封装头部信息
this.addCacheHeaders(e, request.getCacheEntry());
//2、调用 HttpStack 对象去网络中获取数据,返回httpResonse 对象。
httpResponse = this.mHttpStack.performRequest(request, e);
//3、根据状态编码来返回不同的Response对象,如304(未修改)就返回缓存中的数据,如果不是,则根据响应中的数据,重新构造一个NetworkResponse对象。
StatusLine statusCode2 = httpResponse.getStatusLine();
int networkResponse1 = statusCode2.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
if(networkResponse1 != 304) {
byte[] responseContents1;
if(httpResponse.getEntity() != null) {
responseContents1 = this.entityToBytes(httpResponse.getEntity());
} else {
responseContents1 = new byte[0];
}
long requestLifetime1 = SystemClock.elapsedRealtime() - requestStart;
this.logSlowRequests(requestLifetime1, request, responseContents1, statusCode2);
if(networkResponse1 >= 200 && networkResponse1 <= 299) {
return new NetworkResponse(networkResponse1, responseContents1, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
}
throw new IOException();
}
Entry requestLifetime = request.getCacheEntry();
if(requestLifetime == null) {
return new NetworkResponse(304, (byte[])null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
}
requestLifetime.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(304, requestLifetime.data, requestLifetime.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
} catch (SocketTimeoutException var12) {
//4、BasicNetwork实现了重试的机制,如果第一次从网络获取失败,默认会重新再尝试一次,如果失败,则会将Error返回,默认的实现类是DefaultRetryPolicy类。
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) {
boolean statusCode = false;
NetworkResponse networkResponse = null;
if(httpResponse == null) {
throw new NoConnectionError(var15);
}
int statusCode1 = httpResponse.getStatusLine().getStatusCode();
VolleyLog.e("Unexpected response code %d for %s", new Object[]{Integer.valueOf(statusCode1), request.getUrl()});
if(responseContents == null) {
throw new NetworkError(networkResponse);
}
networkResponse = new NetworkResponse(statusCode1, (byte[])responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
if(statusCode1 != 401 && statusCode1 != 403) {
throw new ServerError(networkResponse);
}
attemptRetryOnException("auth", request, new AuthFailureError(networkResponse));
}
}
}
然后再通过传递network参数来创建RequestQueue。其中还有一个参数我们可以看到Volley中使用Cache接口的子类DiskBasedCache做缓存,这是一个文件缓存,磁盘默认为5M。Cache接口有一个initialize方法用来初始化缓存,这个方法可能会执行耗时操作,需要在后台线程中执行。这个方法会把每一个网络请求根据cacheKey一一对应的头部信息缓存在内存中的文件,然后通过cacheKey来获取这些一一对应的头部信息。我们就来看看头部保存了一些什么?
//缓存文件的大小
public long size;
//用于一一对应网络请求的唯一标识
public String key;
//http请求缓存相关的标识
public String etag;
//服务器返回数据的时间
public long serverDate;
//最后一次缓存时间
public long lastModified;
//缓存过期时间,默认softTtl和ttl相同
public long ttl;
// 缓存新鲜度时间
public long softTtl;
// 服务器还回来的头部信息
public Map<String, String> responseHeaders;
然后我们再来看看缓存内容,再Cache接口中
//服务器返回数据
public byte[] data;
//下面同上
public String etag;
public long serverDate;
public long lastModified;
public long ttl;
public long softTtl;
public Map<String, String> responseHeaders = Collections.emptyMap();
看完了要保存的东西,我们在来看看缓存的方法
public synchronized void put(String key, Entry entry) {
//判断是否有足够的缓存空间来缓存新的数据
this.pruneIfNeeded(entry.data.length);
//创建一个缓存文件
File file = this.getFileForKey(key);
try {
FileOutputStream deleted1 = new FileOutputStream(file);
//用entry里面的数据,再封装成一个CacheHeader
DiskBasedCache.CacheHeader e = new DiskBasedCache.CacheHeader(key, entry);
//写入头部缓存信息
boolean success = e.writeHeader(deleted1);
if(!success) {
//缓存不成功头部处理
deleted1.close();
VolleyLog.d("Failed to write header for %s", new Object[]{file.getAbsolutePath()});
throw new IOException();
} else {
//缓存头部成功后再写缓存内容
deleted1.write(entry.data);
deleted1.close();
this.putEntry(key, e);
}
} catch (IOException var7) {
boolean deleted = file.delete();
if(!deleted) {
VolleyLog.d("Could not clean up file %s", new Object[]{file.getAbsolutePath()});
}
}
}
详细缓存机制参考博客
再来看下RequestQueue对象,
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();
}
}
start方法里面,有缓存线程和网络线程这两个线程,如果网络请求没有缓存或者缓存没有记录或者缓存过期的话,那就创建一个网络线程请求NetworkDispatcher 。
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request<?> request;
while (true) {
try {
// 1、调用 mQueue的take()方法从队列中获取请求,如果没有请求,则一直阻塞在那里等待,直到队列中有新的请求到来。
request = mQueue.take();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
// 2、判断请求有没有被取消,如果被取消,则重新获取请求。
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
// 3、调用Network对象将请求发送到网络中,并返回一个 NetworkResponse对象。
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 如果服务器返回一个未修改(304)的响应,并且这个请求已经发送过响应对象,不需要再继续,因为没改过
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// 4、调用请求的pareseNetworkResonse方法,将NetworkResponse对象解析成相对应的Response对象。
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// 5、判断请求是否需要缓存,如果需要缓存,则将其Response中cacheEntry对象放到缓存mCache中。
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// /6、调用 mDelivery将Response对象传到主线程中进行UI更新。
request.markDelivered();
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
parseAndDeliverNetworkError(request, volleyError);//有错误,也会调用到mDelivery,将错误信息传回到主线程,进行提示
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
mDelivery.postError(request, new VolleyError(e));
}
}
}
最后附上思维图