1. 程式人生 > >淺談圖片載入的三級快取(一)

淺談圖片載入的三級快取(一)

之前被人問及過,圖片的三級快取是什麼啊,來給我講講,圖片三級快取,好高大尚的名字,聽著挺厲害,應該是很厲害的技術,當時不會啊,也就沒什麼了,沒有說出來唄,前一階端用到了BitmapUtils的圖片快取框架,索性就自己找些知識點來研究一些圖片的三級快取是什麼吧。真所謂是知識你要是不知道,那就真的說不出所以然來,但是當你真正的去了解了,三級快取也不是那麼高階的技術。好了,閒話不多說了,開始圖片的三級快取原理吧。

什麼是圖片的三級快取

  • 1、記憶體快取 優先載入,速度最快
  • 2、本地快取 次優先載入 速度稍快
  • 3、網路快取 最後載入 速度由網路速度決定(浪費流量)

圖片的三級快取圖形解釋

這裡寫圖片描述

好了,既然知道了什麼三級快取的字面意思了,那麼我們就來處理吧。

圖片快取—記憶體快取的原理

MemoryCacheUtils.java:

package com.example.bitmaputils.bitmap;

import android.graphics.Bitmap;
import android.util.Log;
import android.util.LruCache;

/**
 * 記憶體快取
 */
public class MemoryCacheUtils {

