首先聲明,參考博客地址: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>