一、问题描写叙述

使用LruCache、AsyncTask实现批量图片的载入并达到下列技术要求

1、从缓存中读取图片,若不在缓存中,则开启异步线程(AsyncTask)载入图片,并放入缓存中

2、及时移除无效的异步线程;保证异步载入图片时不会乱序

3、仅仅对当前屏幕可见部分进行缓存、异步载入图片

4、优化性能杜绝OOM

二、案例介绍

案例实现照片墙效果

三、主要技术

  LruCache

  内存缓存技术。在Android中 专门用来做图片缓存处理的组件,主要使用步骤

  (1) 设置缓存图片的内存大小,如设置为手机内存的1/8(当缓存的图片达到了预先设定的值的时候。那么最近使用次数最少的图片就会被回收掉)代码例如以下:

// 获取应用程序最大可用内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;
// 设置图片缓存大小为程序最大可用内存的1/8
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap bitmap) {return bitmap.getByteCount();}};

(2) 将图片放入缓存中(LruCache里面的键值对通常各自是URL和相应的图片)

mMemoryCache.put(key, bitmap);

(3) 从缓存中取图片

mMemoryCache.get(key);

  AsyncTask

  进行耗时操作比方载入图片要求不要堵塞UI线程。就必须使用异步任务。AsyncTask是不须要借助thread+handler就可以实现异步任务的组件,使用起来比較简单且更轻量级一些。实现AsyncTask过程例如以下:

  (1) 扩展子AsyncTask,如

  class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap>

  (2) 重写AsyncTask中的几个方法  

  onPreExecute(), 该方法将在运行实际的后台操作前被UI线程调用。

能够在该方法中做一些准备工作,如在界面上显示一个进度条。

  doInBackground(Params...), 将在onPreExecute 方法运行后、运行。该方法运行在后台线程中,主要负责运行那些非常耗时的后台计算工作。如载入图片

  onPostExecute(Result), 在doInBackground 运行完毕后。该将被UI线程调用,后台的计算结果将通过该方法传递到UI线程

  (3) 在UI线程中通过调用AsyncTask的execute(Params…);

  開始运行异步任务并向后台任务传入数据。运行次序  onPreExecute()——> doInBackground(Params...)——> onPostExecute(Result)

  经验:对大量图片载入。要求每一个图片都要在一个后台任务中取执行,因此须要使用集合记录全部正在下载或等待下载的任务

  Set<BitmapWorkerTask> taskCollection=new HashSet< BitmapWorkerTask >();

  当缓存中没有图片。加入任务并启动异步处理。片段代码如:

