首先声明,参考博客地址:http://www.iteye.com/topic/685986java
对于ListView,相信不少人都很熟悉,由于确实太常见了,因此,作的用户体验更好,就成了咱们的追求。。。android
常见的ListView中不多全是文字的,通常都是图文共存的,而图片的来源是服务器端(不多有写在客户端的吧。。。考虑客户端的大小和更新的问题),因此,网络问题就成了图片是否能顺利加载成功的决定性因素了。你们都知道每次启动一个Android应用,都会启动一个UI主线程,主要是响应用户的交互,若是咱们把不肯定的获取网络图片的操做放在UI主线程,结果也就不肯定了。。。固然,若是你网络足够好的话,应该问题不大,可是,网络谁能保证呢?因此就出现了“异步加载”的方法!缓存
我先叙述一下异步加载的原理,说的通俗一点就是UI主线程继续作与用户交互的响应监听和操做,而加载图片的任务交到其余线程中去作,当图片加载完成以后,再跟据某种机制(好比回调)绘制到要显示的控件中。服务器
首先,贴出AsyncBitmapLoader.java,这个类是关键,主要作的就是当加载图片的时候,去缓冲区查找,若是有的话,则马上返回Bitmap对象,省掉再去网络服务器下载的时间和流量。网络
<span style="font-size:18px;">package onerain.ald.async; 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); } } </span>
PS:我这里用到了两个缓冲,一是内存缓存,一个是本地缓存(即SD卡缓存),其中用到了SoftReference,这个类的主要做用是生成一个“软引用”,你能够认为是一种随时会因为JVM垃圾回收机制回收掉的Map对象(而平时咱们所用到的引用不释放的话不会被JVM回收),之因此用到软引用,就是考虑到android对图片的缓存是有大小限制的,当超过这个大小时,就必定要释放,若是你用引用,保持不释放的话,那么FC(Force close)就离你不远了。。。我这里还用到了一个本地缓存的机制,是和参考博客不太同样的地方,只是提供一种思路,方法尚未完善(主要由于和服务器还没约定好关于图片的命名规则),主要做用是在用户浏览过大量图片以后(超过内存缓存容量以后),保留在本地,一是为了提升读取速度,二是能够减小流量消耗!异步
这个类设计好以后,在自定义的Adapter当中比以前会有些不一样,先上代码再解释jvm
<span style="font-size:18px;">package onerain.ald.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; } } </span>
在Adapter中,主要不一样表如今async
public View getView(int position, View convertView, ViewGroup parent)方法中(我这里用到了一些优化方面的处理,会在其余时间再与你们分享,今天先不解释),和异步加载最相关的是这一段ide
<span style="font-size:18px;">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); }</span>
asyncLoader是咱们定义的异步加载类的对象,经过这个类的优化
public Bitmap loadBitmap(final ImageView imageView, final String imageURL, final ImageCallBack imageCallBack)
加载图片,传递参数也与参考博客有些不一样,我以为这样更好理解一下,就是要显示图片的URL连接,和图片要显示对应的控件,固然最重要的还有这个接口实现的回调对象,是在线程中下载完图片以后,用以加载图片的回调对象。而这个回调对象在传递的时候已经实现了接口方法,即将下载好的图片绘制在对应的控件之中
<span style="font-size:18px;">imageView.setImageBitmap(bitmap);</span>