天天看點

volley 緩存清理不幹淨

公司做的架構使用的volley,這裡做一下圖檔緩存的說明,實作儲存圖檔,擷取圖檔,清理單個圖檔緩存和清理全部圖檔緩存,但是在使用的過程中,發現清理了緩存,圖檔緩存依然存在,為什麼呢,通過檢視源碼,發現,volley不僅有記憶體存儲,還有sd卡存儲,隻是這個是封裝起來的,需要自己去找到,并進行操作

使用步驟:

1.首先建立一個隊列:

private RequestQueue mRequestQueue;      
if (mRequestQueue == null) {
    mRequestQueue = Volley.newRequestQueue(context);
}      

2.使用json作為參數做網絡請求

JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.POST, url, jsonParams,
                new Response.Listener<JSONObject>() {

                    @Override
                    public void onResponse(JSONObject response) {
                       
                }, new Response.ErrorListener() {

            @Override
            public void onErrorResponse(VolleyError error) {
              
}

        }) {
            //添加請求頭
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> headers = new HashMap<String, String>();
                headers.put("Token", Token.getToken());
                return headers;
            }
        };
      

3.将這個jsonObjectRequest()放進隊列

mRequestQueue.add(jsonObjReq);      

調用的時候,直接給jsonObject傳對應的參數即可

這裡需要加載圖檔的方法是:

1.首先建立一個記憶體類,去繼承LruCache  ,

public class BitMapCache implements ImageLoader.ImageCache {
    private LruCache<String, Bitmap> cache;

    private static BitMapCache bitMapCache;

    private BitMapCache() {
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        cache = new LruCache<String, Bitmap>(8 * 1024 * 1024) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight();
            }
        };
    }

    public static BitMapCache getInstance() {
        if (bitMapCache == null) {
            bitMapCache = new BitMapCache();
        }
        return bitMapCache;
    }

    @Override
    public Bitmap getBitmap(String url) {
        return cache.get(url);
    }

    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        cache.put(url, bitmap);
    }

    public int getBitmapCacheSize() {
        return cache.size();
    }

    public void removeBitmapSize() {
        cache.evictAll();
    }

    public void clearBitmap(String url, int maxWidth, int maxHeight) {
        cache.remove(getCacheKey(url, maxWidth, maxHeight));
    }

    //同ImageLoader裡私有方法
    private static String getCacheKey(String url, int maxWidth, int maxHeight) {
        return (new StringBuilder(url.length() + 12)).append("#W").append(maxWidth).append("#H").append(maxHeight).append(url).toString();
    }      

這個緩存圖檔類裡面實作了儲存圖檔,擷取圖檔,清理單個圖檔緩存和清理全部圖檔緩存,但是在使用的過程中,發現清理了緩存,圖檔緩存依然存在,為什麼呢,通過檢視源碼,發現,volley不僅有記憶體存儲,還有sd卡存儲,隻是這個是封裝起來的,需要自己去找到,并進行操作

2.将記憶體緩存放進Imageloader,在執行個體化隊列後,緊跟着執行個體化Imageloader,并設定了記憶體緩存大小

if (mImageLoader == null) {
    mImageLoader = new ImageLoader(mRequestQueue, BitMapCache.getInstance());
}      

3.實作将圖檔顯示的方法

