Android 如何走出OutOfMemoryError:位图大小超出VM预算

Android 如何走出OutOfMemoryError:位图大小超出VM预算,android,layout,widget,Android,Layout,Widget,我是android新手,尝试使用位图和BitmapFactory将SD卡图像放到网格视图中。 但它会导致如下错误: ERROR/AndroidRuntime(6137): java.lang.OutOfMemoryError: bitmap size exceeds VM budget ERROR/AndroidRuntime(6137): at android.graphics.BitmapFactory.nativeDecodeStream(Native Metho

我是android新手,尝试使用位图和BitmapFactory将SD卡图像放到网格视图中。 但它会导致如下错误:

ERROR/AndroidRuntime(6137):     java.lang.OutOfMemoryError: bitmap size exceeds VM budget     
ERROR/AndroidRuntime(6137):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
ERROR/AndroidRuntime(6137):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:459) 
ERROR/AndroidRuntime(6137):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:271)

不要先将完整质量的图像复制到应用程序中。使用
选项
类对质量/大小进行抽样:

ContentResolver cr = getContentResolver();
InputStream is = cr.openInputStream(chosenImageUri);
Options optionSample = new BitmapFactory.Options();
optionSample.inSampleSize = 4; // Or 8 for smaller image
Bitmap bitmap = BitmapFactory.decodeStream(is, null, optionSample);
// Bitmap bitmap = BitmapFactory.decodeFile(filePathString, optionSample);
如果要创建缩略图位图,请尝试使用
inSampleSize=8

如果您发现自己创建了多个位图对象,每个对象都对同一图像进行了一些更改,请尝试使用
Bitmap.recycle()
。但是
recycle()
如果您的应用程序引用了一些旧位图(可能很难检测),则可能会导致一些运行时错误,因此请小心使用它


如果有帮助,请告诉我。

不要先将完整质量的图像复制到应用程序中。使用
选项
类对质量/大小进行抽样:

ContentResolver cr = getContentResolver();
InputStream is = cr.openInputStream(chosenImageUri);
Options optionSample = new BitmapFactory.Options();
optionSample.inSampleSize = 4; // Or 8 for smaller image
Bitmap bitmap = BitmapFactory.decodeStream(is, null, optionSample);
// Bitmap bitmap = BitmapFactory.decodeFile(filePathString, optionSample);
如果要创建缩略图位图,请尝试使用
inSampleSize=8

如果您发现自己创建了多个位图对象,每个对象都对同一图像进行了一些更改,请尝试使用
Bitmap.recycle()
。但是
recycle()
如果您的应用程序引用了一些旧位图(可能很难检测),则可能会导致一些运行时错误,因此请小心使用它


如果有帮助,请告诉我。

Android更关心内存,BitmapFactory只接受有限大小的图像

我认为以下内容将帮助您在使用前缩放图像

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;

public class ImageScale 
{

/**
 * Decodes the path of the image to Bitmap Image.
 * @param imagePath : path of the image.
 * @return Bitmap image.
 */

 public Bitmap decodeImage(String imagePath)
 {  
     Bitmap bitmap=null;

     try
     {

         File file=new File(imagePath);
         BitmapFactory.Options o = new BitmapFactory.Options();
         o.inJustDecodeBounds = true;

         BitmapFactory.decodeStream(new FileInputStream(file),null,o);
         final int REQUIRED_SIZE=200;
         int width_tmp=o.outWidth, height_tmp=o.outHeight;

         int scale=1;
         while(true)
         {
             if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
             break;
             width_tmp/=2;
             height_tmp/=2;
             scale*=2;  
         }  

         BitmapFactory.Options options=new BitmapFactory.Options();

         options.inSampleSize=scale;
         bitmap=BitmapFactory.decodeStream(new FileInputStream(file), null, options);

     }  
     catch(Exception e) 
     {  
         bitmap = null;
     }      
     return bitmap; 
 }

 /**
  * Resizes the given Bitmap to Given size.
  * @param bm : Bitmap to resize.
  * @param newHeight : Height to resize.
  * @param newWidth : Width to resize.
  * @return Resized Bitmap.
  */
 public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) 
 {

    Bitmap resizedBitmap = null;
    try
    {
        if(bm!=null)
        {
            int width = bm.getWidth();
            int height = bm.getHeight();
            float scaleWidth = ((float) newWidth) / width;
            float scaleHeight = ((float) newHeight) / height;
            // create a matrix for the manipulation
            Matrix matrix = new Matrix();
            // resize the bit map
            matrix.postScale(scaleWidth, scaleHeight);
            // recreate the new Bitmap
            resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);

        }
    }
    catch(Exception e)
    {
        resizedBitmap = null;
    }

    return resizedBitmap;
} 

}

Android更关心内存,BitmapFactory只接受有限大小的图像

我认为以下内容将帮助您在使用前缩放图像

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;

public class ImageScale 
{

/**
 * Decodes the path of the image to Bitmap Image.
 * @param imagePath : path of the image.
 * @return Bitmap image.
 */

