分享好友 移动开发首页 频道列表

简单实现 Android 图片三级缓存机制

Android开发  2016-10-13 09:100

用户在使用我们的APP时,通常会重复浏览一些图片,这时如果每一次浏览都需要通过网络获取图片,那么将会非常流量。为了节省用户流量,提高图片加载效率,我们通常使用图片三级缓存策略,即通过网络、本地、内存三级缓存图片,来减少不必要的网络交互,避免浪费流量。

网上已经有很多讲述图片三级缓存的策略,这次我也来实现一次三级缓存,其中用到了LRU+SoftReference关于LRU算法,可以参考我之前的博客 LinkedHashMap最佳实践:LruCache 。首先我将整个机制流程展示给大家:

 简单实现 Android 图片三级缓存机制

下面是源码实现:

/**
  * @ClassName: CastielImageLoader 
  * @Description: LRU+SoftReference
  * @author 猴子搬来的救兵 http://blog.csdn.net/mynameishuangshuai
  */
public class CastielImageLoader {
    private static final int MAX_CAPACITY = 20;// 链表长度
    private static Context mContext;// 获取APP的缓存地址
    private static CastielImageLoader castielImageLoader;
    // 键是图片地址、值是软引用
    private static final LinkedHashMap<String, SoftReference<Bitmap>> firstCacheMap = new LinkedHashMap<String, SoftReference<Bitmap>>(
            MAX_CAPACITY) {
        protected boolean removeEldestEntry(java.util.Map.Entry<String, java.lang.ref.SoftReference<Bitmap>> eldest) {
            // 返回true表示移除最老的软引用,保证内存平衡
            if (this.size() > MAX_CAPACITY) {
                return true;
            } else {// 否则往磁盘中添加
                diskCache(eldest.getKey(), eldest.getValue());
                return false;
            }
        }
    };

    /**
     * 单例模式加载CastielImageLoader
     * @return
     */
    public static CastielImageLoader getInstance() {
        if (castielImageLoader == null) {
            castielImageLoader = new CastielImageLoader();
        }
        return castielImageLoader;
    }
    /**
     * 加载图片到对应组件
     * 
     * @param key 所需加载的路径
     * @param view 被加载的组件
     * @param drawable 没有加载前默认显示图片
     */
    @SuppressWarnings("deprecation")
    public void loadImage(String key, ImageView view, Drawable drawable ,Context context) {
        mContext = context;
        synchronized (view) {
            // 检查缓存中是否已有
            Bitmap bitmap = getFromCache(key);
            if (bitmap != null) {
                // 如果有了就从缓存中取出显示
                view.setImageBitmap(bitmap);
            } else {
                // 软应用缓存中不存在,磁盘中也不存在,只能下载
                // 下载之前应该先放一张默认图,用来友好显示
                view.setBackgroundDrawable(drawable);
                // 用异步任务去下载
                new CastielAsyncImageLoaderTask(view).execute(key);
            }
        }
    }

    /**
     * 判断缓存中是否已经有了,如果有了就从缓存中取出
     * 
     * @param key
     * @return
     */
    private Bitmap getFromCache(String key) {
        // 检查内存软引中是否存在
        synchronized (firstCacheMap) {
            if (firstCacheMap.get(key) != null) {// 内存软引用中有
                Bitmap bitmap = firstCacheMap.get(key).get();
                if (bitmap != null) {// 说明拿到了
                    firstCacheMap.put(key, new SoftReference<Bitmap>(bitmap));
                    return bitmap;
                }
            }
        }
        // 检查磁盘中是否存在
        Bitmap bitmap = getFromLocalSD(key);
        if (bitmap != null) {// 硬盘中有
            firstCacheMap.put(key, new SoftReference<Bitmap>(bitmap));
            return bitmap;
        }
        return null;
    }