public void diaplayHeadImage(Context context, String url, final ImageView imageView, final int width, int height) {
    ImageLoader.ImageListener listener = new ImageLoader.ImageListener() {
        @Override
        public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
            if (response.getBitmap() != null) {
                Bitmap bm = response.getBitmap();
                if (width > 0) {
                    int bmWidth = bm.getWidth();
                    int bmHeight = bm.getHeight();
                    float scale = (float) width / bmWidth;
                    Matrix matrix = new Matrix();
                    matrix.postScale(scale, scale);
                    // 得到新的圖檔
                    bm = Bitmap.createBitmap(bm, 0, 0, bmWidth, bmHeight, matrix, true);
                }
                if (imageView.getTag() == null) {
                    imageView.setImageBitmap(bm);
                } else if (response.getRequestUrl().contains(imageView.getTag().toString())) {
                    imageView.setImageBitmap(bm);
                }
            } else {
                //set error image
                imageView.setImageResource(R.mipmap.img_default);
            }
        }

        @Override
        public void onErrorResponse(VolleyError volleyError) {
            //set error image
            imageView.setImageResource(R.mipmap.img_default);
        }
    };
    //有網再清理緩存
    if (Utils.IsNetworkAvailble(context)) {
        //清理圖檔緩存,記憶體緩存和磁盤緩存
        BitMapCache.getInstance().clearBitmap(url, 0, 0);
        mRequestQueue.getCache().remove(url);
        //  mRequestQueue.getCache().clear();
    }

    if (width != 0 || height != 0) {
        //指定圖檔允許的最大寬度和高度
        mImageLoader.get(url, listener, width, height);
    } else {
        mImageLoader.get(url, listener);
    }
}      

4.這裡明顯沒有存儲到記憶體卡的步驟,那麼圖檔是什麼存儲到記憶體卡的,點選

mImageLoader.get進源碼

       
public ImageLoader.ImageContainer get(String requestUrl, ImageLoader.ImageListener imageListener, int maxWidth, int maxHeight) {
    this.throwIfNotOnMainThread();
    final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);
    Bitmap cachedBitmap = this.mCache.getBitmap(cacheKey);
    ImageLoader.ImageContainer imageContainer;
    if(cachedBitmap != null) {
        imageContainer = new ImageLoader.ImageContainer(cachedBitmap, requestUrl, (String)null, (ImageLoader.ImageListener)null);
        imageListener.onResponse(imageContainer, true);
        return imageContainer;
    } else {
        imageContainer = new ImageLoader.ImageContainer((Bitmap)null, requestUrl, cacheKey, imageListener);
        imageListener.onResponse(imageContainer, true);
        ImageLoader.BatchedImageRequest request = (ImageLoader.BatchedImageRequest)this.mInFlightRequests.get(cacheKey);
        if(request != null) {
            request.addContainer(imageContainer);
            return imageContainer;
        } else {
            ImageRequest newRequest = new ImageRequest(requestUrl, new Listener() {
                public void onResponse(Bitmap response) {
                    ImageLoader.this.onGetImageSuccess(cacheKey, response);
                }
            }, maxWidth, maxHeight, Config.RGB_565, new ErrorListener() {
                public void onErrorResponse(VolleyError error) {
                    ImageLoader.this.onGetImageError(cacheKey, error);
                }
            });
            this.mRequestQueue.add(newRequest);
            this.mInFlightRequests.put(cacheKey, new ImageLoader.BatchedImageRequest(newRequest, imageContainer));
            return imageContainer;
        }
    }
}      

沒有找到,但是發現了即使

cachedBitmap==null和網絡斷開的情況下,仍然會有圖檔展示,是以檢視這裡肯定走了彎路,那就從開頭再找,發現在        
mRequestQueue = Volley.newRequestQueue(context);這裡點進去 Volley類

       
public class Volley {
    private static final String DEFAULT_CACHE_DIR = "volley";

    public Volley() {
    }

    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;
    }

    public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, (HttpStack)null);
    }
}      

藍色字型顯示,在new隊列的時候,裡面出現個緩存  DiskBasedCache(cacheDir)類,覺得自己找到了方向,進入這個類

public class DiskBasedCache implements Cache {
    private final Map<String, DiskBasedCache.CacheHeader> mEntries;
    private long mTotalSize;
    private final File mRootDirectory;
    private final int mMaxCacheSizeInBytes;
    private static final int DEFAULT_DISK_USAGE_BYTES = 5242880;
    private static final float HYSTERESIS_FACTOR = 0.9F;
    private static final int CACHE_VERSION = 2;

