码迷,mamicode.com
首页 > 移动开发 > 详细

[Android]Volley在没有网络时读取图片缓存时出现的问题

时间:2015-08-09 14:09:47      阅读:254      评论:0      收藏:0      [点我收藏+]

标签:volley   缓存   url   加载图片   

Volley框架实现了L2缓存,却没有实现L1缓存。

我们团队遇到的问题是

当imgurl为类似于“XXX,com/XXXX/XXXX.png”时能完成加载

当imgurl为“192.168.XXX.XXX:8080/XX”时总是加载错误


经过一周的排查发现

只要在
服务器的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的基本用法
developer.android.com/t
.....
以上花了大概两三天都扫一遍
除了感慨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)
创建请求队列的时候,创建了L2缓存

问题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();
        }

    }
启动队列,关注CacheDispatcher和NetworkDispatcher

思考,第一次(没有任何缓存状态),应该只有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));
            }
        }
    }
原来重点在request.shouldCache()l&&response.cacheEntry != nul判断是否缓存!
继续点进去
    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();
    }
cacheEntry 是继承Request时需要重写的,例如StringRequest
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);
//省略部分代码....
L2缓存是通过请求url作为cache-key来储存的。
所以entry.isExpired()是重点!!!
!!!!
如果entry.isExpired()为true,则返回缓存,而false则进入网络请求队列继续网络请求
!!!!
   public boolean isExpired() {
            return this.ttl < System.currentTimeMillis();
        }

ttl!ttl!ttl!
结合上面那个网址,结合ttl!学了计算机那么久总能猜到是缓存的生命周期(time to live)!


——————————————楔子——————————————————
这个问题我们团队大概花了一周解决
期间虽然进度拖慢了但收获颇丰
期间也有同学提出用其他图片缓存框架,但是这种饮鸩止渴的方式怎么能用在程序圆身上


版权声明:本文为博主原创文章,未经博主允许不得转载。

[Android]Volley在没有网络时读取图片缓存时出现的问题

标签:volley   缓存   url   加载图片   

原文地址:http://blog.csdn.net/qq284565035/article/details/47375277

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!