Volley应用与源码分析(一)介绍了Volley的两种图片加载方式
第一种加载方式与请求string、json等等方式一样,没有内存缓存,磁盘缓存受限于服务器。
第二种加载方式可以自定义内存缓存,磁盘缓存不受限服务器。
其实:第二种加载方式的本质,是和第一种方式一样,第二种方式是基于第一种方式的封装。所以,我们就分析第一种方式,如果看懂了第一种方式,第二种也就懂了
源码分析
那么,咱们先来看看第一种方式的源码,且看它,是如何做的。
第一步是获取队列(创建队列)
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
//获取磁盘缓存的路径
File cacheDir = new File(context.getCacheDir(), "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;
}
这里做的工作很多,例如:初始化网络请求方式,创建队列等等。
队列创建完之后,调用了队列start()方法。跟进去看看。
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里面,创建了磁盘缓存线程,然后创建了许多个网络请求线程。这里的this.mDispatchers.length默认是4,所以,默认情况,Volley一共有五个线程在后台跑,一个磁盘缓存线程,四个网络请求线程。
到这里,Volley就已经完成准备工作了。
然后我们创建Request,然后把Request添加到我们在刚刚创建的队列中。
rq.add(imageRequest);
就一行代码,但是里面却帮我们做了好多工作哦。
public Request add(Request request) {
.......
//如果不需要缓存,就直接把request添加到网络请求队列里
//默认是需要缓存滴。
if(!request.shouldCache()) {
this.mNetworkQueue.add(request);
return request;
} else {
//mWaitingRequest这个队列的意义是:如果请求是相同的(url相同),那么不好意思,请在当前的线程排队进行(因为有多个网络请求线程)
Map var7 = this.mWaitingRequests;
synchronized(this.mWaitingRequests) {
String cacheKey = request.getCacheKey();
if(this.mWaitingRequests.containsKey(cacheKey)) {
Object 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;
}
}
}
好,现在我们的请求在磁盘缓存的队列里了。那么,转移战场,到处理缓存队列的线程中。
public void run() {
if(DEBUG) {
VolleyLog.v("start new dispatcher", new Object[0]);
}
//设置进程优先级
Process.setThreadPriority(10);
//初始化磁盘缓存
this.mCache.initialize();
........
while(true) {
try {
while(true) {
final Request e = (Request)this.mCacheQueue.take();
e.addMarker("cache-queue-take");
if(e.isCanceled()) {
e.finish("cache-discard-canceled");
} else {
Entry entry = this.mCache.get(e.getCacheKey());
if(entry == null) {//没有缓存
e.addMarker("cache-miss");
this.mNetworkQueue.put(e);
} else if(entry.isExpired()) {//缓存过期
e.addMarker("cache-hit-expired");
e.setCacheEntry(entry);
this.mNetworkQueue.put(e);
} else {//有缓存并且不过期
e.addMarker("cache-hit");
Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
e.addMarker("cache-hit-parsed");
if(entry.refreshNeeded()) {
e.addMarker("cache-hit-refresh-needed");
e.setCacheEntry(entry);
response.intermediate = true;
this.mDelivery.postResponse(e, response, new Runnable() {
public void run() {
try {
CacheDispatcher.this.mNetworkQueue.put(e);
} catch (InterruptedException var2) {
;
}
}
});
} else {
this.mDelivery.postResponse(e, response);
}
}
}
}
} catch (InterruptedException var4) {
if(this.mQuit) {
return;
}
}
}
......
}
缓存线程做的事情其实非常简单,就是:
判断是否有缓存?没有,把请求交给网络请求线程
如果有缓存,那么缓存是否过期?过期,把请求交给网络请求线程
注:是否过期的判断的分析在Volley应用与源码分析(一)
如果有缓存并且不过期,就直接调用Request.parseNetworkResponse(“缓存”)方法。然后如果需要刷新缓存,就刷新缓存。
其实在网络请求线程里,当把数据请求下来后,也是调用Request.parseNetworkResponse(“数据”)方法,殊途同归。
网络线程:
public void run() {
Process.setThreadPriority(10);
while(true) {
Request request;
//等待请求
while(true) {
try{
request = (Request)this.mQueue.take();
break;
} catch (InterruptedException var4) {
if(this.mQuit) {
return;
}
}
}
try {
request.addMarker("network-queue-take");
if(request.isCanceled()) {
request.finish("network-discard-cancelled");
} else {
if(VERSION.SDK_INT >= 14) {
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
}
//请求数据
NetworkResponse e = this.mNetwork.performRequest(request);
request.addMarker("network-http-complete");
if(e.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
} else {
//调用parseNetworkResponse方法
Response response = request.parseNetworkResponse(e);
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 var5) {
this.parseAndDeliverNetworkError(request, var5);
} catch (Exception var6) {
VolleyLog.e(var6, "Unhandled exception %s", new Object[]{var6.toString()});
this.mDelivery.postError(request, new VolleyError(var6));
}
}
}
先通过我们一开始根据不同的版本创建的不同请求网络方式去请求数据。然后把数据传入Request.parseNetworkResponse(“数据”)中。
殊途同归,与缓存线程是一样的!!!
好,去看看Request中看看parseNetworkResponse(“数据”)方法
一看,是抽象方法。这个方法的其实就是处理数据,而不同的数据处理的方式不一样,所以让子类的去实现。既然我们一直拿ImageRequest来分析,就去看看ImageRequest。
private Response doParse(NetworkResponse response) {
byte[] data = response.data;
/*
这里吧data转化成bitmap
*/
//这里调用了回调方法
return bitmap == null?Response.error(new ParseError(response))
:Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response));
}
里面调用了doParse()方法,看看它。
private Response doParse(NetworkResponse response) {
byte[] data = response.data;
/*
这里吧data转化成bitmap
*/
//这里调用了回调方法
return bitmap == null?Response.error(new ParseError(response))
:Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response));
}
这里简单做数据处理,然后回调不同的方法。
注:可能有些新手不知道回调是回调到到哪里,我们在创建Request的时候,会new 两个监听,一个是成功的监听,一个是失败的监听。就是回调我们创建的监听里面。
Volley有强大可扩展性,我们只需要继承Request,实现Request的抽象方法,并且做好数据的处理,就可以拥有自己的Request了!
对于Volley的分析就这里了。下下个星期我应该在学校了。我也应该用心准备一下校招了。接下来的,我的文章将更多关注校招的准备,例如:算法题目的分析,校招知识点的罗列。
为了校招,两周一次的博客在短期内更新为一周一次,下周,八大排序的分析。
最后,想说一点小感想。一开始写博客的原因无法是:
①、一般而言,优秀的工程师都是有博客的,当然,我算不上优秀,但是写博客可以让我通向优秀。
②、博客是最好的简历。
而一个人在大城市居住,孤独感总是随身而行。平时消耗孤独的方式很多,比如阅读,看岛国动作片,看奇葩说,运动。现在发现,写博客,其实也是一种对抗孤独的方式。坚持下去吧,就凭这一点。