2013-09-16 30 views
0

我試圖實現與UrlImageViewHelper(https://github.com/koush/UrlImageViewHelper)非常類似的內容,您可以輕鬆使用簡單的一行代碼,從url加載圖像,並且如果圖像已經下載,它將從緩存加載。主要區別在於我想獲得相同的效果,但不是從網址下載,而是希望通過我自己的客戶端 - 服務器通信從我自己的服務器接收圖像。我的服務器上的每個圖像都可以用字符串唯一標識,並將其用作圖像的標識。我的主要想法是這樣的:使用一個LRU緩存來保存圖像,但不是拿着位圖(這是非常大的),我想保存原始圖像數據二進制,所以我可以使用相同的圖像來構建視具體情況而定,按需提供不同尺寸和質量的位圖。緩存圖像 - 在加載不同的位圖或ImageView的活動後未收集位圖

這是迄今爲止我的執行:

public class ImageHandler { 

    private static class BitmapCache extends LruCache<String, byte[]> 
    { 
     public WigoBitmapCache(int maxSize) { 
      super(maxSize); 
     } 

     @Override 
     protected int sizeOf(String key, byte[] value) { 
      return value.length; 
     } 

    } 

    private static class ImageHandlerThread extends Thread 
    { 

      /* THIS THREAD WILL DECODE THE IMAGE AND SET THE BITMAP TO THE IMAGEVIEW IN THE BACKGROUND */ 
       Activity activity; 
     ImageView imageView; 
     byte[] imageBytes; 

     public ImageHandlerThread(Activity activity, ImageView imageView, byte[] imageBytes) 
     { 
      this.activity=activity; 
      this.imageView=imageView; 
      this.imageBytes=imageBytes; 
     } 

     public void run() { 

      BitmapFactory.Options o = new BitmapFactory.Options(); 
      o.inJustDecodeBounds = true; 
      BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, o); 
      int factor1=o.outHeight/height; 
      int factor2=o.outWidth/width; 
         /* height and width are for now constant */ 
      o = null; 
      o = new BitmapFactory.Options(); 
      if (factor1>factor2) 
       o.inSampleSize=factor1; 
      else 
       o.inSampleSize=factor2; 
      Bitmap bit = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length,o); 
      setBitmap(bit); 
      bit = null; 

     } 


     private void setBitmap(final Bitmap bit) { 
      activity.runOnUiThread(new Runnable() { 

       @Override 
       public void run() { 
        imageView.setImageBitmap(bit); 

       } 
      });  
     } 

    } 

    private static class QueueItem 
    { /*USED TO HOLD INFO ABOUT THE IMAGE REQUEST UNTIL THE IMAGE GETS FROM THE SERVER */ 
     String imageName; 
     Activity activity; 
     ImageView imageView; 

     public QueueItem(String imageName, Activity activity, ImageView imageView) 
     { 
      this.imageName=imageName; 
      this.activity = activity; 
      this.imageView = imageView; 
     } 

    } 

    private BitmapCache cache; // this cache holds the image binaries 
    private ArrayList<QueueItem> queue; // this queue holds the info about the request, until the server sends the image 

    public ImageHandler(int maxSize) 
    { 
     cache=new BitmapCache(maxSize); 
     queue = new ArrayList<QueueItem>(); 

    } 

    public synchronized void setBitmap(Activity activity, ImageView imageView, String imageName) 
    { 
     byte[] imageBytes = cache.get(imageName); 
     if (imageBytes==null) 
     { 
      QueueItem item = new QueueItem(imageName, activity, imageView); 
      queue.add(item);  

      /* HERE IS THE CODE TO RETRIEVE THE IMAGE BINARY FROM MY SERVER, THIS CODE WORKS FINE, SO THERE IS NO REASON TO BOHER YOU WITH IT */ 

     } 
     else 
     { 
      ImageHandlerThread thread = new ImageHandlerThread(activity, imageView, imageBytes); 
      thread.start(); 
     } 

    } 

    public synchronized void insert (String imageName, byte[] imageBytes) 
    { 

     /* THIS METHOD IS THE CALLBACK THAT IS CALLED WHEN THE IMAGE BINARY IS RECEIVED FROM THE SERVER */ 

     cache.put(imageName, imageBytes); 

     for (QueueItem item: queue) 
     { 
      if (item.imageName.equals(imageName)) 
      { 
       ImageHandlerThread thread = new ImageHandlerThread(item.activity, item.imageView, imageBytes); 
       thread.start(); 
       queue.remove(item); 
      } 
     } 
    } 
} 

基本上,這裏的主要方法是setBitmap(),它獲取的活動,需要的位圖的ImageView,圖像名稱的名稱。如果圖像已存在於緩存中,則會啓動一個新線程將字節解碼爲適當大小的位圖,並將位圖設置爲imageView。如果圖像不存在於緩存中,則將請求放入隊列中,直到接收到圖像,從服務器檢索圖像,然後啓動與之前相同的線程。

所有這些工作絕對沒問題,問題是當imageView被設置爲圖像的另一個位圖或者甚至當活動被​​破壞時,位圖仍駐留在內存中,並且不被GC收集。

起初,我認爲這是因爲我保持對活動的引用,並且引用使活動保持活躍狀態​​,但似乎並非如此,對活動的引用非常短暫,圖像從服務器到達,此參考被清除。

我用這個實現快速地運行內存不足,我不知道爲什麼或者該怎麼做來修復它。我創建的位圖不會收集,儘管我不保留對它們的引用。這可能是我解碼圖像的方式的一個神器嗎?或者線程是否保持沒有正確收集的引用?任何人有任何想法?

回答

0

好吧,所以我誤解了這個問題,發生了什麼事情是GC沒有收集完成的線程對象,因爲我在調試模式下運行它,而不是在運行模式下運行。當我在運行模式下運行此應用程序時,我的應用程序的內存使用量根本沒有超過8MB,而當它處於調試模式時,我達到了25MB +的區域。

結論:不要相信調試模式下的GC和內存使用信息,特別是如果您有很多短期線程在運行。