30.android 三级缓存

导读:本篇文章讲解 30.android 三级缓存,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

1.三级缓存(网络+LruCache(原理分析)+DeskLruCache)

当手机上需要显示大量图片若是不停的进行网络请求,
很快就会OOM,这时三级缓存显得尤为重要,
适时地利用资源,进行图片缓存

1.三级缓存的顺序 
(1)内存缓存: 比如说需要加载图片时,系统第一步不会直接网络请求,而是首先找第一级缓存—内存缓存 
(2)本地缓存: 如果内存缓存中没有,就会从第二级缓存—本地缓冲(即sd卡) 
(3)网络缓存: 如果本地缓存中没有,就会从网络缓存中下载图片。

2. 三级缓存级别总结 
(1)内存缓存: 速度快, 优先读取 
(2)本地缓存: 速度其次, 内存没有,读本地 
(3)网络缓存: 速度最慢, 本地也没有,才访问网络

 

    1. 网络缓存

         
(1)doInBackground : 核心方法,请求网络。

(2)onPostExecute: 核心方法,图片加载完成后,显示在手机屏幕上。

 

  2. 本地缓存

(1)putBitmapToLocal: 这里我们将每个存储图片的文件名设为 图片对应的url地址(MD5加密后的),
判断父文件是否存在,不存在则新建,存在则直接存储进去。

(2)getBitmapToLocal: 先从方法参数中获取到图片对应的url,
进行查找,若存在则将图片的Bitmap返回回去(最好返回前先压缩),不存在则返回null。

3. 内存缓存

HashMap版
两个方法,一个是设置内存缓存方法—putBitmapToLocal,一个是取内存缓存方法—getBitmapToLocal。
用对象来存储图片,集合来存储对象,集合都在内存里面,所以决定用集合。 

 软引用版
Bitmap对象虽存在于集合中,但我们每次都 new 一个新的Bitmap,如果有大量的图片,
集合内存根本不够,很快就会OOM,也就是内存溢出。
集合中有许多个对象,都被集合引用!这个引用一直在!垃圾回收器并不会回收,所以会导致内存溢出。
以上只是一方面,而且即使它会回收这些引用的集合,
可它是隔一段时间才会回收,无法及时清理内存!

内存缓存中的 引用级别 

(1) 强引用 默认引用, 即使内存溢出,也不会回收 

(2) 软引用 SoftReference, 内存不够时, 会考虑回收 

(3) 弱引用 WeakReference 内存不够时, 更会考虑回收 

(4)虚引用 PhantomReference 内存不够时, 最优先考虑回收!

回收器断然不会回收强引用,而虚引用则太容易被回收,所以最常用的是软引用 和 弱引用,
在需求不强烈或内存实在是不够的情况下,垃圾回收器才会回收引用的对象。
我们主要回收的是Bitmap对象,对集合进行包装,使用软引用。

        LruCache
自从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,
这让软引用和弱引用变得不再可靠。
这么说来即使内存很充分的情况下,也有优先回收弱引用和软引用。 

LruCache : 可以将最近最少使用的对象回收掉, 从而保证内存不会超出范围! 

 LruCache 的 核心 
最核心的地方其实就是维护一个 HashMap,再设置了一个全局变量 size来计算变量的总大小。
一旦超出大小,就开始删除对象,从而保证内存量在规定范围内!
 

 

      DiskLruCache
LruCache只是管理了内存中图片的存储与释放,如果图片从内存中被移除的话,
那么又需要从网络上重新加载一次图片,这显然非常耗时。
Google提供了一套硬盘缓存的解决方案:DiskLruCache(非Google官方编写,但获得官方认证)。
DiskLruCache并没有限制数据的缓存位置,可以自由地进行设定,
存储在SD卡上,即使缓存再多的数据也不会对手机的内置存储空间有任何影响。
DiskLruCache是不能new出实例的,如果我们要创建一个DiskLruCache的实例,
则需要调用它的open()方法。

 

//————————————–代码实现———————————

//—三级缓存工具类

public class ImageHelper {


    // 内存缓存池
    // private Map<String, SoftReference<Bitmap>> mCache = new
    // LinkedHashMap<String, SoftReference<Bitmap>>();

    // LRUCahce 池子
    private static LruCache<String, Bitmap> mCache;
    private static Handler mHandler;
    private static ExecutorService mThreadPool;
    private static Map<ImageView, Future<?>> mTaskTags = new LinkedHashMap<ImageView, Future<?>>();
    private Context mContext;