    /**
     * LinkedHashMap<>(10,0.75f,true);
     * <p/>
     * 10是最大致   0.75f是載入因子   true是訪問排序   false插入排序
     *
     *
     */
//private LinkedHashMap<String,Bitmap> mMemoryCache = new LinkedHashMap<>(5,0.75f,true); private LruCache<String, Bitmap> mLruCache; public MemoryCacheUtils() { long maxMemory = Runtime.getRuntime().maxMemory();//最大記憶體 預設是16兆 執行時候的 mLruCache = new LruCache<String, Bitmap>((int
) (maxMemory / 8)) { @Override protected int sizeOf(String key, Bitmap value) { //int byteCount = value.getByteCount(); //得到圖片位元組數 // @return number of bytes between rows of the native bitmap pixels. int byteCount = value.getRowBytes() * value.getWidth(); return byteCount; } }; } /** * 從記憶體中讀取 * * @param url */ public Bitmap getFromMemroy(String url) { Log.d("MyBitmapUtils", "從記憶體中載入圖片"); return mLruCache.get(url); } /** * 寫入到記憶體中 * * @param url * @param bitmap */ public void setToMemory(String url, Bitmap bitmap) { mLruCache.put(url, bitmap); } }

很簡單吧,在這裡我們使用了LruCache(),對圖片進行了記憶體快取,這裡我們只是稍微進行了處理,在這裡我們只是介紹原理,當然了哈,效能,OOM溢位問題我們在這裡不作處理。

圖片快取—本地快取

package com.example.bitmaputils.bitmap;


import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.util.Log;

import com.example.bitmaputils.MD5Encoder;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;

/**
 * 本地快取
 */
public class SDcardCacheUtils {

    /**
     * 我們讀取記憶體的絕對路徑
     */
    public static final String CACHE_PATH = Environment
            .getExternalStorageDirectory().getAbsolutePath() + "/aixuexi";

    /**
     * 從本地讀取
     * @param url
     */
    public Bitmap getFromSd(String url){
        String fileName = null;
        try {
            //得到圖片的url的md5的檔名
            fileName = MD5Encoder.encode(url);
        } catch (Exception e) {
            e.printStackTrace();
        }
        File file = new File(CACHE_PATH,fileName);

        //如果存在,就通過bitmap工廠,返回的bitmap,然後返回bitmap
        if (file.exists()){
            try {
                Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
                Log.d("MyBitmapUtils", "從本地讀取圖片啊");
                return bitmap;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 向本地快取
     *
     * @param url   圖片地址
     * @param bitmap   圖片
     */
    public void savaSd(String url,Bitmap bitmap){
        String fileName = null;
        try {
            //我們對圖片的地址進行MD5加密,作為檔名
            fileName = MD5Encoder.encode(url);
        } catch (Exception e) {
            e.printStackTrace();
        }

        /**
         * 以CACHE_PATH為資料夾  fileName為檔名
         */
        File file = new File(CACHE_PATH,fileName);

        //我們首先得到他的符文劍
        File parentFile = file.getParentFile();
        //檢視是否存在,如果不存在就建立
        if (!parentFile.exists()){
            parentFile.mkdirs(); //建立資料夾
        }

        try {
            //將圖片儲存到本地
            /**
             * @param format   The format of the compressed image   圖片的儲存格式
             * @param quality  Hint to the compressor, 0-100. 0 meaning compress for
             *                 small size, 100 meaning compress for max quality. Some
             *                 formats, like PNG which is lossless, will ignore the
             *                 quality setting
             *                 圖片的儲存的質量    100最好
             * @param stream   The outputstream to write the compressed data.
             */
            bitmap.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(file));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

圖片快取—網路快取

NetCacheUtils .java:

package com.example.bitmaputils.bitmap;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ImageView;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * 網路快取工具類
 */
public class NetCacheUtils {


    /**
     * 圖片
     */
    private ImageView mImageView;

    /**
     * 圖片地址
     */
    private String mUrl;

    /**
     * 本地快取
     */
    private SDcardCacheUtils mDcardCacheUtils;


    /**
     * 記憶體快取
     */
    private MemoryCacheUtils mMemoryCacheUtils;


    public NetCacheUtils(SDcardCacheUtils dcardCacheUtils, MemoryCacheUtils memoryCacheUtils) {
        mDcardCacheUtils = dcardCacheUtils;
        mMemoryCacheUtils = memoryCacheUtils;
    }

    /**
     * 從網路中下載圖片
     *
     * @param image
     * @param url
     */
    public void getDataFromNet(ImageView image, String url) {
        new MyAsyncTask().execute(image, url);  //啟動Asynctask,傳入的引數到對應doInBackground()
    }


    /**
     * 非同步下載
     * <p/>
     * 第一個泛型 : 引數型別  對應doInBackground()
     * 第二個泛型 : 更新進度   對應onProgressUpdate()
     * 第三個泛型 : 返回結果result   對應onPostExecute
     */
    class MyAsyncTask extends AsyncTask<Object, Void, Bitmap> {

        /**
         * 後臺下載  子執行緒
         *
         * @param params
         * @return
         */
        @Override
        protected Bitmap doInBackground(Object... params) {

            //拿到傳入的image
            mImageView = (ImageView) params[0];

            //得到圖片的地址
            mUrl = (String) params[1];
            //將imageview和url繫結,防止錯亂
            mImageView.setTag(mUrl);

            Bitmap bitmap = downLoadBitmap(mUrl);

            return bitmap;
        }


        /**
         * 進度更新   UI執行緒
         *
         * @param values
         */
        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }

        /**
         * 回撥結果,耗時方法結束後,主執行緒
         *
         * @param bitmap
         */
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (bitmap != null) {
                //得到圖片的tag值
                String url = (String) mImageView.getTag();
                //確保圖片設定給了正確的image
                if (url.equals(mUrl)) {
                    mImageView.setImageBitmap(bitmap);

                    /**
                     * 當從網路上下載好之後儲存到sdcard中
                     */
                    mDcardCacheUtils.savaSd(mUrl, bitmap);

                    /**
                     *  寫入到記憶體中
                     */
                    mMemoryCacheUtils.setToMemory(mUrl, bitmap);
                    Log.d("MyBitmapUtils", "我是從網路快取中讀取的圖片啊");
                }
            }
        }
    }

    /**
     * 下載圖片
     *
     * @param url 下載圖片地址
     * @return
     */
    private Bitmap downLoadBitmap(String url) {

        //連線
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) new URL(url)
                    .openConnection();

            //設定讀取超時
            conn.setReadTimeout(5000);
            //設定請求方法
            conn.setRequestMethod("GET");
            //設定連線超時連線
            conn.setConnectTimeout(5000);
            //連線
            conn.connect();

            //響應碼
            int code = conn.getResponseCode();

            if (code == 200) {  //請求正確的響應碼是200
                //得到響應流
                InputStream inputStream = conn.getInputStream();
                //得到bitmap物件
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

                return bitmap;
            }
        } catch (IOException e) {
 #
          e.printStackTrace();
        } finally {
            conn.disconnect();
        }

        return null;
    }
}

好了,至此,一個簡單的圖片的三級快取完成了,接下來看看使用吧。

圖片載入工具類

MyBitmapUtils .java:

package com.example.bitmaputils.bitmap;

/**
 * Created by 若蘭 on 2016/1/29.
 * 一個懂得了程式設計樂趣的小白,希望自己
 * 能夠在這個道路上走的很遠,也希望自己學習到的
 * 知識可以幫助更多的人,分享就是學習的一種樂趣
 * QQ:1069584784
 * csdn:http://blog.csdn.net/wuyinlei
 */

import android.graphics.Bitmap;
import android.widget.ImageView;

/**
 * 自定義的bitmap工具類
 */
public class MyBitmapUtils {


    /**
     * 網路快取
     */
    public NetCacheUtils mNetCacheUtils;

    /**
     * 本地快取
     */
    public SDcardCacheUtils mSdCacheUtils;

    /**
     * 記憶體快取
     */
    public MemoryCacheUtils mMemoryCacheUtils;


    public MyBitmapUtils() {
        mSdCacheUtils = new SDcardCacheUtils();
        mMemoryCacheUtils = new MemoryCacheUtils();
        mNetCacheUtils = new NetCacheUtils(mSdCacheUtils, mMemoryCacheUtils);
    }

    /**
     * 展示圖片的方法
     *
     * @param image
     * @param url
     */
    public void display(ImageView image, String url) {


        //從記憶體中讀取
        Bitmap fromMemroy = mMemoryCacheUtils.getFromMemroy(url);
        //如果記憶體中有的h話就直接返回,從記憶體中讀取
        if (fromMemroy != null) {
            image.setImageBitmap(fromMemroy);

            return;
        }


        //從本地SD卡讀取
        Bitmap fromSd = mSdCacheUtils.getFromSd(url);
        if (fromSd != null) {
            image.setImageBitmap(fromSd);

            mMemoryCacheUtils.setToMemory(url, fromSd);

            return;
        }
        //從網路中讀取
        mNetCacheUtils.getDataFromNet(image, url);

    }
}

使用這個工具類就很簡單了
只需在載入資料的介面卡中

//宣告圖片載入工具類
private MyBitmapUtils utils;
        public PhotoAdapter() {
            //mBitmapUtils = new BitmapUtils(MainActivity.this);
           // mBitmapUtils.configDefaultLoadingImage(R.mipmap.defaut);
            utils = new MyBitmapUtils();
        }

然後在getView()方法中,使用工具類中的display()方法就可以了。簡單吧
utils.display(holder.tvImage,mImageViews[position]);

在這裡我們看下圖片載入效果:
這裡寫圖片描述

來看下LOGCAT日誌:

01-29 11:19:48.127 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:19:48.395 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:19:48.687 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:19:49.282 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從記憶體中載入圖片
01-29 11:19:49.628 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:19:50.173 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 我是從網路快取中讀取的圖片啊
01-29 11:19:58.630 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從記憶體中載入圖片
01-29 11:19:58.762 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:19:59.325 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 我是從網路快取中讀取的圖片啊
01-29 11:19:59.624 5556-5556/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊


01-29 11:56:40.897 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:56:40.923 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從記憶體中載入圖片
01-29 11:56:41.105 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:56:41.118 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從記憶體中載入圖片
01-29 11:56:41.268 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:56:41.283 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從記憶體中載入圖片
01-29 11:56:41.431 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:56:43.837 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從記憶體中載入圖片
01-29 11:56:44.013 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊
01-29 11:56:46.047 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從記憶體中載入圖片
01-29 11:56:46.222 15045-15045/com.example.bitmaputils D/MyBitmapUtils: 從本地讀取圖片啊

好了,一個簡單的圖片三級快取原理就這樣的誕生了,當然正如開始所說,這裡只是簡單的原理介紹,像裡面用到的AsyncTask非同步載入,和LruCache在這裡只是使用,在以後的篇章中會對這兩個進行介紹的,好了,由於程式碼展示的程式碼量太多,這裡我就不在上傳怎麼顯示圖片的原始碼了,下面我會提供github原始碼地址,可以找到我的這個demo的原始碼。如果有問題,或者獨特的見解,咱們可以討論哦,QQ:1069584784