文档章节

GridView加载大量图片卡顿问题

ljrapple
 ljrapple
发布于 2014/08/12 09:35
字数 1270
阅读 2080
收藏 1

1  在异步加载之前的代码的和普通加载代码一样,只需要在GirdView的Adapter的public View getView(int position, View convertView, ViewGroupparent)方法使用异步加载的方式返回ImageView。

2  如果能把加载过的图片给缓存起来,而不用每次都从sd卡上读取,这样效率应该会提高不少。所以可以先建一个缓存类,MemoryCache,为了能尽可能缓存,又尽可能的不抛出OOM的异常,可以使用SoftReference<Bitmap>,软引用来缓冲图片。MemoryCache类的代码如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * 缓存类(图片的路径和图片) 
  3.  */  
  4. public class MemoryCache {  
  5.     private HashMap<String, SoftReference<Bitmap>> cache=new HashMap<String, SoftReference<Bitmap>>();  
  6.     //获取缓存图片  
  7.     public Bitmap get(String path) {  
  8.         if(cache.get(path)==null){  
  9.             return null;  
  10.         }else{  
  11.             return cache.get(path).get();  
  12.         }  
  13.     }  
  14.       
  15.     //新增缓存  
  16.     public void put(String url,Bitmap bitmap) {  
  17.         cache.put(url, new SoftReference<Bitmap>(bitmap));  
  18.     }  
  19.       
  20.     //清空缓存  
  21.     public void clear() {  
  22.         cache.clear();  
  23.     }  
  24. }  

3  弄完了缓存类,接下来就可以弄异步加载的类,这个类主要在维护一个Stack,每次向这个类请求一个ImageView时,就要传入对应ImageView和图片的路径。异步类首先会根据图片的路径先去缓存中查找是否有缓存对应的BItMap,如果有就把他放到ImageView返回,如果没有就把这个ImageView和图片路径放到Stack中,并唤醒加载图片的线程。而加载图片的线程(线程优先权低于UI线程),会无限循环查看Stack大小,如果为0,就进入等待。如果不为0,就依次出栈Stack中的元素进行处理。(感觉像生产者-消费者模式)。

3.1 接下来,就写这个异步类的变量和构造函数了:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. private MemoryCache cache;  
  2.     private PhotosStack photosStack;  
  3.     private LoadImageThread loadImageThread;  
  4.     //用来保存每个月ImageView和对应应加载的图片  
  5.     private Map<ImageView, String> viewPath=Collections.synchronizedMap(new HashMap<ImageView, String>());  
  6.     public LoadImage(Activity activity) {  
  7.         super();  
  8.         cache=new MemoryCache();  
  9.         photosStack=new PhotosStack();  
  10.         loadImageThread=new LoadImageThread(activity);  
  11.         //设置异步加载线程优先权低于UI线程  
  12.         loadImageThread.setPriority(Thread.NORM_PRIORITY);  
  13.     }  

其中MemoryCache是缓存类,PhotosStack类中维护一个Stack,LoadImageThread类是负责异步加载图片的。而viewPath这个变量是为了要保证GridView对应Position放对应的ImageView,如果没有这个变量,GridView中排列就会无序,在处理GridView的点击事件时候,就不好处理。而对viewPath的操作的异步,所以就需要线程安全咯。

PhotosStack代码如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. //维护需要被填充的Image和对应图片路径的栈  
  2. class PhotosStack{  
  3.     Stack<MyPhoto> stack=new Stack<LoadImage.MyPhoto>();  
  4.     //移除指定ImageView  
  5.     public void remove(ImageView iView) {  
  6.         for(int i=0;i<stack.size();i++){  
  7.             if(stack.get(i).imageView==iView){  
  8.                 stack.remove(i);  
  9.                 break;  
  10.             }  
  11.         }  
  12.     }  
  13. }  

其中MyPhoto是一个照片的实体类,有两个属性,图片的路径和对应的ImageView。

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.      * 照片的实体类 
  3.      */  
  4.     class MyPhoto{  
  5.          public String path;  
  6.             public ImageView imageView;  
  7.             public MyPhoto(String path, ImageView imageView){  
  8.                 this.path=path;   
  9.                 this.imageView=imageView;  
  10.             }  
  11.     }  
  12.       