    public ImageHelper(Context context) {
        this.mContext = context;
        if (mCache == null) {
            // 最大使用的内存空间
            int maxSize = (int) (Runtime.getRuntime().freeMemory() / 4);
            mCache = new LruCache<String, Bitmap>(maxSize) {
                @Override
                protected int sizeOf(String key, Bitmap value) {
                    return value.getRowBytes() * value.getHeight();
                }
            };
        }

        if (mHandler == null) {
            mHandler = new Handler();
        }

        if (mThreadPool == null) {
            // 最多同时允许的线程数为3个
            mThreadPool = Executors.newFixedThreadPool(3);
        }
    }

    public void display(ImageView iv, String url) {
        // 1.去内存中取
        Bitmap bitmap = mCache.get(url);
        if (bitmap != null) {
            // 直接显示
            iv.setImageBitmap(bitmap);
            return;
        }

        // 2.去硬盘上取
        bitmap = loadBitmapFromLocal(url);
        if (bitmap != null) {
            // 直接显示
            iv.setImageBitmap(bitmap);
            return;
        }

        // 3. 去网络获取图片
        loadBitmapFromNet(iv, url);
    }

    private void loadBitmapFromNet(ImageView iv, String url) {
        // 开线程去网络获取
        // 使用线程池管理
        // new Thread(new ImageLoadTask(iv, url)).start();

        // 判断是否有线程在为 imageView加载数据
        Future<?> futrue = mTaskTags.get(iv);
        if (futrue != null && !futrue.isCancelled() && !futrue.isDone()) {
            System.out.println("取消 任务");
            // 线程正在执行
            futrue.cancel(true);
            futrue = null;
        }

        // mThreadPool.execute(new ImageLoadTask(iv, url));
        futrue = mThreadPool.submit(new ImageLoadTask(iv, url));
        // Future 和 callback/Runable
        // 返回值,持有正在执行的线程
        // 保存
        mTaskTags.put(iv, futrue);
        System.out.println("标记 任务");
    }

    class ImageLoadTask implements Runnable {

        private String mUrl;
        private ImageView iv;

        public ImageLoadTask(ImageView iv, String url) {
            this.mUrl = url;
            this.iv = iv;
        }

        @Override
        public void run() {
            // HttpUrlconnection
            try {
                // 获取连接
                HttpURLConnection conn = (HttpURLConnection) new URL(mUrl).openConnection();

                conn.setConnectTimeout(30 * 1000);// 设置连接服务器超时时间
                conn.setReadTimeout(30 * 1000);// 设置读取响应超时时间

                // 连接网络
                conn.connect();

                // 获取响应码
                int code = conn.getResponseCode();

                if (200 == code) {
                    InputStream is = conn.getInputStream();

                    // 将流转换为bitmap
                    Bitmap bitmap = BitmapFactory.decodeStream(is);

                    // 存储到本地
                    write2Local(mUrl, bitmap);

                    // 存储到内存
                    mCache.put(mUrl, bitmap);

                    // 图片显示:不可取
                    // iv.setImageBitmap(bitmap);
                    mHandler.post(new Runnable() {

                        @Override
                        public void run() {
                            // iv.setImageBitmap(bitmap);

                            display(iv, mUrl);
                        }
                    });
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    /**
     * 本地种去去图片
     *
     * @param url
     */
    private Bitmap loadBitmapFromLocal(String url) {
        // 去找文件,将文件转换为bitmap
        String name;
        try {
            name = MD5Encoder.encode(url);

            File file = new File(getCacheDir(), name);
            if (file.exists()) {

                Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());

                // 存储到内存
                mCache.put(url, bitmap);
                return bitmap;
            }

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;
    }

    private void write2Local(String url, Bitmap bitmap) {
        String name;
        FileOutputStream fos = null;
        try {
            name = MD5Encoder.encode(url);
            File file = new File(getCacheDir(), name);
            fos = new FileOutputStream(file);

            // 将图像写到流中
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                    fos = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private String getCacheDir() {
        String state = Environment.getExternalStorageState();
        File dir = null;
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            // 有sd卡
            dir = new File(Environment.getExternalStorageDirectory(), "/Android/data/" + mContext.getPackageName()
                    + "/icon");
        } else {
            // 没有sd卡
            dir = new File(mContext.getCacheDir(), "/icon");

        }

        if (!dir.exists()) {
            dir.mkdirs();
        }

        return dir.getAbsolutePath();
    }
}

 

//——————————–MD5工具类————————-

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();
    }
}

 

//——————————-在Activity里调用————————————

//url 是个图片地址。

String url = “http://img2.imgtn.bdimg.com/it/u=1278190881,1035053171&fm=15&gp=0.jpg”;
new ImageHelper(this).display(mImg,url);

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/118364.html

(0)
seven_的头像seven_bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!