if (bitmap == null) {//假设缓存没有BitmapWorkerTask task = new BitmapWorkerTask();taskCollection.add(task);task.execute(imageUrl);//运行异步任务,并传入载入的图片url地址}

  要注意及时移除已完毕的任务,例如以下代码

protected void onPostExecute(Bitmap bitmap) {….taskCollection.remove(this);}

  而且在适配器控件滑动停止时取消正在运行的任务。片段代码例如以下:

public void onScrollStateChanged(AbsListView view, int scrollState) {// 仅当GridView精巧时才去下载图片,GridView滑动时取消全部正在下载的任务if (scrollState == SCROLL_STATE_IDLE) {loadBitmaps(mFirstVisibleItem, mVisibleItemCount);} else {cancelAllTasks();//取消全部正在下载或等待下载的任务。}}public void cancelAllTasks() {if (taskCollection != null) {for (BitmapWorkerTask task : taskCollection) {task.cancel(false);}}}

三、完整代码

  1、 MainActivity

public class MainActivity extends Activity {private GridView mPhotoWall;private PhotoWallAdapter adapter;
private ArrayList<File> list;protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);list = new ArrayList<File>();getAllFiles(new File("/sdcard"));mPhotoWall = (GridView) findViewById(R.id.photo_wall);adapter = new PhotoWallAdapter(this, 0, list, mPhotoWall);mPhotoWall.setAdapter(adapter);}/*** 获得指定文件夹下图片文件*/private void getAllFiles(File root) {File files[] = root.listFiles();if (files != null)for (File f : files) {if (f.isDirectory()) {getAllFiles(f);} else {if (f.getName().indexOf(".png") > 0|| f.getName().indexOf(".jpg") > 0|| f.getName().indexOf(".jpeg") > 0)this.list.add(f);}}}protected void onDestroy() {super.onDestroy();adapter.cancelAllTasks();// 退出程序时结束全部的下载任务}
}

2、PhotoWallAdapter适配器

public class PhotoWallAdapter extends ArrayAdapter<File> implementsOnScrollListener {//记录全部正在下载或等待下载的任务。

private Set<BitmapWorkerTask> taskCollection; // 图片缓存技术的核心类。用于缓存全部下载好的图片,在程序内存达到设定值时会//将最少近期使用的图片移除掉。 private LruCache<String, Bitmap> mMemoryCache; //GridView的实例 private GridView mPhotoWall; //第一张可见图片的下标 private int mFirstVisibleItem; // 一屏有多少张图片可见 private int mVisibleItemCount; // 记录是否刚打开程序,用于解决进入程序不滚动屏幕。不会下载图片的问题。 private boolean isFirstEnter = true; ArrayList<File> list = null; public PhotoWallAdapter(Context context, int textViewResourceId, ArrayList<File> objects, GridView photoWall) { super(context, textViewResourceId, objects); mPhotoWall = photoWall; list = objects; taskCollection = new HashSet<BitmapWorkerTask>(); // 获取应用程序最大可用内存 int maxMemory = (int) Runtime.getRuntime().maxMemory(); int cacheSize = maxMemory / 8; // 设置图片缓存大小为程序最大可用内存的1/8 mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { return bitmap.getByteCount(); } }; mPhotoWall.setOnScrollListener(this); } public View getView(int position, View convertView, ViewGroup parent) { final File url = getItem(position); View view; if (convertView == null) { view = LayoutInflater.from(getContext()).inflate( R.layout.photo_layout, null); } else { view = convertView; } final ImageView photo = (ImageView) view.findViewById(R.id.photo); // 给ImageView设置一个Tag,保证异步载入图片时不会乱序 photo.setTag(url.getAbsolutePath()); setImageView(url.getAbsolutePath(), photo); return view; } /** * 给ImageView设置图片。首先从LruCache中取出图片的缓存,设置到ImageView上。假设LruCache中没有该图片的缓存。 * 就给ImageView设置一张默认图片。 * @param imageUrl * 图片的URL地址。用于作为LruCache的键。 * @param imageView * 用于显示图片的控件。 */ private void setImageView(String imageUrl, ImageView imageView) { Bitmap bitmap = getBitmapFromMemoryCache(imageUrl); if (bitmap != null) { imageView.setImageBitmap(bitmap); } else { bitmap=getLoacalBitmap(imageUrl); imageView.setImageResource(R.drawable.empty_photo); } } /** * 将一张图片存储到LruCache中。

* @param key * LruCache的键,这里传入图片的URL地址。 * @param bitmap * LruCache的键,这里传入从网络上下载的Bitmap对象。 */ @SuppressLint("NewApi") public void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemoryCache(key) == null) { mMemoryCache.put(key, bitmap); } } /** * 从LruCache中获取一张图片,假设不存在就返回null。 * @param key * LruCache的键,这里传入图片的URL地址。 * @return 相应传入键的Bitmap对象,或者null。 */ @SuppressLint("NewApi") public Bitmap getBitmapFromMemoryCache(String key) { return mMemoryCache.get(key); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // 仅当GridView精巧时才去下载图片,GridView滑动时取消全部正在下载的任务 if (scrollState == SCROLL_STATE_IDLE) { loadBitmaps(mFirstVisibleItem, mVisibleItemCount); } else { cancelAllTasks(); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mFirstVisibleItem = firstVisibleItem; mVisibleItemCount = visibleItemCount; // 下载的任务应该由onScrollStateChanged里调用。但首次进入程序时onScrollStateChanged并不会调用, // 因此在这里为首次进入程序开启下载任务。 if (isFirstEnter && visibleItemCount > 0) { loadBitmaps(firstVisibleItem, visibleItemCount); isFirstEnter = false; } } /** * 载入Bitmap对象。此方法会在LruCache中检查全部屏幕中可见的ImageView的Bitmap对象, * 假设发现不论什么一个ImageView的Bitmap对象不在缓存中,就会开启异步线程去下载图片。 * * @param firstVisibleItem * 第一个可见的ImageView的下标 * @param visibleItemCount * 屏幕中总共可见的元素数 */ private void loadBitmaps(int firstVisibleItem, int visibleItemCount) { try { for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) { String imageUrl = list.get(i).getAbsolutePath(); Bitmap bitmap = getBitmapFromMemoryCache(imageUrl); if (bitmap == null) {//假设缓存没有 BitmapWorkerTask task = new BitmapWorkerTask(); taskCollection.add(task); task.execute(imageUrl);//运行异步任务,并传入载入的图片url地址(这里是sd卡上的图片) } else { ImageView imageView = (ImageView) mPhotoWall .findViewWithTag(imageUrl); if (imageView != null && bitmap != null) { imageView.setImageBitmap(bitmap); } } } } catch (Exception e) { e.printStackTrace(); } } /** * 取消全部正在下载或等待下载的任务。 */ public void cancelAllTasks() { if (taskCollection != null) { for (BitmapWorkerTask task : taskCollection) { task.cancel(false); } } } /** * 异步下载图片的任务。 */ class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> { /** * 图片的URL地址 */ private String imageUrl; @Override protected Bitmap doInBackground(String... params) { imageUrl = params[0]; // 在后台開始下载图片 Bitmap bitmap = getLoacalBitmap(params[0]); if (bitmap != null) { // 图片下载完毕后缓存到LrcCache中 addBitmapToMemoryCache(params[0], bitmap); } return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); // 依据Tag找到相应的ImageView控件,将下载好的图片显示出来。 ImageView imageView = (ImageView) mPhotoWall .findViewWithTag(imageUrl); if (imageView != null && bitmap != null) { imageView.setImageBitmap(bitmap); } taskCollection.remove(this); } } private Bitmap getLoacalBitmap(String url) { try { FileInputStream fis = new FileInputStream(url); return BitmapFactory.decodeStream(fis); // /把流转化为Bitmap图片 } catch (FileNotFoundException e) { e.printStackTrace(); return null; } } }

  想要了解很多其它内容的小伙伴,能够点击查看源代码,亲自执行測试。

  疑问咨询或技术交流,请增加官方QQ群: (452379712)

作者:杰瑞教育
出处:http://blog.csdn.net/jerehedu/ 
本文版权归烟台杰瑞教育科技有限公司和CSDN共同拥有,欢迎转载,但未经作者允许必须保留此段声明。且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

Android批量图片载入经典系列——使用LruCache、AsyncTask缓存并异步载入图片相关推荐

  1. Android批量图片载入经典系列——afinal框架实现图片的异步缓存载入

    一.问题描写叙述 在之前的系列文章中,我们使用了Volley和Xutil框架实现图片的缓存载入,接下来我们再介绍一下afinal 框架的使用. Afinal 是一个android的http框架.sql ...

  2. Android Glide图片加载框架(三)缓存机制

    文章目录 一.缓存简介 二.缓存用法 内存缓存方式 磁盘缓存方式 三.缓存KEY 四.内存缓存 内存缓存流程 五.磁盘缓存 磁盘缓存流程 Android Glide图片加载框架系列文章 Android ...

  3. Android批量图片加载经典系列——使用LruCache、AsyncTask缓存并异步加载图片

    一.问题描述 使用LruCache.AsyncTask实现批量图片的加载并达到下列技术要求 1.从缓存中读取图片,若不在缓存中,则开启异步线程(AsyncTask)加载图片,并放入缓存中 2.及时移除 ...

  4. Android批量图片加载经典系列——使用二级缓存、异步网络负载形象

    一.问题描写叙述 Android应用中常常涉及从网络中载入大量图片,为提升载入速度和效率,降低网络流量都会採用二级缓存和异步载入机制.所谓二级缓存就是通过先从内存中获取.再从文件里获取,最后才会訪问网 ...

  5. android异步加载图片并缓存到内存和sd卡上,Android批量图片加载经典系列——采用二级缓存、异步加载网络图片...

    http://www.cnblogs.com/jerehedu/p/4560119.html 2015-06-08 09:20 by 杰瑞教育, 232 阅读, 1 评论, 收藏, 编辑一.问题描述 ...

  6. Android—Bitmap图片大小计算、压缩与三级缓存

    Bitmap对象占用内存大小: bitmap.getByteCount() 图片所占内存大小计算方式:图片长度 x 图片宽度 x 一个像素点占用的字节数. Android Bitmap使用的三种颜色格 ...

  7. android批量文件上传(android批量图片上传)

    项目中多处用到文件批量上传功能,今天正好解决了此问题,在此写出来,以便日后借鉴. 首先,以下架构下的批量文件上传可能会失败或者不会成功:1.android客户端+springMVC服务端:服务端采用o ...

  8. Android Glide图片加载框架(四)回调与监听

    文章目录 Android Glide图片加载框架系列文章 Android Glide图片加载框架(一)基本用法 Android Glide图片加载框架(二)源码解析之with() Android Gl ...

  9. Android Glide图片加载框架(二)源码解析之into()

    文章目录 一.前言 二.源码解析 1.into(ImageView) 2.GlideContext.buildImageViewTarget() 3.RequestBuilder.into(Targe ...

最新文章

  1. Halcon知识:如何求一个工件的粗细
  2. 高质量程序设计指南c++/c语言(14)--函数指针
  3. 调优为王!阿里巴巴彩版java性能调优实战,终于到手了!文末福利
  4. 堆排序算法知识点总结
  5. html当当网上书店,完整版:当当网上书店
  6. Java程序员必读书籍推荐
  7. xavier / acts-as-tree-with-dotted-ids
  8. 新《葫芦兄弟》被批毁童年,如果这样拍必然好看一百倍!
  9. 前沿研究丨李德毅院士:基于驾驶脑的智能驾驶车辆硬件平台架构
  10. Ubuntu18 编译和运行PL-SVO(不需要ROS)
  11. 讲讲Python爬虫绕过登录的小技巧
  12. Matlab 在图片上画虚线矩形框
  13. 负值最大与 Alpha-Beta 剪枝的结合
  14. 初识MyBatis Plus
  15. shell中和||的用法
  16. 微信小程序获得用户头像昵称调整(2022年9月28日修改)
  17. 【图像处理】(1)canny图像边缘检测
  18. 计算机二级web程序设计考试环境,2018年计算机二级web程序设计考试大纲
  19. MagicPods(在Windows电脑上可以实现airpods2弹窗,查看单个耳机电量、充电仓电量)
  20. 常用的35个Linux命令合集

热门文章

  1. linux7.0安装oracle乱码,Oracle Linux 7设置中文字符集
  2. 超几何分布_概率小题——分布列专题
  3. 相对路径和绝对路径概念
  4. Java开发必会Git分布式版本控制系统实战篇
  5. 设计模式之美:Strategy(策略) -未经作者同意的转载
  6. 百度测试新搜索结果页面 改进灵感来自谷歌?
  7. Node.js: 深入浅出Nodejs读书笔记
  8. 实例解说Linux命令行uniq
  9. 【今日CV 计算机视觉论文速览 第111期】Fri, 3 May 2019
  10. 【TensorFlow】池化层max_pool中两种paddding操作