3.2  接下来就可以实现给Adapter的调用的方法loadImage(Stringpath,ImageView iv ),接收两参数——图片路径和对应的ImageView。这个方法,首先会去缓存中查看是否有缓存对应的图片,如果有就把它设给ImageView。如果没有,就先为ImageView设个默认图片,然后以同步块(锁为PhotosStack中的stack)的方式加入PhotosStack中的stack中,并唤醒加载图片的线程。最后还要判断下加载图片的线程是否已经被启动了,如果没有,就启动。

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.      * 填充ImageView 
  3.      * @param activity  
  4.      * @param path 图片的路径 
  5.      * @param iv 被填充的ImageView 
  6.      */  
  7.     public void loadImage(String path,ImageView iv ) {  
  8.         viewPath.put(iv, path);  
  9.         Bitmap momeryBitmap=cache.get(path);  
  10.         if(momeryBitmap!=null){//有缓存这张图片  
  11.             iv.setImageBitmap(momeryBitmap);  
  12.         }else{//没有缓存  
  13.             iv.setImageResource(R.drawable.ic_launcher);  
  14.             //有可以这个ImageVIew还没出栈,所以需要先出栈  
  15.             photosStack.remove(iv);  
  16.             //由于photosStack中的stack,出栈入栈操作时不会在异步的,所以需要在同步块中完成出入栈,并以photosStack.stack作为锁  
  17.             synchronized (photosStack.stack) {  
  18.                 photosStack.stack.push(new MyPhoto(path, iv));  
  19.                 //唤醒持有锁的线程  
  20.                 photosStack.stack.notifyAll();  
  21.             }  
  22.         }  
  23.         if(loadImageThread.getState()==Thread.State.NEW){  
  24.             loadImageThread.start();  
  25.         }  
  26.           
  27.           
  28.     }  

3.3 最后可以实现异步加载图片的方法了,主要是循环判断stack中元素的数量,如果为0 ,说明所有的图片已经被加载完毕了,可以进入等待状态。如果不为0,说明还有图片等待加载,就依次出栈这些元素,依次加载图片,并放到缓存中。代码如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. //异步加载图片的线程  
  2.     class LoadImageThread extends Thread{  
  3.         Activity activity;  
  4.           
  5.         public LoadImageThread(Activity activity) {  
  6.             this.activity = activity;  
  7.         }  
  8.   
  9.         @Override  
  10.         public void run() {  
  11.             while(true){  
  12.                 try {  
  13.                     if(photosStack.stack.size()>0){  
  14.                         final MyPhoto photo;  
  15.                         //得到栈顶的MyPhoto  
  16.                         synchronized (photosStack.stack) {  
  17.                              photo= photosStack.stack.pop();  
  18.                         }   
  19.                         cache.put(photo.path,getSmallBitmap(photo.path) );  
  20.                         String ivPathString=viewPath.get(photo.imageView);  
  21.                         if(ivPathString!=null&& ivPathString.equalsIgnoreCase(photo.path)){  
  22.                             Runnable runableRunnable=new Runnable() {  
  23.                                 @Override  
  24.                                 public void run() {  
  25.                                     photo.imageView.setImageBitmap(getSmallBitmap(photo.path));  
  26.                                 }  
  27.                             };  
  28.                             activity.runOnUiThread(runableRunnable);  
  29.                         }  
  30.                     }  
  31.                     if(photosStack.stack.size()==0){  
  32.                         synchronized (photosStack.stack) {  
  33.                             photosStack.stack.wait();  
  34.                         }  
  35.                     }  
  36.                 } catch (InterruptedException e) {  
  37.                     // TODO 自动生成的 catch 块  
  38.                     e.printStackTrace();  
  39.                 }  
  40.             }  
  41.         }  
  42.     }  
  43.       

