GridView加载大量图片卡顿问题

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

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

[java] 
view plain
copy
print
?

  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. public void put(String url,Bitmap bitmap) {
  16. cache.put(url, new SoftReference<Bitmap>(bitmap));
  17. }
  18. //清空缓存
  19. public void clear() {
  20. cache.clear();
  21. }
  22. }

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

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

[java] 
view plain
copy
print
?

  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
?

  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
?

  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. }

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

[java] 
view plain
copy
print
?

  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. }

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

[java] 
view plain
copy
print
?

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

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

[java] 
view plain
copy
print
?

  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. while(true){
  9. if(options.outWidth<=REQUIRE_SIZE|| options.outHeight<=REQUIRE_SIZE){
  10. break;
  11. }else{
  12. options.outWidth=options.outWidth/2;
  13. options.outHeight=options.outHeight/2;
  14. scare++;
  15. }
  16. }
  17. Options newoptions=new Options();
  18. newoptions.inSampleSize=scare;
  19. return BitmapFactory.decodeFile(path, newoptions);

GridView加载大量图片卡顿问题

时间: 2024-11-07 10:08:42

GridView加载大量图片卡顿问题的相关文章

多个fragment之间切换加载数据卡顿

Activity里面可能会以viewpager(或其他容器)与多个Fragment来组合使用,而如果每个fragment都需要去加载数据,或从本地加载,或从网络加载,那么在这个activity刚创建的时候就变成需要初始化大量资源.有可能会导致卡顿. 就需要用到Fragment里的setUserVisibleHint这个方法里. 解决办法 package com.xgs.panku.ui; import android.app.Fragment; /** * 类描述:当切换到这个fragment的

用collection view动态加载数据卡顿的可能原因

在用collectionview动态加载数据时有个好处,就是cell得到了复用,如果整屏显示一个cell,最多只有两个cell,坏处就是需要不断的刷新cell,这时候容易卡顿. 每次滑动collectionview会调用代理方法,获取新的cell,这时你要将数据传给cell并进行显示. 现在的情况是快速滑动的时候和慢速滑动的时候不会卡顿,快速滑动的时候不会请求数据,所以卡顿原因可能是在请求数据的时候,慢速滑动的时候因为间隔时间长,数据应该都加载好了,所以卡顿原因可能是间隔过短,cell已经被创建

Android fragment 切换加载数据卡顿问题

接着上一篇项目的进度,上一篇讲了如何利用fragment来实现下拉菜单,公用菜单,以实现切换主界面数据的功能,这时候遇到的问题是:使用了fragment的切换界面方法,但加载的数据太多,用户从一个界面切换到这个界面的时候,至少有一两秒的卡顿,这是无法忍受的,代码如下: private void initOpenMenuItem(View popupWindow_view) { <span style="white-space:pre"> </span>Drawa

Android GridView加载大量图片时出现OOM情况

最近写的一个应用涉及到使用GridView显示图片,当使用BaseAdapter传统的的实现时,在真机上快速滚动时会出现OOM情况. 一个临时性的解决方案就是改动图片尺寸,减小内存.这种方法简单却不安全.如果图片够多,也存在依然OOM的情况. 一个有效的方法就是使用异步加载. 获取应用程序最大可用内存: int maxMemory = (int) Runtime.getRuntime().maxMemory(); 还可以使用convertView 回收视图,结合使用ViewHolder模式,提高

使用SDWebImage加载大量图片后造成内存泄露的解决办法 转载

使用SDWebImage加载大量图片后造成内存泄露的解决办法 时间:2015-07-21 14:26:47      阅读:5885      评论:0      收藏:0      [点我收藏+] SDWebImage的知名度就不用说了,github上近10k的star,国内外太多的App使用其进行图片加载. 但是最近在使用过程中发现,在UITableView中不断加载更多的内容,使用SDWebImage会造成内存占用越来越大,导致memory warning最终terminate,稍微找了下

UniversalImageLoader(异步加载大量图片)

UniversalImageLoader是用于加载图片的一个开源项目,UniversalImageLoader是实现异步加载大量图片的源码和例子,包括缓存.硬盘缓存.容错机制等技术.在其项目介绍中是这么写的,支持多线程图片加载:提供丰富的细节配置,比如线程池大小,HTPP请求项,内存和磁盘缓存,图片显示时的参数配置等等:提供双缓存:支持加载过程的监听:提供图片的个性化显示配置接口:Widget支持: 要使用ImageLoader就要到这里下载jar包:https://github.com/nos

android加载大量图片内存溢出的三种方法

android加载大量图片内存溢出的三种解决办法 方法一:  在从网络或本地加载图片的时候,只加载缩略图. /** * 按照路径加载图片 * @param path 图片资源的存放路径 * @param scalSize 缩小的倍数 * @return */ public static Bitmap loadResBitmap(String path, int scalSize) { BitmapFactory.Options options = new BitmapFactory.Option

【转】GridView 加载空行并点击编辑每一个单元格

1 代码 2 3 <script runat="server"> 4 protectedvoid Button1_Click(object sender, System.EventArgs e) 5 { 6 GridView1.DataSource = GetData(); 7 GridView1.DataBind(); 8 } 9 10 protectedvoid Button2_Click(object sender, System.EventArgs e) 11 {

弄明白android网络库之Volley listView加载大量图片

一.加载一张图片 Volley是通过ImageRequest来获取网络上的图片的,指定一个URL,返回一个已经编码号的bitmap.当然它也提供了其他便利特性,比如调整图片大小.使用它它主要的好处是 Volley的计划线程确保了如图片编码.调整大小等昂贵的操作自动地在一个工作线程完成,不会给主线程带来太多的麻烦和干扰. a cannedrequest for getting an image at a given URL and calling back with a decodedbitmap