 public Bitmap decodeImage(String imagePath)
 {  
     Bitmap bitmap=null;

     try
     {

         File file=new File(imagePath);
         BitmapFactory.Options o = new BitmapFactory.Options();
         o.inJustDecodeBounds = true;

         BitmapFactory.decodeStream(new FileInputStream(file),null,o);
         final int REQUIRED_SIZE=200;
         int width_tmp=o.outWidth, height_tmp=o.outHeight;

         int scale=1;
         while(true)
         {
             if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
             break;
             width_tmp/=2;
             height_tmp/=2;
             scale*=2;  
         }  

         BitmapFactory.Options options=new BitmapFactory.Options();

         options.inSampleSize=scale;
         bitmap=BitmapFactory.decodeStream(new FileInputStream(file), null, options);

     }  
     catch(Exception e) 
     {  
         bitmap = null;
     }      
     return bitmap; 
 }

 /**
  * Resizes the given Bitmap to Given size.
  * @param bm : Bitmap to resize.
  * @param newHeight : Height to resize.
  * @param newWidth : Width to resize.
  * @return Resized Bitmap.
  */
 public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) 
 {

    Bitmap resizedBitmap = null;
    try
    {
        if(bm!=null)
        {
            int width = bm.getWidth();
            int height = bm.getHeight();
            float scaleWidth = ((float) newWidth) / width;
            float scaleHeight = ((float) newHeight) / height;
            // create a matrix for the manipulation
            Matrix matrix = new Matrix();
            // resize the bit map
            matrix.postScale(scaleWidth, scaleHeight);
            // recreate the new Bitmap
            resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);

        }
    }
    catch(Exception e)
    {
        resizedBitmap = null;
    }

    return resizedBitmap;
} 

}

好吧,显然这个问题将在第100次得到回答。我的想法是:

  • 首先,根据设备上的Android版本:

    如果您使用的是Android 2.x及以下版本(蜂窝之前的任何版本),位图实例占用的内存将不会反映在Runtime.getRuntime().xxxMemory()方法报告的堆上的可用内存量中。这些实例放在堆外的内存中。如果要跟踪位图实例将使用多少内存,必须手动计算为imageWidth*imageHeight*4。这将为您提供映像在(堆外)内存中获取的字节。此内存消耗以及堆上内存消耗的总和必须低于Android为特定设备上的应用程序分配的最大内存,否则将出现OutOfMemory错误

  • Android分配给进程的总内存在很大程度上取决于设备和Android版本。这可能在16到48兆之间。在旧设备上是16或32。较新的是32或48。这一点,您必须单独检查每个您想要瞄准的设备。您也可以在运行时执行此操作,并将其用作您可以在内存中放入多少内容的指南(可能您希望在将图像加载到为应用程序分配16 Mb内存的设备上之前先对其进行减采样)

  • 在Android蜂巢(3.x.x版)及更高版本上,位图实例将在堆内存上使用。这使得跟踪加载图像后仍有多少可用内存变得更容易。同样在这些版本的Android中,位图实例将被自动垃圾收集(如果可能)。在蜂巢前,你必须打电话

    Bitmap.recycle()

释放位图实例占用的内存

  • 使用BitmapFactory解码图像(并创建位图实例)时,可以传入选项,例如仅获取图像的宽度和高度,或在解码前对其进行下采样。这可以帮助您在实际将图像放入内存之前评估图像将占用多少内存。查看文档:

  • 如果你觉得有冒险精神,你可以一起欺骗内存限制,尽管这并不适用于所有设备:创建OpenglSurfaceView,并将图像显示为四边形上的纹理。为了简单起见,可以使用正交投影(如果只需要给出2d的外观)。这里的诀窍是,您可以将图像作为位图加载到内存中,将其指定给OpenGL纹理并清除该位图实例。实际图像仍将从纹理对象显示,并且这些对象不受每进程内存限制的限制


    • 好吧,显然这个问题将在第100次得到回答。我的想法是:

      • 首先,根据设备上的Android版本:

        如果您使用的是Android 2.x及以下版本(蜂窝之前的任何版本),位图实例占用的内存将不会反映在Runtime.getRuntime().xxxMemory()方法报告的堆上的可用内存量中。这些实例放在堆外的内存中。如果要跟踪位图实例将使用多少内存,必须手动计算为imageWidth*imageHeight*4。这将为您提供映像在(堆外)内存中获取的字节。此内存消耗以及堆上内存消耗的总和必须低于Android为特定设备上的应用程序分配的最大内存,否则将出现OutOfMemory错误

      • Android分配给进程的总内存在很大程度上取决于设备和Android版本。这可能在16到48兆之间。在旧设备上是16或32。较新的是32或48。这一点,您必须单独检查每个您想要瞄准的设备。您也可以在运行时执行此操作,并将其用作您可以在内存中放入多少内容的指南(可能您希望在将图像加载到为应用程序分配16 Mb内存的设备上之前先对其进行减采样)

      • 在Android蜂巢(3.x.x版)及更高版本上,位图实例将在堆内存上使用。这使得追踪m是如何