其中缩小图片方法getSmallBitmap(String path),代码如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. //缩小图片  
  2.     public Bitmap getSmallBitmap(String path) {  
  3.         Options options=new Options();  
  4.         options.inJustDecodeBounds=true;  
  5.         BitmapFactory.decodeFile(path,options);  
  6.         int REQUIRE_SIZE=80;  
  7.         int scare=1;  
  8.           
  9.         while(true){  
  10.             if(options.outWidth<=REQUIRE_SIZE|| options.outHeight<=REQUIRE_SIZE){  
  11.                 break;  
  12.             }else{  
  13.                 options.outWidth=options.outWidth/2;  
  14.                 options.outHeight=options.outHeight/2;  
  15.                 scare++;  
  16.             }   
  17.         }  
  18.         Options newoptions=new Options();  
  19.         newoptions.inSampleSize=scare;  
  20.         return BitmapFactory.decodeFile(path, newoptions);  

本文转载自:

共有 人打赏支持
ljrapple
粉丝 6
博文 66
码字总数 17974
作品 0
广州
Android工程师
私信 提问
高效使用Bitmaps(二) 后台加载Bitmap

为什么要在后台加载Bitmap? 有没有过这种体验:你在Android手机上打开了一个带有含图片的ListView的页面,用手猛地一划,就见那ListView嘎嘎地卡,仿佛每一个新的Item都是顶着阻力蹦出来的一...

扔物线
2013/12/12
0
7
解决 ListView 滚动卡的问题

写了个类似下面的GridView,滚动的时候有卡或者跳格的现象,尤其当记录比较多的时候。 GridView和ListView机制原理是类似的,都是基于ListAdapter来处理View的控制的。在排查问题的时候也测试...

无鸯
2011/09/07
1K
0
Universal-image-loader图片加载开源框架基本使用

universal-imageloader加载图片的特点: 基本特点如下, 多线程下载图片,图片可以来源于网络,文件系统,项目文件夹assets中以及drawable中等 支持随意的配置ImageLoader,例如线程池,图片...

肖朝胜
2016/03/21
370
0
Android Adapter 库--CommonAdapter

CommonAdapter 一个适用于ListView/GridView/RecyclerView的Adapter库,简化大量重复代码,支持多种布局,可自定义图片加载的实现。 功能特点: 简化大量重复代码 支持多布局 自定义图片加载 常...

匿名
2016/03/10
315
0
Android手动回收Bitma,引发Canvas: trying to use a recycle

Android手动回收bitmap,引发Canvas: trying to use a recycled bitmap处理 在做Android的开发的时候,在ListView 或是 GridView中需要加载大量的图片,为了避免加载过多的图片引起OutOfMemo...

jpch
2013/12/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

0011-如何在Hive & Impala中使用UDF

1.文档编写目的 本文档讲述如何开发Hive自定义函数(UDF),以及如何在Impala中使用Hive的自定义函数,通过本文档,您将学习到以下知识: 1.如何使用Java开发Hive的自定义函数 2.如何在Hive中...

Hadoop实操
24分钟前
2
0
toString();

package com.atguigu.java1; import java.util.Date; /** * toString()的使用: * * 1.java.lang.Object类中toString()定义如下: * public String toString() { return getClass().getName......

architect刘源源
34分钟前
1
3
不可不说的Java“锁”事

前言 Java提供了种类丰富的锁,每种锁因其特性的不同,在适当的场景下能够展现出非常高的效率。本文旨在对锁相关源码(本文中的源码来自JDK 8)、使用场景进行举例,为读者介绍主流锁的知识点...

Java干货分享
37分钟前
1
0
Java GoEasy 实现服务端推送和Web端推送

项目中需要消息推送,又想降低开发成本。Java服务器端推送,Web端接收推送信息。 具体需求: 需求一:系统框架实现全局异常捕获并录入日志表,实现实时推送消息到客户端页面展示。 需求二:系...

Gibbons
39分钟前
4
0
redis-集群

多个redis节点网络互联,数据共享 所有的节点都是一主一从(可以是多个从),其中从不提供服务,仅作为备用 不支持同时处理多个键(如mset/mget),因为redis需要把键均匀分布在各个节点上,...

chencheng-linux
44分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部