简单实现Android图片三级缓存机制
2016-10-12 16:35
106 查看
用户在使用我们的APP时,通常会重复浏览一些图片,这时如果每一次浏览都需要通过网络获取图片,那么将会非常流量。为了节省用户流量,提高图片加载效率,我们通常使用图片三级缓存策略,即通过网络、本地、内存三级缓存图片,来减少不必要的网络交互,避免浪费流量。
网上已经有很多讲述图片三级缓存的策略,这次我也来实现一次三级缓存,其中用到了LRU+SoftReference关于LRU算法,可以参考我之前的博客LinkedHashMap最佳实践:LruCache。首先我将整个机制流程展示给大家:
下面是源码实现:
网络加载工具类 CastielHttpUtils.java
测试,调用我们的图片缓存工具 MainActivity.java
布局文件 activity_main
测试加载图片结果如下:
网上已经有很多讲述图片三级缓存的策略,这次我也来实现一次三级缓存,其中用到了LRU+SoftReference关于LRU算法,可以参考我之前的博客LinkedHashMap最佳实践:LruCache。首先我将整个机制流程展示给大家:
下面是源码实现:
/** * @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 Gesture之【触摸屏手势识别】操作!利用触摸屏手势实现一个简单切换图片的功能!
- android 简单实现获取网络图片
- Android:实现最简单的单指移动、双指缩放的图片组件
- android获取网络图片简单实现
- java和Android文件下载断点续传和图片下载代码实现,可直接复制简单实现
- 16—【Android游戏开发十六】Android Gesture之【触摸屏手势识别】操作!利用触摸屏手势实现一个简单切换图片的功能
- android中图片翻页效果简单的实现方法
- android-新闻客户端-离线下载的简单实现(图片部分)
- Android中简单实现从网络下在图片显示并保存在本地
- Android之使用ViewPager实现图片展示(最简单的)
- 【Android游戏开发十六】Android Gesture之【触摸屏手势识别】操作!利用触摸屏手势实现一个简单切换图片的功能!
- 【Android游戏开发十六】Android Gesture之【触摸屏手势识别】操作!利用触摸屏手势实现一个简单切换图片的功能!
- android 简单图片动画播放实现
- Android实现获取本机中所有图片(Loader,CursorLoader,LoaderManager,SimpleCursorAdapter的简单应用)
- 【Android2D游戏开发十六】(上文之触摸屏手势)详解Android Gesture 手势操作!利用手势实现一个简单切换图片的功能!
- 【Android游戏开发十六】Android Gesture之【触摸屏手势识别】操作!利用触摸屏手势实现一个简单切换图片的功能!
- (转)【Android游戏开发十六】Android Gesture之【触摸屏手势识别】操作!利用触摸屏手势实现一个简单切换图片的功能!
- Android中Handler类的简单使用,实现图片切换
- android中图片翻页效果简单的实现方法
- Android中简单实现选择图片并裁剪