Volley读取L2缓存时出现的问题

作者: 豆沙包67 | 来源:发表于2016-07-23 13:11 被阅读161次

    没有网络的情况下,Volley的L2(硬盘)缓存不起作用

    答案

    ****服务器的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的源码真值得反复看,琢磨,模仿。

    自己看源码,下面剖析源码,检查下面的问题

    • 看Volley是否实现L2缓存;
    • 什么时候存储L2缓存;
    • L2储存成功后是否使用

    问题1

        public staticRequestQueue newRequestQueue(Context context,HttpStack stack) {
            File cacheDir =newFile(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 =newHurlStack();
                }else{
                    stack =newHttpClientStack(AndroidHttpClient.newInstance(userAgent));
                }
            }
            BasicNetwork network1 =newBasicNetwork((HttpStack)stack);
            RequestQueue queue1 =newRequestQueue(newDiskBasedCache(cacheDir),network1);
            queue1.start();
            returnqueue1;   
        }
    

    可以看见,在调用
    Volley.newRequestQueue(Context)
    创建请求队列的时候,创建了L2缓存

    问题2

    public void start() {
        this.stop();
        this.mCacheDispatcher = newCacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
        this.mCacheDispatcher.start();
    }
    

    启动队列,关注CacheDispatcher和NetworkDispatcher
    思考,第一次(没有任何缓存状态),应该只有NetworkDispatcher起作用
    因此只关注NetworkDispatcher(是线程)

    public void run() {
        Process.setThreadPriority(10);
        //不断在请求队列取请求,忽略
        while (true) {
            Requestrequest;
            while (true) {
                try {
                    request = (Request) this.mQueue.take();
                    break;
                } catch (InterruptedExceptionvar4) {
                    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回复,重点看NetworkResponsee=this.mNetwork.performRequest(request);request.addMarker("network-http-complete");if(e.notModified&&request.hasHadResponseDelivered()){request.finish("not-modified");}else{
                    //转型成Volley的Response类型,重点看Responseresponse=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(VolleyErrorvar5) {this.parseAndDeliverNetworkError(request, var5);
        }catch(Exceptionvar6) {VolleyLog.e(var6, "Unhandled exception %s", newObject[]{
            var6.toString()
        });
        this.mDelivery.postError(request, newVolleyError(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)!
    ——————————————楔子——————————————————
    这个问题我们团队大概花了一周解决
    期间虽然进度拖慢了但收获颇丰
    期间也有同学提出用其他图片缓存框架,但是这种饮鸩止渴的方式怎么可

    相关文章

      网友评论

        本文标题:Volley读取L2缓存时出现的问题

        本文链接:https://www.haomeiwen.com/subject/gchwjttx.html