2015-03-05 3 views
0

У меня есть приложение, которое получает фиксированное количество изображений с предварительного просмотра камеры и преобразует их в список Bitmaps. Для этого у меня есть следующий код:OOM при создании растровых изображений в приложении для Android

private Camera.PreviewCallback SetPreviewCallBack() { 
    return new Camera.PreviewCallback() { 
     @Override 
     public void onPreviewFrame(byte[] data, Camera camera) { 
    List<Bitmap> imageList = new ArrayList<Bitmap>(); 
    for (int i=0; i<30; i++) // Let's suppose this is the real loop. 
          // It's not, as the real loop takes each camera preview frame, 
          // instead of inserting the same one 30 times. 
          // But for this example, it's OK 
    { 
     imageList.add(GetBitmap(
        data, 
        previewWidth, // Calculated 
        previewHeight, // Calculated 
        previewFormat, // Calculated 
        previewRotation)); // Calculated 
    } 

} 

private Bitmap GetBitmap(byte[] data, int width, int height, int previewFormat, int rotation) { 

    YuvImage yuv = new YuvImage(data, previewFormat, width, height, null); 

    ByteArrayOutputStream out = new ByteArrayOutputStream(); 
    yuv.compressToJpeg(new Rect(0, 0, width, height), 50, out); 

    byte[] bytes = out.toByteArray(); 
    final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); 

    Bitmap imageResult = RotateImage(bitmap, 4 - rotation); 
    bitmap.recycle(); 

    return imageResult; 
} 

private Bitmap RotateImage(Bitmap rotateImage, int rotation) { 

    Matrix matrix = new Matrix(); 
    switch (rotation) { 
    case 1: 
     matrix.postRotate(270); 
     break; 
    case 2: 
     matrix.postRotate(180); 
     break; 
    case 3: 
     matrix.postRotate(90); 
     break; 
    } 

    return Bitmap.createBitmap(rotateImage, 0, 0, rotateImage.getWidth(), 
      rotateImage.getHeight(), matrix, true); 
} 

Что я делаю: - хранить этот образ на Singleton класс для того, чтобы получить доступ к нему с другого Activity contanied в моем же приложении. - Я снова вызываю этот фрагмент кода (когда происходит какое-то событие) и повторяю процесс извлечения/сохранения изображения.

03-05 09:35:13.339: E/AndroidRuntime(8762): FATAL EXCEPTION: Thread-818 
03-05 09:35:13.339: E/AndroidRuntime(8762): java.lang.OutOfMemoryError 
03-05 09:35:13.339: E/AndroidRuntime(8762):  at android.graphics.Bitmap.nativeCreate(Native Method) 
03-05 09:35:13.339: E/AndroidRuntime(8762):  at android.graphics.Bitmap.createBitmap(Bitmap.java:726) 
03-05 09:35:13.339: E/AndroidRuntime(8762):  at android.graphics.Bitmap.createBitmap(Bitmap.java:703) 
03-05 09:35:13.339: E/AndroidRuntime(8762):  at android.graphics.Bitmap.createBitmap(Bitmap.java:636) 
03-05 09:35:13.339: E/AndroidRuntime(8762):  at com.facephi.sdk.ui.CameraPreview.RotateImage(CameraPreview.java:779) 
03-05 09:35:13.339: E/AndroidRuntime(8762):  at com.facephi.sdk.ui.CameraPreview.GetBitmap(CameraPreview.java:712) 

Я пытаюсь использовать лучшие методы, чтобы удалить правильным образом неиспользованного Bitmaps как размещены here, но не повезло ...

Любая идея о том, почему у меня это ООМ ошибка?

+0

Установите 'android: largeHeap =" true "' для активности, а также используйте 'BitmapFactory.options' для масштабирования ваших объектов растровых изображений с помощью свойства' inSampleSize'. – Piyush

+0

@Devill, использующий 'android: largeHeap', решает мою проблему, но я боюсь, что он, наконец, потерпит крах, поскольку он только увеличит объем памяти, который будет иметь приложение, но в какой-то момент я тоже доберусь до него. И что будет «BitmapFactory.опции' делать? – Sonhja

+0

OOM означает, что он превышает предел памяти в размере кучи. Для «BitmapFactory.Options» вы можете проверить http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html – Piyush

ответ

1

1. Попробуйте использовать

BitmapFactory.Options options=new BitmapFactory.Options(); 
options.inSampleSize = 8; 
options.inDither = false; 
options.inPurgeable = true; 
options.inInputShareable = true; 
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); 

