2013-07-12 32 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); 

     }