android ListView异步加载图片(双缓存)

来源:互联网 发布:网络电视外置无线网卡 编辑:程序博客网 时间:2024/05/16 14:06

首先声明,参考博客地址:http://blog.csdn.net/onerain88/article/details/7008409


对于ListView,相信很多人都很熟悉,因为确实太常见了,所以,做的用户体验更好,就成了我们的追求。。。


常见的ListView中很少全是文字的,一般都是图文共存的,而图片的来源是服务器端(很少有写在客户端的吧。。。考虑客户端的大小和更新的问题),所以,网络问题就成了图片是否能顺利加载成功的决定性因素了。大家都知道每次启动一个Android应用,都会启动一个UI主线程,主要是响应用户的交互,如果我们把不确定的获取网络图片的操作放在UI主线程,结果也就不确定了。。。当然,如果你网络足够好的话,应该问题不大,但是,网络谁能保证呢?所以就出现了“异步加载”的方法!


我先叙述一下异步加载的原理,说的通俗一点就是UI主线程继续做与用户交互的响应监听和操作,而加载图片的任务交到其他线程中去做,当图片加载完成之后,再跟据某种机制(比如回调)绘制到要显示的控件中。


首先,贴出AsyncBitmapLoader.java,这个类是关键,主要做的就是当加载图片的时候,去缓冲区查找,如果有的话,则立刻返回Bitmap对象,省掉再去网络服务器下载的时间和流量。


