圖片三級緩存

圖片三級緩存

  • 內存緩存,最優先從內存加載,速度快+無流量
  • 本地緩存,其次從本地加載,速度快+無流量
  • 網絡緩存,最後才從網絡加載,速度慢+耗流量

內存溢出

android默認給每個APP只分配16M內存(根據真機有相關浮動)

java引用方式

  • 強引用 Person a = new Person();
  • 軟引用 SoftReference 較弱
  • 弱引用 WeakReference 其次弱
  • 虛引用 PhantomReferences 引用最弱

當內存不夠時,垃圾回收器除了回收一些沒有引用的對象之外,也會回收引用不強的對象。

寫圖片的三級緩存

  • 網絡緩存類使用AsyncTask異步類處理,重點是內存緩存類,內存緩存中不能用HashMap類來寫, 因爲這樣會涉及內存的溢出(oom)的問題。同時用軟引用的話由於API level 9 之後垃圾回收機制對軟引用對象的回收率更高了,所以已經不適合用軟引用, 重用效果不好。但是可以用LruCache類 這用最近最久未使用方法來回收對象。更適合於對緩存的書寫。
  • 其實最適合的是用XUtils類,別人寫的已經很好了!

首先是要寫一個緩存類:

public class MyBitmapUtils {

    // 網絡緩存工具類
    private NetCacheUtils mNetUtils;
    // 本地緩存工具類
    private LocalCacheUtils mLocalUtils;
    // 內存緩存工具類
    private MemoryCacheUtils mMemoryUtils;

    public MyBitmapUtils() {
        mMemoryUtils = new MemoryCacheUtils();
        mLocalUtils = new LocalCacheUtils();
        mNetUtils = new NetCacheUtils(mLocalUtils, mMemoryUtils);
    }

    public void display(ImageView imageView, String url) {
        // 設置默認加載圖片
        imageView.setImageResource(R.drawable.news_pic_default);

        // 先從內存緩存加載
        Bitmap bitmap = mMemoryUtils.getBitmapFromMemory(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            System.out.println("從內存讀取圖片啦...");
            return;
        }

        // 再從本地緩存加載
        bitmap = mLocalUtils.getBitmapFromLocal(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            System.out.println("從本地讀取圖片啦...");
            // 給內存設置圖片
            mMemoryUtils.setBitmapToMemory(url, bitmap);
            return;
        }

        // 從網絡緩存加載
        mNetUtils.getBitmapFromNet(imageView, url);
    }

}

網絡緩存類:

public class NetCacheUtils {

    private LocalCacheUtils mLocalUtils;
    private MemoryCacheUtils mMemoryUtils;

    public NetCacheUtils(LocalCacheUtils localUtils,
            MemoryCacheUtils memoryUtils) {
        mLocalUtils = localUtils;
        mMemoryUtils = memoryUtils;
    }

    public void getBitmapFromNet(ImageView imageView, String url) {
        BitmapTask task = new BitmapTask();
        task.execute(imageView, url);
    }

    /**
     * AsyncTask是線程池+handler的封裝 第一個泛型: 傳參的參數類型類型(和doInBackground一致) 第二個泛型:
     * 更新進度的參數類型(和onProgressUpdate一致) 第三個泛型: 返回結果的參數類型(和onPostExecute一致,
     * 和doInBackground返回類型一致)
     */
    class BitmapTask extends AsyncTask<Object, Integer, Bitmap> {

        private ImageView mImageView;
        private String url;

        // 主線程運行, 預加載
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        // 子線程運行, 異步加載邏輯在此方法中處理
        @Override
        protected Bitmap doInBackground(Object... params) {
            mImageView = (ImageView) params[0];
            url = (String) params[1];
            mImageView.setTag(url);// 將imageView和url綁定在一起
            // publishProgress(values)//通知進度

            // 下載圖片
            return download(url);
        }

        // 主線程運行, 更新進度
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
        }