    public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) {
        this.mEntries = new LinkedHashMap(16, 0.75F, true);
        this.mTotalSize = 0L;
        this.mRootDirectory = rootDirectory;
        this.mMaxCacheSizeInBytes = maxCacheSizeInBytes;
    }

    public DiskBasedCache(File rootDirectory) {
        this(rootDirectory, 5242880);
    }

    public synchronized void clear() {
        File[] files = this.mRootDirectory.listFiles();
        if(files != null) {
            File[] var5 = files;
            int var4 = files.length;

            for(int var3 = 0; var3 < var4; ++var3) {
                File file = var5[var3];
                file.delete();
            }
        }

        this.mEntries.clear();
        this.mTotalSize = 0L;
        VolleyLog.d("Cache cleared.", new Object[0]);
    }

    public synchronized Entry get(String key) {
        DiskBasedCache.CacheHeader entry = (DiskBasedCache.CacheHeader)this.mEntries.get(key);
        if(entry == null) {
            return null;
        } else {
            File file = this.getFileForKey(key);
            DiskBasedCache.CountingInputStream cis = null;

            try {
                cis = new DiskBasedCache.CountingInputStream(new FileInputStream(file), (DiskBasedCache.CountingInputStream)null);
                DiskBasedCache.CacheHeader.readHeader(cis);
                byte[] e = streamToBytes(cis, (int)(file.length() - (long)cis.bytesRead));
                Entry var7 = entry.toCacheEntry(e);
                return var7;
            } catch (IOException var15) {
                VolleyLog.d("%s: %s", new Object[]{file.getAbsolutePath(), var15.toString()});
                this.remove(key);
            } finally {
                if(cis != null) {
                    try {
                        cis.close();
                    } catch (IOException var14) {
                        return null;
                    }
                }

            }

            return null;
        }
    }

    public synchronized void initialize() {
        if(!this.mRootDirectory.exists()) {
            if(!this.mRootDirectory.mkdirs()) {
                VolleyLog.e("Unable to create cache dir %s", new Object[]{this.mRootDirectory.getAbsolutePath()});
            }

        } else {
            File[] files = this.mRootDirectory.listFiles();
            if(files != null) {
                File[] var5 = files;
                int var4 = files.length;

                for(int var3 = 0; var3 < var4; ++var3) {
                    File file = var5[var3];
                    FileInputStream fis = null;

                    try {
                        fis = new FileInputStream(file);
                        DiskBasedCache.CacheHeader e = DiskBasedCache.CacheHeader.readHeader(fis);
                        e.size = file.length();
                        this.putEntry(e.key, e);
                    } catch (IOException var16) {
                        if(file != null) {
                            file.delete();
                        }
                    } finally {
                        try {
                            if(fis != null) {
                                fis.close();
                            }
                        } catch (IOException var15) {
                            ;
                        }

                    }
                }

            }
        }
    }

    public synchronized void invalidate(String key, boolean fullExpire) {
        Entry entry = this.get(key);
        if(entry != null) {
            entry.softTtl = 0L;
            if(fullExpire) {
                entry.ttl = 0L;
            }

            this.put(key, entry);
        }

    }

    public synchronized void put(String key, Entry entry) {
        this.pruneIfNeeded(entry.data.length);
        File file = this.getFileForKey(key);

        try {
            FileOutputStream deleted1 = new FileOutputStream(file);
            DiskBasedCache.CacheHeader e = new DiskBasedCache.CacheHeader(key, entry);
            e.writeHeader(deleted1);
            deleted1.write(entry.data);
            deleted1.close();
            this.putEntry(key, e);
        } catch (IOException var6) {
            boolean deleted = file.delete();
            if(!deleted) {
                VolleyLog.d("Could not clean up file %s", new Object[]{file.getAbsolutePath()});
            }

        }
    }

    public synchronized void remove(String key) {
        boolean deleted = this.getFileForKey(key).delete();
        this.removeEntry(key);
        if(!deleted) {
            VolleyLog.d("Could not delete cache entry for key=%s, filename=%s", new Object[]{key, this.getFilenameForKey(key)});
        }

    }

    private String getFilenameForKey(String key) {
        int firstHalfLength = key.length() / 2;
        String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode());
        localFilename = localFilename + String.valueOf(key.substring(firstHalfLength).hashCode());
        return localFilename;
    }

    public File getFileForKey(String key) {
        return new File(this.mRootDirectory, this.getFilenameForKey(key));
    }

    private void pruneIfNeeded(int neededSpace) {
        if(this.mTotalSize + (long)neededSpace >= (long)this.mMaxCacheSizeInBytes) {
            if(VolleyLog.DEBUG) {
                VolleyLog.v("Pruning old cache entries.", new Object[0]);
            }

            long before = this.mTotalSize;
            int prunedFiles = 0;
            long startTime = SystemClock.elapsedRealtime();
            Iterator iterator = this.mEntries.entrySet().iterator();

            while(iterator.hasNext()) {
                java.util.Map.Entry entry = (java.util.Map.Entry)iterator.next();
                DiskBasedCache.CacheHeader e = (DiskBasedCache.CacheHeader)entry.getValue();
                boolean deleted = this.getFileForKey(e.key).delete();
                if(deleted) {
                    this.mTotalSize -= e.size;
                } else {
                    VolleyLog.d("Could not delete cache entry for key=%s, filename=%s", new Object[]{e.key, this.getFilenameForKey(e.key)});
                }

                iterator.remove();
                ++prunedFiles;
                if((float)(this.mTotalSize + (long)neededSpace) < (float)this.mMaxCacheSizeInBytes * 0.9F) {
                    break;
                }
            }

            if(VolleyLog.DEBUG) {
                VolleyLog.v("pruned %d files, %d bytes, %d ms", new Object[]{Integer.valueOf(prunedFiles), Long.valueOf(this.mTotalSize - before), Long.valueOf(SystemClock.elapsedRealtime() - startTime)});
            }

        }
    }

    private void putEntry(String key, DiskBasedCache.CacheHeader entry) {
        if(!this.mEntries.containsKey(key)) {
            this.mTotalSize += entry.size;
        } else {
            DiskBasedCache.CacheHeader oldEntry = (DiskBasedCache.CacheHeader)this.mEntries.get(key);
            this.mTotalSize += entry.size - oldEntry.size;
        }

        this.mEntries.put(key, entry);
    }

    private void removeEntry(String key) {
        DiskBasedCache.CacheHeader entry = (DiskBasedCache.CacheHeader)this.mEntries.get(key);
        if(entry != null) {
            this.mTotalSize -= entry.size;
            this.mEntries.remove(key);
        }

    }

    private static byte[] streamToBytes(InputStream in, int length) throws IOException {
        byte[] bytes = new byte[length];

        int count;
        int pos;
        for(pos = 0; pos < length && (count = in.read(bytes, pos, length - pos)) != -1; pos += count) {
            ;
        }

        if(pos != length) {
            throw new IOException("Expected " + length + " bytes, read " + pos + " bytes");
        } else {
            return bytes;
        }
    }

    private static class CacheHeader {
        public long size;
        public String key;
        public String etag;
        public long serverDate;
        public long ttl;
        public long softTtl;
        public Map<String, String> responseHeaders;

        private CacheHeader() {
        }

        public CacheHeader(String key, Entry entry) {
            this.key = key;
            this.size = (long)entry.data.length;
            this.etag = entry.etag;
            this.serverDate = entry.serverDate;
            this.ttl = entry.ttl;
            this.softTtl = entry.softTtl;
            this.responseHeaders = entry.responseHeaders;
        }

        public static DiskBasedCache.CacheHeader readHeader(InputStream is) throws IOException {
            DiskBasedCache.CacheHeader entry = new DiskBasedCache.CacheHeader();
            ObjectInputStream ois = new ObjectInputStream(is);
            byte version = ois.readByte();
            if(version != 2) {
                throw new IOException();
            } else {
                entry.key = ois.readUTF();
                entry.etag = ois.readUTF();
                if(entry.etag.equals("")) {
                    entry.etag = null;
                }

                entry.serverDate = ois.readLong();
                entry.ttl = ois.readLong();
                entry.softTtl = ois.readLong();
                entry.responseHeaders = readStringStringMap(ois);
                return entry;
            }
        }

        public Entry toCacheEntry(byte[] data) {
            Entry e = new Entry();
            e.data = data;
            e.etag = this.etag;
            e.serverDate = this.serverDate;
            e.ttl = this.ttl;
            e.softTtl = this.softTtl;
            e.responseHeaders = this.responseHeaders;
            return e;
        }

        public boolean writeHeader(OutputStream os) {
            try {
                ObjectOutputStream e = new ObjectOutputStream(os);
                e.writeByte(2);
                e.writeUTF(this.key);
                e.writeUTF(this.etag == null?"":this.etag);
                e.writeLong(this.serverDate);
                e.writeLong(this.ttl);
                e.writeLong(this.softTtl);
                writeStringStringMap(this.responseHeaders, e);
                e.flush();
                return true;
            } catch (IOException var3) {
                VolleyLog.d("%s", new Object[]{var3.toString()});
                return false;
            }
        }

        private static void writeStringStringMap(Map<String, String> map, ObjectOutputStream oos) throws IOException {
            if(map != null) {
                oos.writeInt(map.size());
                Iterator var3 = map.entrySet().iterator();

                while(var3.hasNext()) {
                    java.util.Map.Entry entry = (java.util.Map.Entry)var3.next();
                    oos.writeUTF((String)entry.getKey());
                    oos.writeUTF((String)entry.getValue());
                }
            } else {
                oos.writeInt(0);
            }

        }

        private static Map<String, String> readStringStringMap(ObjectInputStream ois) throws IOException {
            int size = ois.readInt();
            Object result = size == 0?Collections.emptyMap():new HashMap(size);

            for(int i = 0; i < size; ++i) {
                String key = ois.readUTF().intern();
                String value = ois.readUTF().intern();
                ((Map)result).put(key, value);
            }

            return (Map)result;
        }
    }

    private static class CountingInputStream extends FilterInputStream {
        private int bytesRead;

        private CountingInputStream(InputStream in) {
            super(in);
            this.bytesRead = 0;
        }

        public int read() throws IOException {
            int result = super.read();
            if(result != -1) {
                ++this.bytesRead;
            }

            return result;
        }

        public int read(byte[] buffer, int offset, int count) throws IOException {
            int result = super.read(buffer, offset, count);
            if(result != -1) {
                this.bytesRead += result;
            }

            return result;
        }
    }
}      

