Android Volley框架的使用(四)圖片的三級緩存策略(內存LruCache+磁盤DiskLruCache+網絡Volley)

 

     在開發安卓應用中避免不了要使用到網絡圖片,獲取網絡圖片很簡單,可是須要付出必定的代價——流量。對於少數的圖片而言問題不大,但若是手機應用中包含大量的圖片,這勢必會耗費用戶的必定流量,若是咱們不加以處理,每次打開應用都去網絡獲取圖片,那麼用戶可就不樂意了,這裏的處理就是指今天要講的緩存策略(緩存層分爲三層:內存層,磁盤層,網絡層)。java

  關於緩存層的工做,當咱們第一次打開應用獲取圖片時,先到網絡去下載圖片,而後依次存入內存緩存,磁盤緩存,當咱們再一次須要用到剛纔下載的這張圖片時,就不須要再重複的到網絡上去下載,直接能夠從內存緩存和磁盤緩存中找,因爲內存緩存速度較快,咱們優先到內存緩存中尋找該圖片,若是找到則運用,若是沒有找到(內存緩存大小有限),那麼咱們再到磁盤緩存中去找。只要咱們合理的去協調這三層緩存運用,即可以提高應用性能和用戶體驗。android

 此博文源碼下載地址  https://github.com/Javen205/VolleyDemo.gitgit

 

一、內存層:(手機內存)github

內存緩存相對於磁盤緩存而言,速度要來的快不少,但缺點容量較小且會被系統回收,這裏的實現我用到了LruCache。算法

LruCache這個類是Android3.1版本中提供的,若是你是在更早的Android版本中開發,則須要導入android-support-v4的jar包。緩存

 

磁盤層:(SD卡)網絡

相比內存緩存而言速度要來得慢不少,但容量很大,這裏的實現我用到了DiskLruCache類。框架

DiskLruCache是非Google官方編寫,但得到官方認證的硬盤緩存類,該類沒有限定在Android內,因此理論上java應用也可使用DiskLreCache來緩存。eclipse

這是DiskLruCache類的下載地址:http://pan.baidu.com/s/1o6tPjz8ide

 

網絡層:(移動網絡,無線網絡)

這個就沒什麼解釋的了,就是咱們上網用的流量。這裏的網絡訪問實現我用到了開源框架Volley。

開源框架Volley是2013年Google I/O大會發布的,Volley是Android平臺上的網絡通訊庫,能使網絡通訊更快,更簡單,更健壯。它的設計目標就是很是適合去進行數據量不大,但通訊頻繁的網絡操做,而對於大數據量的網絡操做,好比說下載文件等,Volley的表現就會很是糟糕。

這是Volley的下載地址:http://pan.baidu.com/s/1kThedX9

 

如下是代碼實現:

先附上兩個工具類 


