ListView效能優化非同步載入圖片

jia635發表於2014-07-06
Android進階:ListView效能優化非同步載入圖片 使滑動效果流暢

ListView 是一種可以顯示一系列專案並能進行滾動顯示的 View,每一行的Item可能包含複雜的結構,可能會從網路上獲取icon等的一些圖示資訊,就現在的網路速度要想保持ListView執行的很好滾動流暢是做不到的

 

所以這裡就需要把這些資訊利用多執行緒實現非同步載入

 

實現這樣功能的類

 

 


  1. public class AsyncImageLoader {  
  2.     private HashMap<String, SoftReference<Drawable>> imageCache;  
  3.    
  4.     public AsyncImageLoader() {  
  5.         imageCache = new HashMap<String, SoftReference<Drawable>>();  
  6.     }  
  7.    
  8.     public Drawable loadDrawable(final String imageUrl, final ImageCallback imageCallback) {  
  9.         if (imageCache.containsKey(imageUrl)) {  
  10.             SoftReference<Drawable> softReference = imageCache.get(imageUrl);  
  11.             Drawable drawable = softReference.get();  
  12.             if (drawable != null) {  
  13.                 return drawable;  
  14.             }  
  15.         }  
  16.         final Handler handler = new Handler() {  
  17.             @Override  
  18.             public void handleMessage(Message message) {  
  19.                 imageCallback.imageLoaded((Drawable) message.obj, imageUrl);  
  20.             }  
  21.         };  
  22.         new Thread() {  
  23.             @Override  
  24.             public void run() {  
  25.                 Drawable drawable = loadImageFromUrl(imageUrl);  
  26.                 imageCache.put(imageUrl, new SoftReference<Drawable>(drawable));  
  27.                 Message message = handler.obtainMessage(0, drawable);  
  28.                 handler.sendMessage(message);  
  29.             }  
  30.         }.start();  
  31.         return null;  
  32.     }  
  33.    
  34.     public static Drawable loadImageFromUrl(String url) {  
  35.         // ...   
  36.     }  
  37.    
  38.     public interface ImageCallback {  
  39.         public void imageLoaded(Drawable imageDrawable, String imageUrl);  
  40.     }  
  41. }  

 

 

注意這裡使用了 SoftReference來快取圖片,允許 GC在需要的時候可以對快取中的圖片進行清理。它這樣工作:

·         呼叫 loadDrawable(ImageUrl, imageCallback),傳入一個匿名實現的 ImageCallback介面

·         如果圖片在快取中不存在的話,圖片將從單一的執行緒中下載並在下載結束時通過 ImageCallback回撥

·         如果圖片確實存在於快取中,就會馬上返回,不會回撥 ImageCallback

 

        然後我們還可以根據09google I/0開發者大會提到的方式來繼續優化Adapter 使用ViewHolder來減少一些比較費時的操作,譬如inflate XML 和 findViewById()等操作

      


  1. public class ImageAndTextListAdapter extends ArrayAdapter<ImageAndText> {  
  2.    
  3.     private ListView listView;  
  4.     private AsyncImageLoader asyncImageLoader;  
  5.    
  6.     public ImageAndTextListAdapter(Activity activity, List<ImageAndText> imageAndTexts, ListView listView) {  
  7.         super(activity, 0, imageAndTexts);  
  8.         this.listView = listView;  
  9.         asyncImageLoader = new AsyncImageLoader();  
  10.     }  
  11.    
  12.     @Override  
  13.     public View getView(int position, View convertView, ViewGroup parent) {  
  14.         Activity activity = (Activity) getContext();  
  15.    
  16.         // Inflate the views from XML   
  17.         View rowView = convertView;  
  18.         ViewCache viewCache;  
  19.         if (rowView == null) {  
  20.             LayoutInflater inflater = activity.getLayoutInflater();  
  21.             rowView = inflater.inflate(R.layout.image_and_text_row, null);  
  22.             viewCache = new ViewCache(rowView);  
  23.             rowView.setTag(viewCache);  
  24.         } else {  
  25.             viewCache = (ViewCache) rowView.getTag();  
  26.         }  
  27.         ImageAndText imageAndText = getItem(position);  
  28.    
  29.         // Load the image and set it on the ImageView   
  30.         String imageUrl = imageAndText.getImageUrl();  
  31.         ImageView imageView = viewCache.getImageView();  
  32.         imageView.setTag(imageUrl);  
  33.         Drawable cachedImage = asyncImageLoader.loadDrawable(imageUrl, new ImageCallback() {  
  34.             public void imageLoaded(Drawable imageDrawable, String imageUrl) {  
  35.                 ImageView imageViewByTag = (ImageView) listView.findViewWithTag(imageUrl);  
  36.                 if (imageViewByTag != null) {  
  37.                     imageViewByTag.setImageDrawable(imageDrawable);  
  38.                 }  
  39.             }  
  40.         });  
  41.         imageView.setImageDrawable(cachedImage);  
  42.    
  43.         // Set the text on the TextView   
  44.         TextView textView = viewCache.getTextView();  
  45.         textView.setText(imageAndText.getText());  
  46.    
  47.         return rowView;  
  48.     }  
  49. }   

 

 

 

      這裡我們沒有載入完iamge之後直接設定到相應的ImageView上 ,而是通過Tag查詢,這裡我們重用的View 這裡有個listView的引用來通過Tag查詢 可見 CallBack的實現

 


  1. ImageView imageViewByTag = (ImageView) listView.findViewWithTag(imageUrl);  
  2.                if (imageViewByTag != null) {  
  3.                    imageViewByTag.setImageDrawable(imageDrawable);  
  4.                }  

 

 

      這裡通過ViewCatch來減少了 findViewById的使用

 


  1. public class ViewCache {  
  2.    
  3.     private View baseView;  
  4.     private TextView textView;  
  5.     private ImageView imageView;  
  6.    
  7.     public ViewCache(View baseView) {  
  8.         this.baseView = baseView;  
  9.     }  
  10.    
  11.     public TextView getTextView() {  
  12.         if (textView == null) {  
  13.             textView = (TextView) baseView.findViewById(R.id.text);  
  14.         }  
  15.         return titleView;  
  16.     }  
  17.    
  18.     public ImageView getImageView() {  
  19.         if (imageView == null) {  
  20.             imageView = (ImageView) baseView.findViewById(R.id.image);  
  21.         }  
  22.         return imageView;  
  23.     }  
  24. }   

 


    總結 :這裡主要做了三點優化

 

  • 在單一執行緒里載入圖片
  •   重用列表中行
  • 快取行中的 View

相關文章