图片的缓存和LruCache用法

为了保证内存的使用始终维持在一个合理的范围,通常会把被移除屏幕的图片进行回收处理。此时垃圾回收器也会认为你不再持有这些图片的引用,从而对这些图片进行GC操作(garbage collection:垃圾回收)。用这种思路来解决问题是非常好的,可是为了能让程序快速运行,在界面上迅速地加载图片,你又必须要考虑到某些图片被回收之后,用户又将它重新滑入屏幕这种情况。这时重新去加载一遍刚刚加载过的图片无疑是性能的瓶颈,你需要想办法去避免这个情况的发生。

这个时候,使用内存缓存技术可以很好的解决这个问题,它可以让组件快速地重新加载和处理图片。下面我们就来看一看如何使用内存缓存技术来对图片进行缓存,从而让你的应用程序在加载很多图片的时候可以提高响应速度和流畅性。

内存缓存技术对那些大量占用应用程序宝贵内存的图片提供了快速访问的方法。其中最核心的类是LruCache (此类在android-support-v4的包中提供) 。这个类非常适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。

在过去,开发者会经常使用一种非常流行的内存缓存技术的实现,即软引用或弱引用 (SoftReference or WeakReference)。但是现在已经不再推荐使用这种方式了,因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用和弱引用变得不再可靠。另外,Android 3.0 (API Level 11)中,图片的数据会存储在本地的内存当中,因而无法用一种可预见的方式将其释放,这就有潜在的风险造成应用程序的内存溢出并崩溃。


强引用:

    平时我们编程的时候例如:Object object=new Object();那object就是一个强引用了。如果一个对象具有强引用,
那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OOM异常,
使程序异常终止,也不会回收具有强引用的对象来解决内存不足问题。

软引用:

软引用类似于可有可无的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的
内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。 软引用可以和一个引
用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联
的引用队列中。

使用软引用能防止内存泄露,增强程序的健壮性。

弱引用:

内存不足时,会被回收。

虚引用:


最优先回收

强引用缓存图片:

//强引用:容易造成内存溢出,因为不会被回收,该对象的引用一直都在

private HashMap<String, Bitmap> mMemoryCache = new HashMap<String, Bitmap>();

  /**
     * 写内存缓存
     *
     * @param url
     * @param bitmap
     */
    public void setCache(String url, Bitmap bitmap) {
        mMemoryCache.put(url, bitmap);
    }


    /**
     * 读内存缓存
     *
     * @param url
     */
    public Bitmap getCache(String url) {
       return mMemoryCache.get(url);
    }

软引用缓存图片:

//软应用:使用软引用:内存不足时,会被回收。

 private HashMap<String, SoftReference<Bitmap>> mMemoryCache = new HashMap<>();

  /**
     * 写内存缓存
     *
     * @param url
     * @param bitmap
     */
    public void setCache(String url, Bitmap bitmap) {
        //使用软引用将bitmap包装起来
        SoftReference<Bitmap> soft = new SoftReference<Bitmap>(bitmap);
        mMemoryCache.put(url, soft);
    }


    /**
     * 读内存缓存
     *
     * @param url
     */
    public Bitmap getCache(String url) {
       SoftReference<Bitmap> softReference = mMemoryCache.get(url);
        if (softReference != null) {
            Bitmap bitmap = softReference.get();
            return bitmap;
        }
        return null;   
    }

从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收(内存足够时也会回收)持有软引用或弱引用的对象,
这让软引用和弱引用变得不再可靠。Google建议使用LruCache。

LruCache:内存缓存技术,这个类非常适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用
存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。
Lru: least recentlly used 最近最少使用算法
自己可以控制内存的大小,可以将最近最少使用的对象回收掉, 从而保证内存不会超出范围

 //LruCache;
    private LruCache<String, Bitmap> mMemoryCache;

    public MemoryCacheUtil() {
        // 获取分配给app的内存大小
        long maxMemory = Runtime.getRuntime().maxMemory();
        // 使用最大可用内存值的1/8作为缓存的大小。
        mMemoryCache = new LruCache<String, Bitmap>((int) (maxMemory / 8)) {

            //返回每个对象的大小
            @Override
            protected int sizeOf(String key, Bitmap value) {
                //int byteCount = value.getRowBytes() * value.getHeight();// 计算图片大小:每行字节数*高度
                int byteCount = value.getByteCount();
                return byteCount;
            }
        };
    }
 /**
     * 写内存缓存
     *
     * @param url
     * @param bitmap
     */
    public void setCache(String url, Bitmap bitmap) {
        mMemoryCache.put(url, bitmap);
    }


    /**
     * 读内存缓存
     *
     * @param url
     */
    public Bitmap getCache(String url) {
        return mMemoryCache.get(url);
    }

