Android 图片三级缓存的实现以及原理解释

网络图片加载以及处理

网络图片加载三级缓存详解以及实现

效果图
加载图片
1. 讲讲网络图片的加载吧?他们怎么实现的[三级缓存的实现]?
* 网络图片加载流程:判断内存中有没有该图片,如果有的话直接显示,如果内存中没有该图片,则去查看本地[SD卡]是否有该图片的保存,如果本地也没有这张图片,则去网络中加载图片。加载完图片分别保存在本地和缓存中。这样下次再次加载该图片的时候就不用去网络加载。浪费客户流量了。
* 图片三级缓存:
* 内存缓存: 图片加载的时候应该优先加载内存中的图片。速度快。
* 本地缓存: 如果内存中没有该图片,应该优先加载本地中的图片,速度会比内存加载稍慢。
* 网络缓存: 如果内存和本地都没有该图片,网络加载图片,速度取决于网络。
* 三级缓存原理:
* 打开App先去判断缓存和本地有没有该图片,如果缓存和本地都没有,则去网络加载,然后缓存在内存和本地。
* 之后打开App如果内存缓存了该图片则加载内存中的图片,不走本地和网络请求。避免去重复网络请求图片。
2. 网络图片加载代码实现:
* 项目目录:
这里写图片描述

* 负责判断显示图片 LoadImage.java 类的实现:
public class LoadImage {
    private static LoadImage instance;
    private DiskCacheUtils mLocalCache;
    private MemoryCacheUtils mMemoryCache;
    private NetWorkCacheUtils mNetWorkCache;

    private LoadImage(Context context) {
        mMemoryCache = new MemoryCacheUtils();
        mLocalCache = new DiskCacheUtils(context);
        mNetWorkCache = new NetWorkCacheUtils(mLocalCache, mMemoryCache);
    }

    /***
     * 单利模式保证LoadImage实例唯一性
     *
     * @return
     */
    public static LoadImage getLoadImageInstance(Context context) {
        if (instance == null) {
            synchronized (LoadImage.class) {
                if (instance == null) {
                    instance = new LoadImage(context);
                }
            }
        }
        return instance;
    }

    /***
     * 显示图片
     *
     * @param img
     * @param imgPath
     */
    public void showImg(ImageView img, String imgPath) {
        //TODO 先去缓存中读取图片
        Bitmap bitmap = null;
        bitmap = mMemoryCache.getMemoryBitMap(imgPath);
        if (bitmap != null && img.getTag().equals(imgPath)) {
            img.setImageBitmap(bitmap);
            return;
        }
        bitmap = mLocalCache.getDiskCacheBitMap(imgPath);
        if (bitmap != null && img.getTag().equals(imgPath)) {
            img.setImageBitmap(bitmap);
            return;
        }
         mNetWorkCache.getNetWorkBitMap(imgPath,img);
        //TODO 加载失败
    }

}
  • 内存缓存 MemoryCacheUtils .java类代码实现:
public class MemoryCacheUtils {
    //    private HashMap<String, Bitmap> memoryHashMap = new HashMap<>();  //强引用存储图片 浪费内存
//    private HashMap<String, SoftReference<Bitmap>> softReferenceHashMap = new HashMap<>();   //用图片存储采取软引用 系统内存紧张的时候会回收
    private LruCache<String, Bitmap> mMemoryLruCache;   // Android2.3+后,系统会优先考虑回收弱引用对象,官方提出使用LruCache  所以在用SoftReference不在靠谱

