2013-11-14 45 views
0

研究員,我尋求你的幫助。在Xamarin.Android中使用位圖處理Android內存Android的內存

我有一個內存相關的問題。我真的不知道如何解決這個問題,所以我只會介紹我的代碼片段。請記住,雖然它在Xamarin.Android上,但它也適用於普通的Android。

我使用一個庫,它只是開始一個相機的意圖。我正在使用的庫(或組件)是:http://components.xamarin.com/view/xamarin.mobile。這不是真的有意義,但也許你可以指出我爲什麼我應該或不應該使用這個庫的其他見解。

無論如何,啓動相機並捕獲輸入。我使用以下代碼:

private void StartCamera() { 
     var picker = new MediaPicker (this); 

     if (! picker.IsCameraAvailable) { 

      Toast.MakeText (this, "No available camera found!", ToastLength.Short).Show(); 

     } else { 
      var intent = picker.GetTakePhotoUI (new StoreCameraMediaOptions{ 
       Name = "photo.jpg", 
       Directory = "photos" 
      }); 

      StartActivityForResult (intent, 1); 

     } 
    } 

onActivityForResult()方法在我從此相機意圖返回時調用。在該方法中,i執行以下操作:

protected override async void OnActivityResult (int requestCode, Result resultCode, Intent data) 
    { 
     // User canceled 
     if (resultCode == Result.Canceled) 
      return; 

     System.GC.Collect(); 

     dialog = new ProgressDialog (this); 
     dialog.SetProgressStyle (ProgressDialogStyle.Spinner); 
     dialog.SetIconAttribute (Android.Resource.Attribute.DialogIcon); 
     dialog.SetTitle (Resources.GetString(Resource.String.dialog_picture_sending_title)); 
     dialog.SetMessage (Resources.GetString(Resource.String.dialog_picture_sending_text)); 
     dialog.SetCanceledOnTouchOutside (false); 
     dialog.SetCancelable (false); 
     dialog.Show(); 

     MediaFile file = await data.GetMediaFileExtraAsync (this); 

     await ConvertingAndSendingTask (file); 

     dialog.Hide(); 

     await SetupView(); 

    } 

然後,在我的ConvertingAndSendingTask()i中的圖象轉換成具有縮放的位圖的期望的尺寸。代碼如下:

public async Task ConvertingAndSendingTask(MediaFile file) { 

     try{ 

      System.GC.Collect(); 

      int targetW = 1600; 
      int targetH = 1200; 

      BitmapFactory.Options options = new BitmapFactory.Options(); 
      options.InJustDecodeBounds = true; 
      Bitmap b = BitmapFactory.DecodeFile (file.Path, options); 

      int photoW = options.OutWidth; 
      int photoH = options.OutHeight; 

      int scaleFactor = Math.Min(photoW/targetW, photoH/targetH); 

      options.InJustDecodeBounds = false; 
      options.InSampleSize  = scaleFactor; 
      options.InPurgeable  = true; 

      Bitmap bitmap = BitmapFactory.DecodeFile(file.Path, options); 

      float resizeFactor = CalculateInSampleSize (options, 1600, 1200); 

      Bitmap bit = Bitmap.CreateScaledBitmap(bitmap, (int)(bitmap.Width/resizeFactor),(int)(bitmap.Height/resizeFactor), false); 

      bitmap.Recycle(); 

      System.GC.Collect(); 

      byte[] data = BitmapToBytes(bit); 

      bit.Recycle(); 

      System.GC.Collect(); 

      await app.api.SendPhoto (data, app.ChosenAlbum.foreign_id); 

      bitmap.Recycle(); 

      System.GC.Collect(); 


     } catch(Exception e) { 

      System.Diagnostics.Debug.WriteLine (e.StackTrace); 

     } 




    } 

那麼,此方法將它發送好與多個存儲器更新的設備,但是,下端設備它在一個內存不足錯誤結束。或者反正幾乎是OOM。當Somethimes我進展順利,但是當我想要拍第二張或第三張照片時,它總是以OOM錯誤結束。

我意識到我正在做的是內存密集型。例如:

  1. 首先,我想要原始圖像的初始寬度和高度。
  2. 然後它被抽樣(我真的不知道它是否做得好)。
  3. 然後我加載採樣的位圖到內存中。
  4. 當我將它加載到內存中時,我的縮放位圖必須在回收()第一個位圖之前加載到內存中。
  5. 最終我需要一個字節[]通過網絡發送位圖。但是我需要在發佈我的縮放位圖之前先將其轉換。
  6. 然後我釋放我縮放的位圖併發送字節[]。
  7. 然後作爲最後一步,byte []需要從內存中釋放。我已經在我的BitmapToBytes()方法上做了如下所示的工作,但我想將其包含在其他的見解中。

    static byte[] BitmapToBytes(Bitmap bitmap) { 
        byte[] data = new byte[0]; 
        using (MemoryStream stream = new MemoryStream()) 
        { 
         bitmap.Compress (Bitmap.CompressFormat.Jpeg, 90, stream); 
         stream.Close(); 
         data = stream.ToArray(); 
        } 
        return data; 
    } 
    

是否有人看到任何好的部分,我可以優化這個過程?我知道我的記憶力很大,但我想不出另一種方式。

應該提到的是,我總是希望我的圖像爲1600x1200(橫向)或1200x1600(縱向)。我通過以下方式計算該值:

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

     if (height > reqHeight || width > reqWidth) { 

      // Calculate ratios of height and width to requested height and 
      // width 
      float heightRatio = ((float) height/(float) reqHeight); 
      float widthRatio = ((float) width/(float) reqWidth); 

      // Choose the smallest ratio as inSampleSize value, this will 
      // guarantee 
      // a final image with both dimensions larger than or equal to the 
      // requested height and width. 
      inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; 
     } 

     if(height < reqHeight || width < reqWidth) { 
      // Calculate ratios of height and width to requested height and 
      // width 
      float heightRatio = ((float) reqHeight/(float) height); 
      float widthRatio = ((float) reqWidth/(float) width); 

      // Choose the smallest ratio as inSampleSize value, this will 
      // guarantee 
      // a final image with both dimensions larger than or equal to the 
      // requested height and width. 
      inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; 
     } 

     return inSampleSize; 
    } 

有沒有人有任何建議或替代工作流?

我會這麼幫忙!

+0

嘿傑克,你解決這個問題,我正要做類似的工作。 –

回答

2

這可能是一個非常延遲的答覆,但可能有助於某人得到同樣的問題。

  • 使用計算出的InSampleSize值(選項)將文件解碼爲 位圖。這本身會生成縮小的位圖,而不是在CreateScaledBitmap中使用。
  • 如果您期待高分辨率圖像作爲輸出,那麼難以處理OutOfMemory問題的是 。因爲分辨率更高的圖像沒有提供任何可見的優勢,但仍佔用了寶貴的內存,並且由於視圖執行了額外的縮放而導致額外的性能開銷。 [xamarin doc]
  • 計算與圖像視圖高度和寬度有關的位圖目標寬度和高度。你可以通過MeasuredHeight和MeasuredWidth屬性來計算。 [注:此之後才完成圖像絞車]
  • 考慮使用異步方法來解碼文件在主線程上,而不是運行[DecodeFileAsync]

更多細節關注該http://appliedcodelog.blogspot.in/2015/07/avoiding-imagebitmap.html