Volley的简单使用
- Volley是2013年google退出的网络框架,他的优点是适合进行数据量不大但频繁的通信,对于一些数据量大的网络请求却是表现的非常差
- 要使用Volley请求网络,我们需要下载到他的jar包,在项目下add
- 他使用队列来管理网络请求,接下来看一下Volley的简单用法
- 先创建管理网络请求的队列
RequestQueue queue = Volley.newRequestQueue(appContext);
- 它提供可使用的请求有好几个,StringRequest,IsonRequest,ImageRequest,等,我们这次就简单的使用一下StringRequest来创建一个网络请求吧
StringRequest stringRequest = new StringRequest(Request.Method.GET, "https://tieba.baidu.com/index.html"
, new Response.Listener<String>() {
@Override
public void onResponse(String s) {
Log.d(TAG, "onResponse: 请求成功" + s);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Log.d(TAG, "onErrorResponse: 请求失败");
}
});
- 可以看到,参数分别是请求的方式,url,以及请求成功的回调,和请求失败的回调
- 然后将请求添加到队列就可以了
queue.add(stringRequest)
- 简单的使用就到这里,其他的比如Json请求也差不多,这里就不在举例
Volley源码分析
- 我们就直接按照他的使用步骤来分析
- 先看创建队列方法,几个构造器最终调用的是三个参数的构造方法
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) {
;
}
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;
}
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();
}
}
- 好了,队列创建出来了,就该创建我们自己的请求了,然后添加到队列当中,来看一下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 var8 = 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;
}
}
}
private final Map<String, Queue<Request<?>>> mWaitingRequests;
private final Set<Request<?>> mCurrentRequests;
private final PriorityBlockingQueue<Request<?>> mCacheQueue;
private final PriorityBlockingQueue<Request<?>> mNetworkQueue;
- 这里我只摘出部分变量,可以看到,有一个正在等待Map,有一个当前请求的set,有一个缓存队列,有一个网络队列
- 这里再捋一下这个思路,想要将一个请求添加到队列当中
- 先看看这个请求可不可以缓存,如果不可以缓存,则直接添加到网络队列
- 如果可以缓存,再看这个请求是否已经包含在正在等待的Map列里面,如果已经包含,那么得到这个请求所对应的队列,对这个队列处理之后,然后将这个请求放在该队列,然后重新将这个队列和这个请求以键值对的方式放在正在等待的map当中
- 如果不包含在正在等待的map当中的话,直接将这个请求和一个值为空的队列以键值对的方式当在正在等待的Map当中,同时将这个请求放在缓存队列当中
- 最后,返回这个请求
- 可见,我们每次add的时候,要么add进网络队列,要么add进正在等待的map,而当add进这个map的时候,总会创建一个对列(LinkedList)与之对应
- 那么,现在添加进对列了,接下来该怎么办,当然是看我们的缓存线程和网络调度线程的工作啦
- 先看缓存调度线程的run方法
public void run() {
if(DEBUG) {
VolleyLog.v("start new dispatcher", new Object[0]);
}
Process.setThreadPriority(10);
this.mCache.initialize();
while(true) {
final Request request;
while(true) {
request = null;
try {
request = (Request)this.mCacheQueue.take();
break;
} catch (InterruptedException var6) {
if(this.mQuit) {
return;
}
}
}
try {
request.addMarker("cache-queue-take");
if(request.isCanceled()) {
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()) {
this.mDelivery.postResponse方法(request, response);
} else {
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) {
;
}
}
});
}
}
}
} catch (Exception var5) {
VolleyLog.e(var5, "Unhandled exception %s", new Object[]{var5.toString()});
}
}
}
- 那么现在来看看他是怎么将数据回去主线程的,这里的mDelivery是来自ExecutorDelivery类的实例,来看看他的postResponse方法
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
this.mResponsePoster.execute(new ExecutorDelivery.ResponseDeliveryRunnable(request, response, runnable));
}
- 这里的this.mResponsePoster就是当我们创建实例的时候传进去的继承自Handler对象,看一下
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
public ExecutorDelivery(final Handler handler) {
this.mResponsePoster = new Executor() {
public void execute(Runnable command) {
handler.post(command);
}
};
}
- 看到了没,把Handler传进来之后Handler在实现的Executor接口内部的execute方法中调用了一下handler.post(command);我们再回到postResponse方法
- 我们的主线程Handler.post方法其实拿到的是这个ExecutorDelivery.ResponseDeliveryRunnable实例参数
- 这里就是已经用Handler将我们的数据发送到了主线程了
- 在这里我们看看这个继承自Runable的ExecutorDelivery.ResponseDeliveryRunnable的run方法
public void run() {
if(this.mRequest.isCanceled()) {
this.mRequest.finish("canceled-at-delivery");
} else {
if(this.mResponse.isSuccess()) {
this.mRequest.deliverResponse(this.mResponse.result);
} else {
this.mRequest.deliverError(this.mResponse.error);
}
if(this.mResponse.intermediate) {
this.mRequest.addMarker("intermediate-response");
} else {
this.mRequest.finish("done");
}
if(this.mRunnable != null) {
this.mRunnable.run();
}
}
}
- 这里看到了吧,失败还是成功,在这里回调就好
- 看完了缓存调度线程,再来看网络调度线程的run方法
public void run() {
Process.setThreadPriority(10);
while(true) {
Request request;
long startTimeMs;
while(true) {
startTimeMs = SystemClock.elapsedRealtime();
request = null;
try {
request = (Request)this.mQueue.take();
break;
} catch (InterruptedException var6) {
if(this.mQuit) {
return;
}
}
}
try {
request.addMarker("network-queue-take");
if(request.isCanceled()) {
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);
}
}
}
- 这个方法的内部其实跟缓存调度线程是差不多的,这里不再具体分析
- 这里我们再进去看一下具体的网络请求代码,这个方法this.mNetwork.performRequest(request)
- 这里的netWork就是我们在创建队列的时候根据不同版本创建的,再来看一下
if(stack == null) {
if(VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork((HttpStack)stack);
- 根据不同的版本创建不同的请求方式,我们就直接看第一个,因为在BasicNetwork里面调用的也是他的栈参数的方法,看下
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);
...
}
}
}
- 方法比较长,这里只截取到开头,我们可以看到他调用的还是参数栈的方法来请求网络,我们继续来看他的栈的这个方法
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
String url = request.getUrl();
...
URL parsedUrl = new URL(url);
HttpURLConnection connection = this.openConnection(parsedUrl, request);
Iterator i$ = map.keySet().iterator();
while(i$.hasNext()) {
String headerName = (String)i$.next();
connection.addRequestProperty(headerName, (String)map.get(headerName));
}
setConnectionParametersForRequest(connection, request);
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if(responseCode == -1) {
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
} else {
StatusLine responseStatus = new BasicStatusLine(protocolVersion, connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
if(hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
response.setEntity(entityFromConnection(connection));
}
Iterator i$ = connection.getHeaderFields().entrySet().iterator();
while(i$.hasNext()) {
Entry<String, List<String>> header = (Entry)i$.next();
if(header.getKey() != null) {
Header h = new BasicHeader((String)header.getKey(), (String)((List)header.getValue()).get(0));
response.addHeader(h);
}
}
return response;
}
}
- 在最开始就能看到,这里用的是HttpURLConnection来请求网络的
- 那么到这里Volley的分析已经完了,其实说起来很简单,就是不停的将请求添加到队列,然后在队列里面有两个线程不停的对这些请求做处理,返回主线程,根据结果回调主线程的方法,这个就是Volley的全部过程了
- 其实这个Volley也就这样,重点理解他的过程
总结
- 因为Volley的优点是响应频繁但数据量不大的网络请求,所以他用一个队列来管理这些个请求
- 他设置了一个缓存队列,这个方式有效的缓解了频繁的网络请求的压力
- 同时传入主线程Handler,使得最后的传出数据不那么复杂
- 虽然分缓存调度和网络调度,但是共用的是一套数据