图片的三级缓存:
1.先从内存缓存中查找图片,如果找到,直接加载;
2.本地缓存;
3.网络缓存。

网络缓存:

package com.xiaoyehai.threadpool.util;

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

/**
 * 网络缓存工具类
 * Created by xiaoyehai on 2016/11/22.
 */
public class NetCacheUtils {

    private MemoryCacheUtil mMemoryCacheUtil;

    private LocalCacheUtils mLocalCacheUtils;

    public NetCacheUtils(MemoryCacheUtil mMemoryCacheUtil, LocalCacheUtils mLocalCacheUtils) {
        this.mMemoryCacheUtil = mMemoryCacheUtil;
        this.mLocalCacheUtils = mLocalCacheUtils;
    }

    public void getBitmapFromNet(ImageView imageView, String url) {
        new BitmapTask().execute(imageView, url); //启动AsyncTask
        imageView.setTag(url); //设置标记,处理错位
    }

    /**
     * AsyncTask:异步任务类,可以实现异步请求和主界面更新(Handler+线程池)
     * 1.doInBackground里面的参数类型
     * 2.onProgressUpdate里面的参数类型
     * 3.onPostExecute里面的参数类型及doInBackground的返回类型
     */
    class BitmapTask extends AsyncTask<Object, Integer, Bitmap> {

        private ImageView iv;
        private String url;

        @Override
        protected void onPreExecute() {
            // TODO: 2017/1/31 预加载,运行在主线程
            super.onPreExecute();
        }

        @Override
        protected Bitmap doInBackground(Object... params) {
            // TODO: 2017/1/31  正在加载,运行在子线程,可以直接异步请求
            iv = (ImageView) params[0];
            url = (String) params[1];

            //开始下载图片
            Bitmap bitmap = HttpUtils.loadBitmap(url);
            return bitmap;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            // TODO: 2017/1/31 更新进度的方法,运行在主线程
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            // TODO: 2017/1/31 加载结束,运行在主线程,可以直接更新ui
            String str_url = (String) iv.getTag();
            if (bitmap != null && str_url.equals(url)) {
                iv.setImageBitmap(bitmap);

                //缓存到本地
                mLocalCacheUtils.setCache(url, bitmap);
                //缓存到内存
                mMemoryCacheUtil.setCache(url, bitmap);
            }
            super.onPostExecute(bitmap);
        }
    }
}

本地缓存:

package com.xiaoyehai.threadpool.util;

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

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

/**
 * 本地(sd卡)缓存工具类
 * Created by xiaoyehai on 2016/11/22.
 */
public class LocalCacheUtils {

    //缓存的文件夹路径
    public static final String FILE_PATH = Environment.getExternalStorageDirectory()
            .getAbsolutePath() + "/image_cache";