    public MemoryCacheUtils() {

        int maxMemory = (int) (Runtime.getRuntime().maxMemory());  //最大可用虚拟内存,超过这一个数值将会抛出OutOfMemory / 1024
        int cacheSize = maxMemory / 8; //使用第1/8的可用内存为这个内存缓存。
        mMemoryLruCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount(); //缓存大小将以千字节为单位而不是项目数。  / 1024
            }
        };
    }

    /***
     * 读取缓存中的图片
     *
     * @param imgPath
     * @return
     */
    public Bitmap getMemoryBitMap(String imgPath) {
//        memoryHashMap.get(imgPath);
//        SoftReference<Bitmap> softReference = softReferenceHashMap.get(imgPath);
//        if (softReference != null) {
//            return softReference.get();
//        }
        Bitmap bitmap = mMemoryLruCache.get(imgPath);
        if (bitmap != null) {
            return bitmap;
        }
        return null;
    }

    /**
     * 缓存中存储图片
     *
     * @param imgPath
     * @param bitmap
     */
    public void setMemoryBitMap(String imgPath, Bitmap bitmap) {
//        memoryHashMap.put(imgPath, bitmap);
//        softReferenceHashMap.put(imgPath, new SoftReference(bitmap));
        if (getMemoryBitMap(imgPath) == null) {
            mMemoryLruCache.put(imgPath, bitmap);
        }

    }

}
  • 本地缓存 DiskCacheUtils.java类实现:
    public class DiskCacheUtils {
    private final Object mDiskCacheLock = new Object();
    private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
    private static final String DISK_CACHE_SUBDIR = "DiskCache";
    private Context mContext;

    public DiskCacheUtils(Context context) {

    }

    /***
     * 获取内存中的图片
     *
     * @param imgPath
     * @return
     */
    public Bitmap getDiskCacheBitMap(String imgPath) {
        try {
            File file = LoadImageUtils.getDiskDir(mContext, DISK_CACHE_SUBDIR);

            if (!file.exists()) {
                file.exists();
            }

            Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
            if (bitmap != null) {
                return bitmap;
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            Log.e("-->>>路径000","====000" +e.getMessage());
        }

        return null;
    }

    /***
     * 把图片缓存在本地
     *
     * @param imgPath
     * @param bitMap
     */
    public void addBitmapToCache(String imgPath, Bitmap bitMap) {
        Log.e("----=====","=======addBitmapToCache");
        if (getDiskCacheBitMap(imgPath) != null) {
            return;
        }
        try {
            //TODO  应该判断是否有SD卡
            File file = LoadImageUtils.getDiskDir(mContext, DISK_CACHE_SUBDIR);
            if (!file.exists()) {
                file.mkdirs();
            }
//            File cacheFile = new File(file,imgPath);
            bitMap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(file));
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
  • 网络图片请求,并且缓存之内存中和本地 NetWorkCacheUtils .java:
public class NetWorkCacheUtils {
    private DiskCacheUtils mLocaCache;
    private MemoryCacheUtils mMemoryCache;
    private static final int pixelsWidth = 480, pixelsHeight = 800;   //默认手机分辨率设置为 480 * 800  后边可以更改为自动获取

    public NetWorkCacheUtils(DiskCacheUtils localCache, MemoryCacheUtils memoryCache) {
        this.mLocaCache = localCache;
        this.mMemoryCache = memoryCache;
    }


    public void getNetWorkBitMap(final String imgPath, final ImageView img) {
        Log.e("-------", "====" + imgPath);
        //下载网络图片 然后返回 为了简单起见直接用OkHttp 下载了  主要是为了写三级缓存 先不管文件下载
        OkHttpUtils._downloadAsyn(imgPath, new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //错误了不做任何处理
                Log.e("出错了-->", "--->>>>出错啦出错啦");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    InputStream is = response.body().byteStream();
                    if (is == null) {
                        return;
                    }
                    BitmapFactory.Options options = new BitmapFactory.Options();
                    options.inJustDecodeBounds = true;   //设置为true 先不把图片读到内存中
                    BitmapFactory.decodeFile(imgPath, options);
                    //获取图片原始的宽高
                    int width = options.outWidth;
                    int height = options.outHeight;
                    int inSampleSize = 1;
                    if (height > pixelsHeight || width > pixelsWidth) {
                        int heightRatio = Math.round((float) height / (float) pixelsHeight);
                        int widthRatio = Math.round((float) width / (float) pixelsWidth);
                        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
                    }
                    options.inSampleSize = inSampleSize;
                    options.inJustDecodeBounds = false;
                    options.inSampleSize = 2;  //将宽度和高度 压缩成原来的1/2
                    options.inPreferredConfig = Bitmap.Config.ARGB_4444;
                    final Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
                    if (bitmap != null) {
                        mLocaCache.addBitmapToCache(imgPath, bitmap);
                        mMemoryCache.setMemoryBitMap(imgPath, bitmap);

                        if (img.getTag().equals(imgPath)) {  //避免加载图片错位
                            img.post(new Runnable() {
                                @Override
                                public void run() {
                                    img.setImageBitmap(bitmap);
                                }
                            });
                        } else {
                            img.post(new Runnable() {
                                @Override
                                public void run() {
                                    img.setBackgroundColor(Color.parseColor("#FF80AB"));
                                }
                            });
                        }
                    }
                }
            }
        });
    }


}

项目代码查看github

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值