Это значительно поможет сэкономить память.

2. Не храните в своем Bitmap объекте в одном и том же месте. Это всегда приведет к OutOfMemoryError. Создайте Bitmap заново, каждый раз, когда вы хотите его использовать.

EDIT:

Смотрите также официальный ThreadSample пример и Displaying Bitmaps Efficiently учебник.

+0

Что такое параметр 'is'? – Sonhja

+0

Я храню этот список изображений на 'Singleton', потому что мне нужно передать этот список изображений между двумя действиями. Если я попытаюсь разобрать его, я всегда превышу лимит «Parcelable». Но позвольте мне попробовать ваше решение. Если мне повезет, использование этого «BitmapFactory.Options» уменьшит размер изображения достаточно, поэтому я смогу напрямую проанализировать его, и мне не нужно его хранить в классе «Singleton». – Sonhja

+0

Я отредактировал ответ, поскольку вы используете 'decodeByteArray()' вместо 'decodeStream()' –

2

У вас очень ограниченная память на Android, поэтому вы получаете это исключение. В общем, вам не нужно хранить столько изображений в памяти и загружать их только тогда, когда они вам понадобятся (то есть показывая их или что-то делать с ними), и должны избавиться от них (recycle()), как только вы закончите с их. Кроме того, вы должны загружать их с таким низким разрешением, как вы можете получить.

Сказав все это, и не зная, почему вы должны иметь их все в памяти (у вас может быть законная причина), вы можете увеличить размер кучи для своего приложения, указав соответствующий атрибут в своем манифесте (http://developer.android.com/guide/topics/manifest/application-element.html#largeHeap), но даже это не гарантирует, что вы можете загружать слишком много изображений.

Помните, что по умолчанию Android будет использовать 4 байта на пиксель для ваших изображений, поэтому, если у вас есть 30 изображений, каждый с плотностью 1 миллион пикселей, вы используете 120 миллионов байт или примерно 120 МБ памяти. Напротив, байт по умолчанию, назначенный вашему приложению, может быть таким же низким, как и мертвый 16mb (зависит от многих случаев. См. Здесь для получения более подробной информации: Android heap size on different phones/devices and OS versions).

Если вы можете каким-то образом изменить свой код, чтобы вам не пришлось загружать их в память, вы можете посмотреть на кеширование растровых изображений (http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html), что может значительно помочь в решении проблем с памятью.

+1

Следует отметить, что вы можете уменьшить количество байтов, выделяемых на пиксель, путем выделения растровые изображения с Config.RGB_565 (эти растровые изображения не будут иметь альфа-канал чуть более низкого качества, но если вам это не понадобится, это уменьшит выделение памяти наполовину). –

+0

@kha Интересно ... позвольте мне попробовать. Конечно, как вы уже упоминали, у меня есть законная причина, почему мне нужны эти образы. Я настаиваю на том, чтобы не хранить их, но до сих пор я должен их получить. Я собираюсь проверить ваши ссылки. Они выглядят интересными. – Sonhja

+0

@GilMoshayof Очень справедливая точка. Я просто ссылался на поведение по умолчанию. Я сам меняю на RGB_565, но большинство людей просто используют значение по умолчанию, поэтому я написал это в качестве примера. Пример в кодовой базе исходного вопроса не менял ее. – kha

Смежные вопросы