GridView加載大量圖片卡頓問題

1  在異步加載以前的代碼的和普通加載代碼同樣,只須要在GirdView的Adapter的public View getView(int position, View convertView, ViewGroupparent)方法使用異步加載的方式返回ImageView。 java

2  若是能把加載過的圖片給緩存起來,而不用每次都從sd卡上讀取,這樣效率應該會提升很多。因此能夠先建一個緩存類,MemoryCache,爲了能儘量緩存,又儘量的不拋出OOM的異常,可使用SoftReference<Bitmap>,軟引用來緩衝圖片。MemoryCache類的代碼以下: 緩存

[java]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
  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.     //新增緩存  
  16.     public void put(String url,Bitmap bitmap) {  
  17.         cache.put(url, new SoftReference<Bitmap>(bitmap));  
  18.     }  
  19.       
  20.     //清空緩存  
  21.     public void clear() {  
  22.         cache.clear();  
  23.     }  
  24. }  

3  弄完了緩存類,接下來就能夠弄異步加載的類,這個類主要在維護一個Stack,每次向這個類請求一個ImageView時,就要傳入對應ImageView和圖片的路徑。異步類首先會根據圖片的路徑先去緩存中查找是否有緩存對應的BItMap,若是有就把他放到ImageView返回,若是沒有就把這個ImageView和圖片路徑放到Stack中,並喚醒加載圖片的線程。而加載圖片的線程(線程優先權低於UI線程),會無限循環查看Stack大小,若是爲0,就進入等待。若是不爲0,就依次出棧Stack中的元素進行處理。(感受像生產者-消費者模式)。 安全

3.1 接下來,就寫這個異步類的變量和構造函數了: 異步

[java]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
  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的操做的異步,因此就須要線程安全咯。 ide

PhotosStack代碼以下: 函數

[java]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
  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。 this

[java]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
  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.     }  
  12.       

3.2  接下來就能夠實現給Adapter的調用的方法loadImage(Stringpath,ImageView iv ),接收兩參數——圖片路徑和對應的ImageView。這個方法,首先會去緩存中查看是否有緩存對應的圖片,若是有就把它設給ImageView。若是沒有,就先爲ImageView設個默認圖片,而後以同步塊(鎖爲PhotosStack中的stack)的方式加入PhotosStack中的stack中,並喚醒加載圖片的線程。最後還要判斷下加載圖片的線程是否已經被啓動了,若是沒有,就啓動。 url

[java]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
  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.           
  27.           
  28.     }  

3.3 最後能夠實現異步加載圖片的方法了,主要是循環判斷stack中元素的數量,若是爲0 ,說明全部的圖片已經被加載完畢了,能夠進入等待狀態。若是不爲0,說明還有圖片等待加載,就依次出棧這些元素,依次加載圖片,並放到緩存中。代碼以下: spa

[java]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
  1. //異步加載圖片的線程  
  2.     class LoadImageThread extends Thread{  
  3.         Activity activity;  
  4.           
  5.         public LoadImageThread(Activity activity) {  
  6.             this.activity = activity;  
  7.         }  
  8.   
  9.         @Override  
  10.         public void run() {  
  11.             while(true){  
  12.                 try {  
  13.                     if(photosStack.stack.size()>0){  
  14.                         final MyPhoto photo;  
  15.                         //獲得棧頂的MyPhoto  
  16.                         synchronized (photosStack.stack) {  
  17.                              photo= photosStack.stack.pop();  
  18.                         }   
  19.                         cache.put(photo.path,getSmallBitmap(photo.path) );  
  20.                         String ivPathString=viewPath.get(photo.imageView);  
  21.                         if(ivPathString!=null&& ivPathString.equalsIgnoreCase(photo.path)){  
  22.                             Runnable runableRunnable=new Runnable() {  
  23.                                 @Override  
  24.                                 public void run() {  
  25.                                     photo.imageView.setImageBitmap(getSmallBitmap(photo.path));  
  26.                                 }  
  27.                             };  
  28.                             activity.runOnUiThread(runableRunnable);  
  29.                         }  
  30.                     }  
  31.                     if(photosStack.stack.size()==0){  
  32.                         synchronized (photosStack.stack) {  
  33.                             photosStack.stack.wait();  
  34.                         }  
  35.                     }  
  36.                 } catch (InterruptedException e) {  
  37.                     // TODO 自動生成的 catch 塊  
  38.                     e.printStackTrace();  
  39.                 }  
  40.             }  
  41.         }  
  42.     }  
  43.       

其中縮小圖片方法getSmallBitmap(String path),代碼以下: .net

[java]  view plain copy print ? 在CODE上查看代碼片 派生到個人代碼片
  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.           
  9.         while(true){  
  10.             if(options.outWidth<=REQUIRE_SIZE|| options.outHeight<=REQUIRE_SIZE){  
  11.                 break;  
  12.             }else{  
  13.                 options.outWidth=options.outWidth/2;  
  14.                 options.outHeight=options.outHeight/2;  
  15.                 scare++;  
  16.             }   
  17.         }  
  18.         Options newoptions=new Options();  
  19.         newoptions.inSampleSize=scare;  
  20.         return BitmapFactory.decodeFile(path, newoptions);  
相關文章
相關標籤/搜索