• Android Volley框架的使用(四)图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)


         在开发安卓应用中避免不了要使用到网络图片,获取网络图片很简单,但是需要付出一定的代价——流量。对于少数的图片而言问题不大,但如果手机应用中包含大量的图片,这势必会耗费用户的一定流量,如果我们不加以处理,每次打开应用都去网络获取图片,那么用户可就不乐意了,这里的处理就是指今天要讲的缓存策略(缓存层分为三层:内存层,磁盘层,网络层)。

      关于缓存层的工作,当我们第一次打开应用获取图片时,先到网络去下载图片,然后依次存入内存缓存,磁盘缓存,当我们再一次需要用到刚才下载的这张图片时,就不需要再重复的到网络上去下载,直接可以从内存缓存和磁盘缓存中找,由于内存缓存速度较快,我们优先到内存缓存中寻找该图片,如果找到则运用,如果没有找到(内存缓存大小有限),那么我们再到磁盘缓存中去找。只要我们合理的去协调这三层缓存运用,便可以提升应用性能和用户体验。

     此博文源码下载地址  https://github.com/Javen205/VolleyDemo.git

    1、内存层:(手机内存)

    内存缓存相对于磁盘缓存而言,速度要来的快很多,但缺点容量较小且会被系统回收,这里的实现我用到了LruCache。

    LruCache这个类是Android3.1版本中提供的,如果你是在更早的Android版本中开发,则需要导入android-support-v4的jar包。

    磁盘层:(SD卡)

    相比内存缓存而言速度要来得慢很多,但容量很大,这里的实现我用到了DiskLruCache类。

    DiskLruCache是非Google官方编写,但获得官方认证的硬盘缓存类,该类没有限定在Android内,所以理论上java应用也可以使用DiskLreCache来缓存。

    这是DiskLruCache类的下载地址:http://pan.baidu.com/s/1o6tPjz8

    网络层:(移动网络,无线网络)

    这个就没什么解释的了,就是我们上网用的流量。这里的网络访问实现我用到了开源框架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

  • 相关阅读:
    自己写的asp日历控件
    处处留心皆学问
    模块化闲谈
    原理……
    DIV和SPAN的区别
    javascript学习感触
    CSS 匹配顺序
    配置闲谈
    找到的一个读取shape数据的代码
    问题和收获
  • 原文地址:https://www.cnblogs.com/zyw-205520/p/4997863.html
Copyright © 2020-2023  润新知