    /**
     * 判断本地磁盘中是否已经有了该图片,如果有了就从本地磁盘中取出
     * @param key
     * @return
     */
    private Bitmap getFromLocalSD(String key) {
        String fileName = MD5Util.getMD5Str(key);
        if (fileName == null) {// 如果文件名为Null,直接返回null
            return null;
        } else {
            String filePath = mContext.getCacheDir().getAbsolutePath() + File.separator + fileName;
            InputStream is = null;
            try {
                is = new FileInputStream(new File(filePath));
                Bitmap bitmap = BitmapFactory.decodeStream(is);
                return bitmap;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * 把图片缓存到本地磁盘,拿到图片,写到SD卡中
     * 
     * @param key 图片的URL
     * @param value Bitmap
     */
    private static void diskCache(String key, SoftReference<Bitmap> value) {
        // 把写入SD的图片名字改为基于MD5加密算法加密后的名字
        String fileName = MD5Util.getMD5Str(key);
        String filePath = mContext.getCacheDir().getAbsolutePath() + File.separator + fileName;
        FileOutputStream os = null;
        try {
            os = new FileOutputStream(new File(filePath));
            if (value.get() != null) {
                value.get().compress(Bitmap.CompressFormat.JPEG, 60, os);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 
     * @ClassName: MyAsyncImageLoaderTask 
     * @Description: 异步加载图片 
     * @author 
     */
    class CastielAsyncImageLoaderTask extends AsyncTask<String, Void, Bitmap>{

        private ImageView imageView;// 图片组件
        private String key;//图片路径

        public CastielAsyncImageLoaderTask(ImageView imageView) {
            this.imageView = imageView;
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            this.key = params[0];// 图片的路径
            Bitmap bitmap = castielDownload(key);
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap result) {
            super.onPostExecute(result);
            if (result != null) {// 说明已经下载下来了
                addFirstCache(key,result);
                imageView.setImageBitmap(result);// 加载网络中的图片
            }
        }
    }

    /**
     * 根据图片路径执行图片下载
     * @param key
     * @return
     */
    public Bitmap castielDownload(String key) {
        InputStream is = null;
        try {
            is = CastielHttpUtils.castielDownLoad(key);
            return BitmapFactory.decodeStream(is);// InputStream这种加载方式暂用内存最小
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 添加到缓存中去
     * @param key
     * @param result
     */
    public void addFirstCache(String key, Bitmap result) {
        if (result != null) {
            synchronized (firstCacheMap) {
                firstCacheMap.put(key, new SoftReference<Bitmap>(result));
            }
        }
    }
}

网络加载工具类 CastielHttpUtils.java

public class CastielHttpUtils {
    public static InputStream castielDownLoad(String key) throws IOException{
        HttpURLConnection conn = (HttpURLConnection) new URL(key).openConnection();
        return conn.getInputStream();
    }
}

测试,调用我们的图片缓存工具 MainActivity.java

public class MainActivity extends Activity {

    ImageView img;
    String imgURl = "http://img2.imgtn.bdimg.com/it/u=3722998253,3365379445&fm=21&gp=0.jpg";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        img = (ImageView) findViewById(R.id.img);
        CastielImageLoader.getInstance().loadImage(imgURl, img, this.getResources().getDrawable(R.drawable.ic_launcher),MainActivity.this);
    }

}

布局文件 activity_main

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="猴子搬来的救兵 http://blog.csdn.net/mynameishuangshuai" />

    <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tv"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="80dp"
        android:src="@drawable/tt" />

</RelativeLayout>

测试加载图片结果如下:

 简单实现 Android 图片三级缓存机制

查看更多关于【Android开发】的文章

展开全文
相关推荐
反对 0
举报 0
评论 0
图文资讯
热门推荐
优选好物
更多热点专题
更多推荐文章
AsyncTask 工作原理(上)
AsyncTask 是一种轻量级是异步任务类,它可以在线程池中执行后台任何,将执行的进度和最终结果传递给主线程,并在主线程中更新UI。AsyncTask 是一个抽象类,其构造函数//Params:传入doInBackground 中的参数类型//Progress: 后台执行进度的类型,传入onProgre

0评论2016-11-10163

vysor 原理以及 Android 同屏方案
vysor是一个免root实现电脑控制手机的chrome插件,目前也有几款类似的通过电脑控制手机的软件,不过都需要root权限,并且流畅度并不高。vysor没有多余的功能,流畅度也很高,刚接触到这款插件时我惊讶于它的流畅度以及免root,就一直对它的实现原理很感兴趣。

0评论2016-10-13222

Picasso 解析 (1)- 一张图片是如何加载出来的
前言Picasso是JakeWharton大神在github上的一个开源图片加载框架,使用起来极其方便,甚至只需要一行代码就可以搞定图片加载:Picasso.with(context).load(http://i.imgur.com/DvpvklR.png).into(imageView);具体如何使用该框架我就不在这里赘述了,大家可以

0评论2016-10-04251

Android 图片缓存之初识 Glide(三)
前言:前面总结学习了图片的使用以及Lru算法,今天来学习一下比较优秀的图片缓存开源框架。技术本身就要不断的更迭,从最初的自己使用SoftReference实现自己的图片缓存,到后来做电商项目自己的实现方案不能满足项目的需求改用Afinal,由于Afinal不再维护而选

0评论2016-08-3091

Android编程实现擦除Bitmap中某一块的方法
这篇文章主要介绍了Android编程实现擦除Bitmap中某一块的方法,涉及Android操作Bitmap颜色像素值调整的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下

0评论2015-11-2588

android中Bitmap用法(显示,保存,缩放,旋转)实例分析
这篇文章主要介绍了android中Bitmap用法,以实例形式较为详细的分析了android中Bitmap操作图片的显示、保存、缩放、旋转等相关技巧,需要的朋友可以参考下

0评论2015-11-08152

Android生成带圆角的Bitmap图片
这篇文章主要介绍了Android生成带圆角的Bitmap图片,涉及Android通过Canvas实现绘制带圆角的图片相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下

0评论2015-09-13127

Afianl框架里面的FinalBitmap加载网络图片
这篇文章主要介绍了Afianl框架里面的FinalBitmap加载网络图片的相关资料,需要的朋友可以参考下

0评论2015-09-11124

Android Bitmap详细介绍
Android中Bitmap的常见操作整理一览,需要的朋友可以参考下

0评论2015-09-07112

Android界面 NotificationManager使用Bitmap做图标
Android界面 NotificationManager使用Bitmap做图标,如何实现呢,本文将介绍解决方法,需要的朋友可以参考下

0评论2015-09-05120

基于android示例程序(bitmapfun) 高效加载图片让人无语地方
尝试了使用git上的一个开源项目afinal(bitmapfun的封装版)来加载图片,但是在测试的时候发现了一个问题,新的图片加载器(bitmapfun)比之前用的ImageDownloader要慢很多,特别是在网络状况不好的时候,那简直是太让人无语了

0评论2015-08-2475

更多推荐