2013-07-12 83 views
1

我有一個應用程序,它允許用戶拍照。照片拍完後,用戶可以將其發送到我的網絡服務器。但在我這樣做之前,它需要調整位圖的大小,因爲我希望將一致的大小發送到我的網絡服務器。用於android的單聲道內存高效位圖處理

無論如何,我用來將位圖加載到內存然後操作它的代碼似乎佔用了大量內存。此代碼目前正在使用:

/* 
    * This method is used to calculate image size. 
    * And also resize/scale image down to 1600 x 1200 
    */ 
    private void ResizeBitmapAndSendToWebServer(string album_id) { 

     Bitmap bm = null; 
        // This line is taking up to much memory each time.. 
     Bitmap bitmap = MediaStore.Images.Media.GetBitmap(Android.App.Application.Context.ApplicationContext.ContentResolver,fileUri); 

        /* 
        * My question is : Could i do the next image manipulation 
        * before i even load the bitmap into memory? 
        */ 
     int width = bitmap.Width; 
     int height = bitmap.Height; 

     if (width >= height) { // <-- Landscape picture 

      float scaledWidth = (float)height/width; 

      if (width > 1600) { 
       bm = Bitmap.CreateScaledBitmap (bitmap, 1600, (int)(1600 * scaledWidth), true); 
      } else { 
       bm = bitmap; 
      } 
     } else { 

      float scaledHeight = (float)width/height; 

      if (height > 1600) { 
       bm = Bitmap.CreateScaledBitmap (bitmap, (int)(1600 * scaledHeight), 1600 , true); 
      } else { 
       bm = bitmap; 
      } 

     } 
        // End of question code block. 

     MemoryStream stream = new MemoryStream(); 
     bitmap.Compress (Bitmap.CompressFormat.Jpeg, 80, stream); 
     byte[] bitmapData = stream.ToArray(); 
     bitmap.Dispose(); 

     app.api.SendPhoto (Base64.EncodeToString (bitmapData, Base64Flags.Default), album_id); 

    } 

什麼將是一個很好,乾淨的方法來解決這種內存問題?

編輯1:

閱讀其他職位後,很明顯,我認爲我在做一些低效的事情與我的代碼。這是我一直在做的事情:

  1. 將完整的位圖加載到內存中。
  2. 決定它是不是風景。
  3. 然後用正確的尺寸創建新的位圖。
  4. 然後將此位圖轉換爲字節數組
  5. 處置初始位圖。 (但不要從內存中刪除縮放的位圖)。

我真正應該做的:

  1. 確定真正的位圖尺寸,而不加載到內存:

    private void FancyMethodForDeterminingImageDimensions() { 
    
        BitmapFactory.Options options = new BitmapFactory.Options(); 
        options.InJustDecodeBounds = true; 
    
        BitmapFactory.DecodeFile(fileUri.Path, options); 
    
        // Now the dimensions of the bitmap are known without loading 
        // the bitmap into memory. 
        // I am not further going to explain this, i think the purpose is 
        // explaining enough. 
        int outWidth = options.OutWidth; 
        int outHeight = options.OutHeight; 
    
    } 
    

如果設置爲true,解碼器將返回null(無位圖),但仍然會設置out ...字段,允許調用者查詢 位圖,而不必爲其像素分配內存。

  1. 現在我知道真正的尺寸。所以我可以下載它之前我加載到內存中。
  2. (在我的情況)將位圖轉換爲base64字符串併發送它。
  3. 處理所有內容以清除內存。

我目前不能測試這個,因爲我不在我的開發機器上。任何人都可以給我一些反饋,如果這是正確的方式?將不勝感激。

+0

無法在服務器端調整大小嗎?你也不使用(或處置)你的'bm'變量,爲什麼? –

+0

看到我的答案http://stackoverflow.com/questions/16183635/out-of-memory-error-on-setimageresource/16184893#16184893 –

+0

@Chintan allthough你的方法是正確的,它並不總是給權利inSampleSize。看到我的評論在這裏:[鏈接](http://stackoverflow.com/questions/17319361/best-practis-for-handling-bitmaps-in-android-with-mono-droid-xamarin-android/17330028?noredirect=1 #comment25150832_17330028) – jHogen

回答

2
 private void ResizeBitmapAndSendToWebServer(string album_id) { 

      BitmapFactory.Options options = new BitmapFactory.Options(); 
      options.InJustDecodeBounds = true; // <-- This makes sure bitmap is not loaded into memory. 
      // Then get the properties of the bitmap 
      BitmapFactory.DecodeFile (fileUri.Path, options); 
      Android.Util.Log.Debug ("[BITMAP]" , string.Format("Original width : {0}, and height : {1}", options.OutWidth, options.OutHeight)); 
      // CalculateInSampleSize calculates the right aspect ratio for the picture and then calculate 
      // the factor where it will be downsampled with. 
      options.InSampleSize = CalculateInSampleSize (options, 1600, 1200); 
      Android.Util.Log.Debug ("[BITMAP]" , string.Format("Downsampling factor : {0}", CalculateInSampleSize (options, 1600, 1200))); 
      // Now that we know the downsampling factor, the right sized bitmap is loaded into memory. 
      // So we set the InJustDecodeBounds to false because we now know the exact dimensions. 
      options.InJustDecodeBounds = false; 
      // Now we are loading it with the correct options. And saving precious memory. 
      Bitmap bm = BitmapFactory.DecodeFile (fileUri.Path, options); 
      Android.Util.Log.Debug ("[BITMAP]" , string.Format("Downsampled width : {0}, and height : {1}", bm.Width, bm.Height)); 
      // Convert it to Base64 by first converting the bitmap to 
      // a byte array. Then convert the byte array to a Base64 String. 
      MemoryStream stream = new MemoryStream(); 
      bm.Compress (Bitmap.CompressFormat.Jpeg, 80, stream); 
      byte[] bitmapData = stream.ToArray(); 
      bm.Dispose(); 

      app.api.SendPhoto (Base64.EncodeToString (bitmapData, Base64Flags.Default), album_id); 

     }