[Android]Volley在没有网络时读取图片缓存时出现的问题
来源:互联网 发布:淘宝卖家基本设置出错 编辑:程序博客网 时间:2024/04/30 15:55
Volley框架实现了L2缓存,却没有实现L1缓存。
我们团队遇到的问题是
当imgurl为类似于“http://www.XXX,com/XXXX/XXXX.png”时能完成加载
当imgurl为“http://192.168.XXX.XXX:8080/XXX/XXX.png”时总是加载错误
经过一周的排查发现
只要在
服务器的Response头中加入'cache-control:public, max-age=43200'(只是示例)
———————————原理—————————————————
简单来说
Volley框架考虑的东西实在太全面了
终端(手机或浏览器)的缓存策略是由服务器来制定的
而Volley在做缓存之前会判断服务器是否允许自己做这个缓存
服务器缓存在缺省情况下,是不让终端做缓存的
更多关于缓存的资料,请移步
HTTP 缓存 — Web Fundamentals-
———————————求解过程———————————————
这个问题我们团队之前也有遇到
Google得到的信息是Volley已实现L2缓存(基于硬盘),需要自己实现L1缓存(基于内存)。
既然L2缓存已经实现,那没网的情况下是可以加载图片的。
有个小伙伴发现,用不同的url会影响L2缓存的实现。
表示不服,看源码,先看别人写过的源码解析
Volley 源码解析
Android 网络通信框架Volley简介(Google IO 2013)
Android Volley完全解析(一),初识Volley的基本用法
https://developer.android.com/training/volley/index.html
.....
以上花了大概两三天都扫一遍
除了感慨Volley写得游刃有余之外,然并卵。
建议Volley的源码真值得反复看,琢磨,模仿。
不服
自己看源码,下面列源码
在扫过上述别人的分析后,对自己的问题的关键有所了解
我决定:
1.看Volley是否实现L2缓存;
2.什么时候存储L2缓存;
3.L2储存成功后是否使用
问题1
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) { ; } //省略代码 //这个创建了L2缓存文件夹 RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1); return queue1; }
Volley.newRequestQueue(Context)
问题2
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(); } }
思考,第一次(没有任何缓存状态),应该只有NetworkDispatcher起作用
因此只关注NetworkDispatcher(是线程)
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()); } //得到Response回复,重点看 NetworkResponse e = this.mNetwork.performRequest(request); request.addMarker("network-http-complete"); if(e.notModified && request.hasHadResponseDelivered()) { request.finish("not-modified"); } else { //转型成Volley的Response类型,重点看 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)); } } }
继续点进去
public final boolean shouldCache() { return this.mShouldCache; }
public Request(int method, String url, ErrorListener listener) { this.mEventLog = MarkerLog.ENABLED?new MarkerLog():null; this.mShouldCache = true; //!!!!!!!!!!!!!!!!!!!!!!!!! this.mCanceled = false; this.mResponseDelivered = false; this.mRequestBirthTime = 0L; this.mCacheEntry = null; this.mMethod = method; this.mUrl = url; this.mErrorListener = listener; this.setRetryPolicy(new DefaultRetryPolicy()); this.mDefaultTrafficStatsTag = TextUtils.isEmpty(url)?0:Uri.parse(url).getHost().hashCode(); }
parsed = new String(response.data,HttpHeaderParser.parseCharset(response.headers));
好!这个结果也是预料之中。
因为使用Android模拟器的时候,也是可以在/data/data/包名/volley/...目录下有图片文件生成
继续看调用缓存的时候发生了什么。
问题3
代码有点多,直接贴最关键的
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);//省略部分代码....
所以entry.isExpired()是重点!!!
!!!!
如果entry.isExpired()为true,则返回缓存,而false则进入网络请求队列继续网络请求
!!!!
public boolean isExpired() { return this.ttl < System.currentTimeMillis(); }
ttl!ttl!ttl!
结合上面那个网址,结合ttl!学了计算机那么久总能猜到是缓存的生命周期(time to live)!
——————————————楔子——————————————————
这个问题我们团队大概花了一周解决
期间虽然进度拖慢了但收获颇丰
期间也有同学提出用其他图片缓存框架,但是这种饮鸩止渴的方式怎么能用在程序圆身上
- [Android]Volley在没有网络时读取图片缓存时出现的问题
- 使用Volley缓存图片时,缓存无效的原因。
- Listview中图片加载使用Volley框架时,出现图片显示不正确的问题的解析
- 用ImageLoader缓存,volley网络请求百度api的图片显示在listview(demo)
- Android Volley框架的使用之图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)
- Android Volley框架的使用(四)图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)
- Android Volley框架的使用(四)图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)
- Android Volley框架的使用(四)图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)
- Android Volley框架的使用(四)图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)
- Android Volley框架的使用(四)图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)
- Android Volley框架的使用(四)图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)
- Android读取网络图片并缓存
- 20.获取网络图片,将图片存储在文件,缓存中,然后先从缓存中读取,没有再从文件中读取
- UIimage更新图片时出现缓存问题
- Android开发之搜芽项目的图片加载问题(使用Volley进行网络图片加载)
- Linux 父子进程在没有同步时出现的问题
- Android Volley Cache出现的问题
- Android Volley,使用Volley加载网络图片
- 剑指Offer面试题34(java版):丑数
- 在Fedora22环境下编译调试linux 0.12
- osgi5——camel整合activiti
- pat(B) 1002. 写出这个数
- UE4发报机-关卡蓝图调用蓝图类的发报机
- [Android]Volley在没有网络时读取图片缓存时出现的问题
- LDS文件格式分析
- x86架构和arm架构处理器分析
- mybatis 源码系列 组件之 Transaction
- 百度编辑器多图上传返回图片绝对路径问题
- Oracle 表连接 导致排序错乱
- vi的复制粘贴命令
- linux 信号signal和sigaction理解
- Andoid之——缓存杂项