可以看到。在這個類裡,寫了各種存儲和讀取資料的方法,找到這裡找到了方向,原來圖檔的讀取和寫入在一開始的時候就已經進行了。

5.找到了這個,但是是封裝的,那麼怎麼去清理這個緩存呢,在new隊列以後,我發現,使用隊列對象,可以直接擷取緩存的擷取和删除

mRequestQueue.getCache().get(url)  //擷取單個緩存
mRequestQueue.getCache().remove(url);  //清除單個緩存
mRequestQueue.getCache().clear();     //清除全部緩存
mRequestQueue.getCache().put(url,bitmap);  //放入緩存

      

 再加上清理記憶體緩存的方法

cache.remove(getCacheKey(url, maxWidth, maxHeight));
       
//同ImageLoader裡私有方法
private static String getCacheKey(String url, int maxWidth, int maxHeight) {
    return (new StringBuilder(url.length() + 12)).append("#W").append(maxWidth).append("#H").append(maxHeight).append(url).toString();
}
       
cache.evictAll();      

這樣,volley的所有緩存都得到了清理

但是在什麼時候存的,怎麼取得,我研究了這個文章,但是還是不夠通透,http://blog.csdn.net/asdzheng/article/details/45955653       這個更詳細http://www.w2bc.com/Article/20215   想要深入了解可以進去研究下,都是分析源碼的