生成MD5序列幫助類,DiskLruCache磁盤緩存類(下載地址見上文

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Utils {
    /**
     * 使用md5的算法進行加密
     */
    public static String md5(String plainText) {
        byte[] secretBytes = null;
        try {
            secretBytes = MessageDigest.getInstance("md5").digest(
                    plainText.getBytes());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("沒有md5這個算法!");
        }
        String md5code = new BigInteger(1, secretBytes).toString(16);// 16進制數字
        // 若是生成數字未滿32位,須要前面補0
        for (int i = 0; i < 32 - md5code.length(); i++) {
            md5code = "0" + md5code;
        }
        return md5code;
    }

}

 

com.android.volley.toolbox. ImageLoader.ImageLoader( RequestQueue queue, ImageCache imageCache)
咱們能夠看到Volley加載圖片須要一個請求隊列以及圖片的緩存機制
 
 
圖片緩存類ImageCacheUtil 包含(LruCache內存緩存,DiskLruCache磁盤緩存)
 

 

 

package com.javen.volley.cache;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.support.v4.util.LruCache;
import android.util.Log;

import com.android.volley.toolbox.ImageLoader.ImageCache;
import com.javen.volley.cache.DiskLruCache.Snapshot;

/**
 * 圖片緩存幫助類
 * 包含內存緩存LruCache和磁盤緩存DiskLruCache
 * @author Javen
 */
public class ImageCacheUtil implements ImageCache {
    
    private String TAG=ImageCacheUtil.this.getClass().getSimpleName();

    //緩存類
    private static LruCache<String, Bitmap> mLruCache;

    private static DiskLruCache mDiskLruCache;
    //磁盤緩存大小
    private static final int DISKMAXSIZE = 10 * 1024 * 1024;

    public ImageCacheUtil(Context context) {
        // 獲取應用可佔內存的1/8做爲緩存
        int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8);
        // 實例化LruCaceh對象
        mLruCache = new LruCache<String, Bitmap>(maxSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight();
            }
        };
        try {
            // 獲取DiskLruCahce對象
//            mDiskLruCache = DiskLruCache.open(getDiskCacheDir(MyApplication.newInstance(), "xxxxx"), getAppVersion(MyApplication.newInstance()), 1, DISKMAXSIZE);
            mDiskLruCache = DiskLruCache.open(getDiskCacheDir(context.getApplicationContext(), "xxxxx"), getAppVersion(context), 1, DISKMAXSIZE);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 從緩存(內存緩存,磁盤緩存)中獲取Bitmap
     */
    @Override
    public Bitmap getBitmap(String url) {
        if (mLruCache.get(url) != null) {
            // 從LruCache緩存中取
            Log.i(TAG,"從LruCahce獲取");
            return mLruCache.get(url);
        } else {
            String key = MD5Utils.md5(url);
            try {
                if (mDiskLruCache.get(key) != null) {
                    // 從DiskLruCahce取
                    Snapshot snapshot = mDiskLruCache.get(key);
                    Bitmap bitmap = null;
                    if (snapshot != null) {
                        bitmap = BitmapFactory.decodeStream(snapshot.getInputStream(0));
                        // 存入LruCache緩存
                        mLruCache.put(url, bitmap);
                        Log.i(TAG,"從DiskLruCahce獲取");
                    }
                    return bitmap;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 存入緩存(內存緩存,磁盤緩存)
     */
    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        // 存入LruCache緩存
        mLruCache.put(url, bitmap);
        // 判斷是否存在DiskLruCache緩存,若沒有存入
        String key = MD5Utils.md5(url);
        try {
            if (mDiskLruCache.get(key) == null) {
                DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                if (editor != null) {
                    OutputStream outputStream = editor.newOutputStream(0);
                    if (bitmap.compress(CompressFormat.JPEG, 100, outputStream)) {
                        editor.commit();
                    } else {
                        editor.abort();
                    }
                }
                mDiskLruCache.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 該方法會判斷當前sd卡是否存在,而後選擇緩存地址
     * 
     * @param context
     * @param uniqueName
     * @return
     */
    public static File getDiskCacheDir(Context context, String uniqueName) {
        String cachePath;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
            cachePath = context.getExternalCacheDir().getPath();
        } else {
            cachePath = context.getCacheDir().getPath();
        }
        return new File(cachePath + File.separator + uniqueName);
    }

    /**
     * 獲取應用版本號
     * 
     * @param context
     * @return
     */
    public int getAppVersion(Context context) {
        try {
            PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            return info.versionCode;
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
        return 1;
    }

}

Volley請求隊列處理類,用來管理Rquest請求對象操做

 

package com.javen.volley;

import android.content.Context;
import android.text.TextUtils;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;
import com.javen.volley.cache.ImageCacheUtil;

public class VolleyController {

    // 建立一個TAG,方便調試或Log
    private static final String TAG = "VolleyController";

    // 建立一個全局的請求隊列
    private RequestQueue reqQueue;
    private ImageLoader imageLoader;

    // 建立一個static VolleyController對象,便於全局訪問
    private static VolleyController mInstance;
    
    private Context mContext;

    private VolleyController(Context context) {
        mContext=context;
    }

    /**
     * 如下爲須要咱們本身封裝的添加請求取消請求等方法
     */

    // 用於返回一個VolleyController單例
    public static VolleyController getInstance(Context context) {
        if (mInstance == null) {
            synchronized(VolleyController.class)
            {
                if (mInstance == null) {
                    mInstance = new VolleyController(context);
                }
            }
        }
        return mInstance;
    }

    // 用於返回全局RequestQueue對象,若是爲空則建立它
    public RequestQueue getRequestQueue() {
        if (reqQueue == null){
            synchronized(VolleyController.class)
            {
                if (reqQueue == null){
                    reqQueue = Volley.newRequestQueue(mContext);
                }
            }
        }
        return reqQueue;
    }
    
    
    public ImageLoader getImageLoader(){
        getRequestQueue();
        //若是imageLoader爲空則建立它,第二個參數表明處理圖像緩存的類
        if(imageLoader==null){
            //LruCache
            //imageLoader=new ImageLoader(reqQueue, new LruBitmapCache());
            //LruCache  DiskLruCache
            imageLoader=new ImageLoader(reqQueue, new ImageCacheUtil(mContext));
        }
        return imageLoader;
    }


    /**
     * 將Request對象添加進RequestQueue,因爲Request有*StringRequest,JsonObjectResquest...
     * 等多種類型,因此須要用到*泛型。同時可將*tag做爲可選參數以便標示出每個不一樣請求
     */

    public <T> void addToRequestQueue(Request<T> req, String tag) {
        // 若是tag爲空的話,就是用默認TAG
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);

        getRequestQueue().add(req);
    }

    public <T> void addToRequestQueue(Request<T> req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }

    // 經過各Request對象的Tag屬性取消請求
    public void cancelPendingRequests(Object tag) {
        if (reqQueue != null) {
            reqQueue.cancelAll(tag);
        }
    }
}

 

 

圖片緩存管理類(方便外部調用)

 

這裏向外部提供了一個loadImage的重載方法,一個傳入加載圖片的寬高,一個默認加載原圖,使得外部再也不須要關注任何關於緩存的操做。

package com.javen.volley.utils;

import android.content.Context;
import android.graphics.Bitmap;
import android.widget.ImageView;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader.ImageContainer;
import com.android.volley.toolbox.ImageLoader.ImageListener;
import com.javen.volley.VolleyController;

/**
 * 圖片緩存管理類 獲取ImageLoader對象
 * @author Javen
 *
 */
public class ImageCacheManager {
    private static String TAG = ImageCacheManager.class.getSimpleName();

    /**
     * 獲取ImageListener
     * 
     * @param view
     * @param defaultImage
     * @param errorImage
     * @return
     */
    public static ImageListener getImageListener(final ImageView view, final Bitmap defaultImage, final Bitmap errorImage) {

        return new ImageListener() {

            @Override
            public void onErrorResponse(VolleyError error) {
                // 回調失敗
                if (errorImage != null) {
                    view.setImageBitmap(errorImage);
                }
            }

            @Override
            public void onResponse(ImageContainer response, boolean isImmediate) {
                // 回調成功
                if (response.getBitmap() != null) {
                    view.setImageBitmap(response.getBitmap());
                } else if (defaultImage != null) {
                    view.setImageBitmap(defaultImage);
                }
            }
        };

    }

    /**
     * 提供給外部調用方法
     * 
     * @param url
     * @param view
     * @param defaultImage
     * @param errorImage
     */
    public static void loadImage(Context context,String url, ImageView view, Bitmap defaultImage, Bitmap errorImage) {
       VolleyController.getInstance(context).getImageLoader().get(url, ImageCacheManager.getImageListener(view, defaultImage, errorImage), 0, 0);
    }

    /**
     * 提供給外部調用方法
     * 
     * @param url
     * @param view
     * @param defaultImage
     * @param errorImage
     */
    public static void loadImage(Context context,String url, ImageView view, Bitmap defaultImage, Bitmap errorImage, int maxWidth, int maxHeight) {
         VolleyController.getInstance(context).getImageLoader().get(url, ImageCacheManager.getImageListener(view, defaultImage, errorImage), maxWidth, maxHeight);
    }
}

 

使用方法

 

   public void CacheImage(View view){
        Bitmap defaultImage=BitmapFactory.decodeResource(getResources(), R.drawable.loading);
        Bitmap errorImage=BitmapFactory.decodeResource(getResources(), R.drawable.load_error);
        ImageCacheManager.loadImage(this, url, imageView, defaultImage, errorImage);
    }

 轉:http://blog.csdn.net/jie1991liu/article/details/46926421

相關文章
相關標籤/搜索