浅谈图片加载的三级缓存

作者: 菜鸟_一枚 | 来源:发表于2016-04-29 17:33 被阅读1092次

    浅谈图片加载的三级缓存

    之前被人问及过,图片的三级缓存是什么啊,来给我讲讲,图片三级缓存,好高大尚的名字,听着挺厉害,应该是很厉害的技术,当时不会啊,也就没什么了,没有说出来呗,前一阶端用到了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.
     * 一个懂得了编程乐趣的小白,希望自己
     * 能够在这个道路上走的很远,也希望自己学习到的
     * 知识可以帮助更多的人,分享就是学习的一种乐趣
     * 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的源码。如果有问题,或者独特的见解,咱们可以讨论哦

    相关文章

      网友评论

      • 0bc95b58ed61:value.getRowBytes() * value.getWidth() 这个应该错了 正确的应该是
        public final int getByteCount() {
        // int result permits bitmaps up to 46,340 x 46,340
        return getRowBytes() * getHeight();
        }
      • 76ffef5441ab:能解决oom吗
      • 娃娃要从孩子抓起:谢谢博主分享,期待更多好文章。
        菜鸟_一枚: @娃娃要从孩子抓起 会的哈,谢谢

      本文标题:浅谈图片加载的三级缓存

      本文链接:https://www.haomeiwen.com/subject/dbfkrttx.html