import java.io.File;  import java.io.FileNotFoundException;  import java.io.FileOutputStream;  import java.io.IOException;  import java.io.InputStream;  import java.lang.ref.SoftReference;  import java.util.HashMap;    import onerain.ald.common.HttpUtils;  import android.graphics.Bitmap;  import android.graphics.BitmapFactory;  import android.os.Handler;  import android.os.Message;  import android.widget.ImageView;    /**  * @author oneRain  **/  public class AsyncBitmapLoader  {      /**      * 内存图片软引用缓冲      */      private HashMap<String, SoftReference<Bitmap>> imageCache = null;            public AsyncBitmapLoader()      {          imageCache = new HashMap<String, SoftReference<Bitmap>>();      }            public Bitmap loadBitmap(final ImageView imageView, final String imageURL, final ImageCallBack imageCallBack)      {          //在内存缓存中,则返回Bitmap对象          if(imageCache.containsKey(imageURL))          {              SoftReference<Bitmap> reference = imageCache.get(imageURL);              Bitmap bitmap = reference.get();              if(bitmap != null)              {                  return bitmap;              }          }          else          {              /**              * 加上一个对本地缓存的查找              */              String bitmapName = imageURL.substring(imageURL.lastIndexOf("/") + 1);              File cacheDir = new File("/mnt/sdcard/test/");              File[] cacheFiles = cacheDir.listFiles();              int i = 0;              for(; i<cacheFiles.length; i++)              {                  if(bitmapName.equals(cacheFiles[i].getName()))                  {                      break;                  }              }                            if(i < cacheFiles.length)              {                  return BitmapFactory.decodeFile("/mnt/sdcard/test/" + bitmapName);              }          }                    final Handler handler = new Handler()          {              /* (non-Javadoc)              * @see android.os.Handler#handleMessage(android.os.Message)              */              @Override              public void handleMessage(Message msg)              {                  // TODO Auto-generated method stub                  imageCallBack.imageLoad(imageView, (Bitmap)msg.obj);              }          };                    //如果不在内存缓存中,也不在本地(被jvm回收掉),则开启线程下载图片          new Thread()          {              /* (non-Javadoc)              * @see java.lang.Thread#run()              */              @Override              public void run()              {                  // TODO Auto-generated method stub                  InputStream bitmapIs = HttpUtils.getStreamFromURL(imageURL);                                    Bitmap bitmap = BitmapFactory.decodeStream(bitmapIs);                  imageCache.put(imageURL, new SoftReference<Bitmap>(bitmap));                  Message msg = handler.obtainMessage(0, bitmap);                  handler.sendMessage(msg);                                    File dir = new File("/mnt/sdcard/test/");                  if(!dir.exists())                  {                      dir.mkdirs();                  }                                    File bitmapFile = new File("/mnt/sdcard/test/" +                           imageURL.substring(imageURL.lastIndexOf("/") + 1));                  if(!bitmapFile.exists())                  {                      try                      {                          bitmapFile.createNewFile();                      }                      catch (IOException e)                      {                          // TODO Auto-generated catch block                          e.printStackTrace();                      }                  }                  FileOutputStream fos;                  try                  {                      fos = new FileOutputStream(bitmapFile);                      bitmap.compress(Bitmap.CompressFormat.JPEG,                               100, fos);                      fos.close();                  }                  catch (FileNotFoundException e)                  {                      // TODO Auto-generated catch block                      e.printStackTrace();                  }                  catch (IOException e)                  {                      // TODO Auto-generated catch block                      e.printStackTrace();                  }              }          }.start();                    return null;      }            /**      * 回调接口      * @author onerain      *      */      public interface ImageCallBack      {          public void imageLoad(ImageView imageView, Bitmap bitmap);      }  }

PS:我这里用到了两个缓冲,一是内存缓存,一个是本地缓存(即SD卡缓存),其中用到了SoftReference,这个类的主要作用是生成一个“软引用”,你可以认为是一种随时会由于JVM垃圾回收机制回收掉的Map对象(而平时我们所用到的引用不释放的话不会被JVM回收),之所以用到软引用,就是考虑到android对图片的缓存是有大小限制的,当超过这个大小时,就一定要释放,如果你用引用,保持不释放的话,那么FC(Force close)就离你不远了。。。我这里还用到了一个本地缓存的机制,是和参考博客不太一样的地方,只是提供一种思路,方法还没有完善(主要因为和服务器还没约定好关于图片的命名规则),主要作用是在用户浏览过大量图片之后(超过内存缓存容量之后),保留在本地,一是为了提高读取速度,二是可以减少流量消耗!


这个类设计好之后,在自定义的Adapter当中比之前会有些不同,先上代码再解释


import java.util.List;    import onerain.ald.R;  import onerain.ald.async.AsyncBitmapLoader;  import onerain.ald.async.AsyncBitmapLoader.ImageCallBack;  import onerain.ald.entity.BaseBookEntity;  import onerain.ald.holder.ViewHolder;  import android.content.Context;  import android.graphics.Bitmap;  import android.view.LayoutInflater;  import android.view.View;  import android.view.ViewGroup;  import android.widget.BaseAdapter;  import android.widget.ImageView;  import android.widget.TextView;    /**  * @author oneRain  **/  public class ListAdapter extends BaseAdapter  {      private Context context = null;      private List<BaseBookEntity> bookList = null;            private AsyncBitmapLoader asyncLoader = null;            public ListAdapter(Context context, List<BaseBookEntity> bookList)      {          this.context = context;          this.bookList = bookList;          this.asyncLoader = new AsyncBitmapLoader();      }            @Override      public int getCount()      {          // TODO Auto-generated method stub          return bookList.size();      }        @Override      public Object getItem(int position)      {          // TODO Auto-generated method stub          return bookList.get(position);      }        @Override      public long getItemId(int position)      {          // TODO Auto-generated method stub          return position;      }        @Override      public View getView(int position, View convertView, ViewGroup parent)      {          // TODO Auto-generated method stub          ViewHolder holder = null;                    if(convertView == null)          {              LayoutInflater inflater = LayoutInflater.from(context);              convertView = inflater.inflate(R.layout.item, null);                            holder = new ViewHolder((ImageView)convertView.findViewById(R.id.imageView),                      (TextView)convertView.findViewById(R.id.textView));              convertView.setTag(holder);          }          else          {              holder = (ViewHolder) convertView.getTag();          }                    ImageView imageView = holder.getImageView();                    <span style="color:#FF0000;">imageView.setImageBitmap(null);</span>                    //根据图片URL去查找内存缓存有没有对应的Bitmap对象,并传递回调方法,如果没有,则等下载完毕回调          Bitmap bitmap = asyncLoader.loadBitmap(imageView,                   bookList.get(position).getBook_pic(),                  new ImageCallBack()                  {                      @Override                      public void imageLoad(ImageView imageView, Bitmap bitmap)                      {                          // TODO Auto-generated method stub                          imageView.setImageBitmap(bitmap);                      }                  });                    if(bitmap == null)          {              imageView.setImageResource(R.drawable.ic_launcher);          }          else          {              imageView.setImageBitmap(bitmap);          }                    holder.getTextView().setText(bookList.get(position).getTitle());                    return convertView;      }  } 

在Adapter中,主要不同表现在

public View getView(int position, View convertView, ViewGroup parent)方法中(我这里用到了一些优化方面的处理,会在其他时间再与大家分享,今天先不解释),和异步加载最相关的是这一段


ImageView imageView = holder.getImageView();          //根据图片URL去查找内存缓存有没有对应的Bitmap对象,并传递回调方法,如果没有,则等下载完毕回调      Bitmap bitmap = asyncLoader.loadBitmap(imageView,               bookList.get(position).getBook_pic(),              new ImageCallBack()              {                  @Override                  public void imageLoad(ImageView imageView, Bitmap bitmap)                  {                      // TODO Auto-generated method stub                      imageView.setImageBitmap(bitmap);                  }              });                if(bitmap == null)      {          imageView.setImageResource(R.drawable.ic_launcher);      }      else      {          imageView.setImageBitmap(bitmap);      }

asyncLoader是我们定义的异步加载类的对象,通过这个类的

public Bitmap loadBitmap(final ImageView imageView, final String imageURL, final ImageCallBack imageCallBack)

加载图片,传递参数也与参考博客有些不同,我觉得这样更好理解一下,就是要显示图片的URL链接,和图片要显示对应的控件,当然最重要的还有这个接口实现的回调对象,是在线程中下载完图片之后,用以加载图片的回调对象。而这个回调对象在传递的时候已经实现了接口方法,即将下载好的图片绘制在对应的控件之中


0 0