2012-11-13 65 views
1

我正在開發一款適用於Android的Mono應用程序。無法擊敗'內存不足'異常

我一直在努力解決過去幾天內存不足的問題,並開始失去希望!

我有一個ListView顯示200到600個項目的任何東西。這些項目由位圖縮略圖和一些文本組成。

我異步使用的AsyncTask位圖解碼,這裏是代碼:

public class BitmapWorkerTask : AsyncTask 
    { 
     private WeakReference imageViewReference; 
     public string thisURL = ""; 
     private int sampleSize = 0; 
     private int reqHeight = 0; 
     private int reqWidht = 0; 

     public BitmapWorkerTask(ImageView imageView, int pSampleSize, int pReqWidth, int pReqHeight) 
     { 
      //_____________________________________________________________________ 
      // Use a WeakReference to ensure the ImageView can be garbage collected 
      imageViewReference = new WeakReference(imageView); 

      reqHeight = pReqHeight; 
      reqWidht = pReqWidth; 
      sampleSize = pSampleSize; 
     } 

     protected override Java.Lang.Object DoInBackground(params Java.Lang.Object[] @params) 
     { 
      string strUrl = @params[0].ToString(); 

      try 
      { 
       return DecodeSampleBitmapFromStream(strUrl, reqWidht, reqHeight); 
      } 
      catch (Exception ex) 
      { 
       return null; 
      } 
     } 

     protected override void OnPostExecute(Java.Lang.Object result) 
     { 
      base.OnPostExecute(result); 

      if (IsCancelled) 
      { 
       result = null; 
       Log.Debug("TT", "OnPostExecute - Task Cancelled"); 
      } 
      else 
      { 
       Bitmap bmpResult = result as Bitmap; 

       if (imageViewReference != null && bmpResult != null) 
       { 
        ImageView view = imageViewReference.Target as ImageView; 

        if (view != null) 
        { 
         view.SetImageBitmap(bmpResult); 
        } 
       } 
      } 
     } 

     public static int CalculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) 
     { 
      //_____________________________ 
      // Raw height and width of image 
      int height = options.OutHeight; 
      int width = options.OutWidth; 
      int inSampleSize = 1; 

      if (height > reqHeight || width > reqWidth) 
      { 
       if (width > height) 
       { 
        inSampleSize = (int)Math.Round((float)height/(float)reqHeight); 
       } 
       else 
       { 
        inSampleSize = (int)Math.Round((float)width/(float)reqWidth); 
       } 
      } 

      return inSampleSize; 

     } 

     public static Bitmap DecodeSampleBitmapFromStream(string URL, int reqWidth, int reqHeight) 
     { 
      URL url = new URL(URL); 

      try 
      { 
       //______________________________________________________________ 
       // First decode with inJustDecodeBounds=true to check dimensions 
       BitmapFactory.Options options = new BitmapFactory.Options(); 
       options.InJustDecodeBounds = true; 
       BitmapFactory.DecodeStream(url.OpenConnection().InputStream, null, options); 

       //______________________ 
       // Calculate inSampleSize 
       options.InSampleSize = CalculateInSampleSize(options, reqWidth, reqHeight); 

       //____________________________________ 
       // Decode bitmap with inSampleSize set 
       options.InJustDecodeBounds = false; 
       return BitmapFactory.DecodeStream(url.OpenConnection().InputStream, null, options); 
      } 
      catch (Exception ex) 
      { 
       return null; 
      } 
      finally 
      { 
       url.Dispose(); 
      } 
     } 

我開始使用這種方法的列表GetView()函數此的AsyncTask:

public void loadBitmap(string url, ImageView imageView) 
     { 
      if (Common.cancelPotentialWork(url, imageView)) 
      { 
       BitmapWorkerTask task = new BitmapWorkerTask(imageView, 2, 80,80); 

       AsyncDrawable asyncDrawable = new AsyncDrawable(null, null, task); 

       imageView.SetImageDrawable(asyncDrawable); 

       task.Execute(url); 
      } 
     } 

一切按預期工作一段時間,但如果我不斷滾動瀏覽列表,我最終會開始獲取OutOfMemoryException秒,並且應用程序崩潰。我對Android列表工作原理的理解是,它在屏幕上移動時會處理ListItem視圖,但感覺好像沒有發生!

當我滾動列表時,感覺像所有這些位圖解碼都是出於什麼原因被存儲在內存中?我在這裏可能會丟失什麼,阻止了這些位圖被丟棄?我可以在哪裏實施致電Bitmap.Recycle()以確保位圖清除?

我做了一個測試,我在每次調用GetView時都調用了GC.Collect,這似乎確保我的內存使用情況相當一致,但我知道這不應該被需要,它會影響滾動性能。

爲什麼當我通過我的列表滾動而沒有調用GC.Collect()上午我沒有看到那些垃圾收集消息表明系統實際上正在做例行收集?

任何幫助表示讚賞,我失去了代碼的意志!

+0

如果你能擊敗他們... ...購買更多的內存。 :) – gdoron

回答

1

我對Android列表的工作原理的理解是,它在列表項視圖離開屏幕時處理掉,但感覺好像沒有發生!

這是不正確的。

Android的功能是堅持一組項目視圖,它試圖在屏幕關閉後再次使用它們。這是convertView參數的用途。

我看不到你的Adaptor代碼發佈在問題中,所以我不知道你的代碼是使用convertView參數,但我猜想它應該在convertView的情況下是:

  • 應該取消任何現有的異步獲取圖像/轉換
  • 它應該開始一個新的

的MvvmCross代碼可能有點太複雜,你作爲一個重ference/example here,但你至少可以看到使用中的convertView - 參見protected virtual View GetBindableView(View convertView, object source, int templateId)