как выйти из OutOfMemoryError: размер растрового изображения превышает бюджет виртуальной машины - PullRequest
0 голосов
/ 27 февраля 2012

Я очень новичок в Android и пытаюсь поместить изображения SDcard в сетку, используя Bitmap и BitmapFactory. Но это вызывает ошибку вроде:

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)

Ответы [ 3 ]

5 голосов
/ 27 февраля 2012

Ну хорошо, по-видимому, на этот вопрос ответят в сотый раз на SO.Вот идея:

  • сначала в зависимости от версии Android на устройстве:

    , если вы используете версию 2.x Android и ниже (любая версия доСота) память, занятая вашими экземплярами Bitmap, НЕ будет отражаться в объеме свободной памяти, имеющейся в куче, как сообщается методами Runtime.getRuntime (). XxxMemory ().Эти экземпляры помещаются в память за пределами кучи.Если вы хотите отследить, сколько памяти будет использовать экземпляр растрового изображения, вы должны вручную вычислить его как imageWidth * imageHeight * 4.Это даст вам байты, взятые вашим изображением в (вне кучи) памяти.Это потребление памяти, а также потребление памяти в куче должно иметь общее значение, которое ниже максимального объема памяти, выделенного Android для вашего приложения на определенном устройстве, в противном случае вы получите ошибку OutOfMemory.

  • общий объем памяти, выделенный для процесса Android, сильно зависит от устройства и версии Android.Это может быть где-то между 16 и 48 Мег.На старых устройствах - 16 или 32. На новых - 32 или 48. Это нужно проверить отдельно на каждом устройстве, на которое вы хотите настроить таргетинг.Вы можете сделать это также во время выполнения и использовать его как руководство относительно того, сколько вещей вы можете поместить в память (может быть, вы хотите уменьшить выборку изображений перед их загрузкой на устройство, которое выделяет 16 МБ памяти для вашего приложения)

  • В Android Honeycomb (версия 3.xx) и более поздних версиях экземпляр Bitmap будет использовать оперативную память.Это облегчает отслеживание того, сколько свободной памяти у вас остается после загрузки изображений.Также в этих версиях Android экземпляр Bitmap будет собираться (когда это возможно) автоматически.На этапе предварительного создания сот вы должны вручную вызвать

    Bitmap.recycle ();

, чтобы освободить память, занятую вашим экземпляром растрового изображения.

  • При использовании BitmapFactory для декодирования изображений (и создания экземпляров растрового изображения) вы можете передавать такие параметры, как только получить ширину и высоту изображения или уменьшить его до декодирования.Это может помочь вам оценить, сколько памяти займет изображение, прежде чем вы на самом деле поместите его в память.Ознакомьтесь с документами: http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html

  • Если вы чувствуете энтузиазм, вы можете обмануть ограничение памяти все вместе, хотя это работает не на всех устройствах: создайте OpenglSurfaceView и отобразите ваши изображения в виде текстурна квадроциклах.Вы можете использовать ортогональную проекцию для простоты (если вам нужно только создать видимость 2d).Хитрость в том, что вы можете загрузить изображение в память в виде растрового изображения, назначить его текстуре OpenGL и очистить этот экземпляр растрового изображения.Фактическое изображение будет по-прежнему отображаться из объекта Texture, и эти объекты не ограничены ограничением памяти для каждого процесса.

1 голос
/ 27 февраля 2012

Не копируйте изображение в полном качестве в свое приложение. Используйте класс Options, чтобы немного сэмплировать качество / размер:

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() может привести к некоторым ошибкам во время выполнения, если ваше приложение имеет какую-то ссылку на старые растровые изображения (может быть трудно обнаружить), поэтому будьте осторожны при использовании.

Дайте мне знать, если это поможет.

0 голосов
/ 27 февраля 2012

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;
} 

}

Чтобы получить путь к изображению из URI, используйте эту функцию:

private String decodePath(Uri data)
{        
     Cursor cursor = getContentResolver().query(data, null, null, null, null);
     cursor.moveToFirst(); 
     int idx = cursor.getColumnIndex(ImageColumns.DATA);
     String fileSrc = cursor.getString(idx);   
     return fileSrc;

}
...