        // 主線程運行, 更新主界面
        @Override
        protected void onPostExecute(Bitmap result) {
            if (result != null) {
                // 判斷當前圖片是否就是imageView要的圖片, 防止listview重用導致的圖片錯亂的情況出現
                String bindUrl = (String) mImageView.getTag();
                if (bindUrl.equals(url)) {
                    // 給imageView設置圖片
                    mImageView.setImageBitmap(result);
                    System.out.println("網絡下載圖片成功!");

                    // 將圖片保存在本地
                    mLocalUtils.setBitmapToLocal(result, url);

                    // 將圖片保存在內存
                    mMemoryUtils.setBitmapToMemory(url, result);
                }
            }
        }

    }

    /**
     * 下載圖片
     * 
     * @param url
     */
    public Bitmap download(String url) {
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) (new URL(url).openConnection());

            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            conn.setRequestMethod("GET");

            conn.connect();

            int responseCode = conn.getResponseCode();
            if (responseCode == 200) {
                InputStream in = conn.getInputStream();
                // 將流轉化爲bitmap對象
                Bitmap bitmap = BitmapFactory.decodeStream(in);
                return bitmap;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }

        return null;
    }

}

本地緩存類:

public class LocalCacheUtils {

    // 圖片緩存的文件夾
    public static final String DIR_PATH = Environment
            .getExternalStorageDirectory().getAbsolutePath()
            + "/bitmap_cache66";

    public Bitmap getBitmapFromLocal(String url) {
        try {
            File file = new File(DIR_PATH, MD5Encoder.encode(url));

            if (file.exists()) {
                Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(
                        file));
                return bitmap;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public void setBitmapToLocal(Bitmap bitmap, String url) {
        File dirFile = new File(DIR_PATH);

        // 創建文件夾
        if (!dirFile.exists() || !dirFile.isDirectory()) {
            dirFile.mkdirs();
        }

        try {
            File file = new File(DIR_PATH, MD5Encoder.encode(url));
            // 將圖片壓縮保存在本地,參1:壓縮格式;參2:壓縮質量(0-100);參3:輸出流
            bitmap.compress(CompressFormat.JPEG, 100,
                    new FileOutputStream(file));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

內存緩存類:

public class MemoryCacheUtils {

    // private HashMap<String, SoftReference<Bitmap>> mMemroyCache = new
    // HashMap<String, SoftReference<Bitmap>>();
    // Android 2.3 (API Level
    // 9)開始,垃圾回收器會更傾向於回收持有軟引用或弱引用的對象,這讓軟引用和弱引用變得不再可靠,建議用LruCache
    private LruCache<String, Bitmap> mCache;

    public MemoryCacheUtils() {
        int maxMemory = (int) Runtime.getRuntime().maxMemory();// 獲取虛擬機分配的最大內存
                                                                // 16M
        // LRU 最近最少使用, 通過控制內存不要超過最大值(由開發者指定), 來解決內存溢出
        mCache = new LruCache<String, Bitmap>(maxMemory / 8) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                // 計算一個bitmap的大小
                int size = value.getRowBytes() * value.getHeight();// 每一行的字節數乘以高度
                return size;
            }
        };
    }

    public Bitmap getBitmapFromMemory(String url) {
        // SoftReference<Bitmap> softReference = mMemroyCache.get(url);
        // if (softReference != null) {
        // Bitmap bitmap = softReference.get();
        // return bitmap;
        // }
        return mCache.get(url);
    }

    public void setBitmapToMemory(String url, Bitmap bitmap) {
        // SoftReference<Bitmap> soft = new SoftReference<Bitmap>(bitmap);
        // mMemroyCache.put(url, soft);
        mCache.put(url, bitmap);
    }

}

MD5相關類:

public class MD5Encoder {

    public static String encode(String string) throws Exception {
        byte[] hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
        StringBuilder hex = new StringBuilder(hash.length * 2);
        for (byte b : hash) {
            if ((b & 0xFF) < 0x10) {
                hex.append("0");
            }
            hex.append(Integer.toHexString(b & 0xFF));
        }
        return hex.toString();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章