    /**
     * 写本地缓存
     *
     * @param url
     * @param bitmap
     */
    public void setCache(String url, Bitmap bitmap) {
        File dir = new File(FILE_PATH);
        if (!dir.exists() || dir.isDirectory()) {
            dir.mkdirs();//创建文件夹
        }
        try {
            String fileName = MD5Encoder.encode(url);
            File cacheFile = new File(dir, fileName);
            // 参1:图片格式;参2:压缩比例0-100; 参3:输出流
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(cacheFile));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 读本地缓存
     *
     * @param url
     * @return
     */
    public Bitmap getCache(String url) {
        try {
            File cacheFile = new File(FILE_PATH, MD5Encoder.encode(url));
            if (cacheFile.exists()) {
                Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(cacheFile));
                return bitmap;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
public class MD5Encoder {

    public static String encode(String string) throws Exception {
        byte[] hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
        StringBuilder hex = new StringBuilder(hash.length * 2);
        for (byte b : hash) {
            if ((b & 0xFF) < 0x10) {
                hex.append("0");
            }
            hex.append(Integer.toHexString(b & 0xFF));
        }
        return hex.toString();
    }
}

内存缓存:

package com.xiaoyehai.threadpool.util;

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;

/**
 * 内存缓存
 * 从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收(内存足够时也会回收)持有软引用或弱引用的对象,
 * 这让软引用和弱引用变得不再可靠。Google建议使用LruCache。
 * <p>
 * Psrson p=new Person;强引用:不会被回收
 * 栈:存放对象的引用(p)
 * 堆:存放对象(new Person)
 * <p>
 * 垃圾回收器:只回收没有引用的对象,不及时
 * 强引用(默认):垃圾回收器不会回收
 * 软引用:内存不足时,会被回收。使用软引用能防止内存泄露,增强程序的健壮性。
 * 弱引用:内存不足时,会被回收。
 * 虚引用:最优先回收
 * <p>
 * LruCache:内存缓存技术,这个类非常适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用
 * 存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。
 * Lru: least recentlly used 最近最少使用算法
 * 自己可以控制内存的大小,可以将最近最少使用的对象回收掉, 从而保证内存不会超出范围
 * <p>
 * Created by xiaoyehai on 2016/11/22.
 */
public class MemoryCacheUtil {


    //强引用:容易造成内存溢出,因为不会被回收,该对象的引用一直都在
    //private HashMap<String, Bitmap> mMemoryCache = new HashMap<String, Bitmap>();

    //软应用:使用软引用:内存不足时,会被回收。
    //private HashMap<String, SoftReference<Bitmap>> mMemoryCache = new HashMap<>();


    //LruCache;
    private LruCache<String, Bitmap> mMemoryCache;

    public MemoryCacheUtil() {
        // 获取分配给app的内存大小
        long maxMemory = Runtime.getRuntime().maxMemory();
        // 使用最大可用内存值的1/8作为缓存的大小。
        mMemoryCache = new LruCache<String, Bitmap>((int) (maxMemory / 8)) {

            //返回每个对象的大小
            @Override
            protected int sizeOf(String key, Bitmap value) {
                //int byteCount = value.getRowBytes() * value.getHeight();// 计算图片大小:每行字节数*高度
                int byteCount = value.getByteCount();
                return byteCount;
            }
        };
    }


    /**
     * 写内存缓存
     *
     * @param url
     * @param bitmap
     */
    public void setCache(String url, Bitmap bitmap) {
        //mMemoryCache.put(url, bitmap);

        //使用软引用将bitmap包装起来
//        SoftReference<Bitmap> soft = new SoftReference<Bitmap>(bitmap);
//        mMemoryCache.put(url, soft);

        mMemoryCache.put(url, bitmap);
    }


    /**
     * 读内存缓存
     *
     * @param url
     */
    public Bitmap getCache(String url) {
        //return mMemoryCache.get(url);

//        SoftReference<Bitmap> softReference = mMemoryCache.get(url);
//        if (softReference != null) {
//            Bitmap bitmap = softReference.get();
//            return bitmap;
//        }
//        return null;

        return mMemoryCache.get(url);
    }
}

BitmapUtils:

package com.xiaoyehai.threadpool.util;

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

import com.xiaoyehai.threadpool.R;


/**
 * 自定义具有三级缓存功能的图片加载工具类
 * <p>
 * 1.内存缓存:速度最快,不耗流量
 * 2.本地sd卡缓存:速度快,不耗流量
 * 3.网络缓存:速度慢,浪费流量
 * Created by xiaoyehai on 2016/11/22.
 */
public class BitmapUtils {

    private MemoryCacheUtil mMemoryCacheUtil;

    private LocalCacheUtils mLocalCacheUtils;

    private NetCacheUtils mNetCacheUtils;

    public BitmapUtils() {
        mMemoryCacheUtil = new MemoryCacheUtil();
        mLocalCacheUtils = new LocalCacheUtils();
        mNetCacheUtils = new NetCacheUtils(mMemoryCacheUtil, mLocalCacheUtils);
    }

    public void display(ImageView imageView, String url) {
        //设置默认图片
        imageView.setImageResource(R.mipmap.ic_launcher);

        //一级缓存:内存缓存中获取数据
        Bitmap bitmap = mMemoryCacheUtil.getCache(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }

        //二级缓存:从本地(sd卡)缓存中获取数据
        bitmap = mLocalCacheUtils.getCache(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);

            //写内存缓存
            mMemoryCacheUtil.setCache(url, bitmap);
            return;
        }

        //三级缓存:网络获取数据
        mNetCacheUtils.getBitmapFromNet(imageView, url);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_36699930/article/details/80405852