2013-04-29 2 views
6

В onPictureTaken, я хочу сделать следующее:Как избежать «исключения из памяти» при обработке Bitmap?

Bitmap decodedPicture = BitmapFactory.decodeByteArray(data, 0, data.length); 
Matrix matrix = new Matrix(); 
matrix.preScale(-1.0f, 1.0f); 
Bitmap picture = Bitmap.createBitmap(decodedPicture, 0, 0, decodedPicture.getWidth(), decodedPicture.getHeight(), matrix, false); 

View v1 = mainLayout.getRootView(); 
v1.setDrawingCacheEnabled(true); 
Bitmap screenshot = Bitmap.createBitmap(v1.getDrawingCache()); 
v1.setDrawingCacheEnabled(false); 

Bitmap scaledPicture = Bitmap.createScaledBitmap(picture, screenshot.getWidth(), screenshot.getHeight(), true); 

Bitmap compos = Bitmap.createBitmap(scaledPicture.getWidth(), scaledPicture.getHeight(), Bitmap.Config.ARGB_8888); 

Canvas canvas = new Canvas(compos); 
canvas.drawBitmap(scaledPicture, new Matrix(), null); 
canvas.drawBitmap(screenshot, new Matrix(), null); 

MediaStore.Images.Media.insertImage(getContentResolver(), compos, "name" , "description"); 
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory()))); 

Мое единственное требование заключается в том, что я хотел бы сохранить высокое качество фото ... Кажется, я, возможно, придется пожертвовать этим.

На моих устройствах Nexus 4 и новее этот код работает нормально и, как ожидалось. Но на более старых устройствах, у которых меньше памяти, у меня заканчивается RAM! :(

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

+0

Вы можете Null некоторые ссылки, а затем намекают на GC? Это должно удалить некоторые ненужные данные ... – tilpner

ответ

3

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

http://zerocredibility.wordpress.com/2011/01/27/android-bitmap-scaling/

Возможно, вы, возможно, упростите этот класс в зависимости от ваших конкретных потребностей.

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

0

У вас так много объектов Bitmap, расположенных вокруг. попробуйте повторно использовать/повторно использовать часть этого.

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

 Bitmap decodedPicture = BitmapFactory.decodeByteArray(data, 0, data.length); 
     Matrix matrix = new Matrix(); 
     matrix.preScale(-1.0f, 1.0f); 
     Bitmap picture = Bitmap.createBitmap(decodedPicture, 0, 0, decodedPicture.getWidth(), decodedPicture.getHeight(), matrix, false); 

     decodedPicture.recycle(); 
     decodedPicture=null; 

     View v1 = mainLayout.getRootView(); 
     v1.setDrawingCacheEnabled(true); 
     Bitmap screenshot = Bitmap.createBitmap(v1.getDrawingCache()); 
     v1.setDrawingCacheEnabled(false); 

     Bitmap scaledPicture = Bitmap.createScaledBitmap(picture, screenshot.getWidth(), screenshot.getHeight(), true); 

     picture.recycle(); 
     picture=null; 

     Bitmap compos = Bitmap.createBitmap(scaledPicture.getWidth(), scaledPicture.getHeight(), Bitmap.Config.ARGB_8888); 

     Canvas canvas = new Canvas(compos); 
     canvas.drawBitmap(scaledPicture, new Matrix(), null); 
     canvas.drawBitmap(screenshot, new Matrix(), null); 

     MediaStore.Images.Media.insertImage(getContentResolver(), compos, "name" , "description"); 
     sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory()))); 

Также обратите внимание на площадь вашей памяти, убедитесь, что используемая вами разумная память устройства не слишком велика.

FYI, на пост honycomb устройства растровое изображение пикселей, выделенное на нативный слой. Вам нужно recycle() или finalizer() для восстановления модуля памяти

+0

с использованием 'finalize()' - плохая идея. http://stackoverflow.com/questions/2506488/java-finalize-method-call. также из документации Android на 'recycle()': 'Это расширенный вызов и, как правило, не нужно вызывать, поскольку обычный процесс GC освободит эту память, если больше нет ссылок на это растровое изображение.' –

0

Учитывая вы не хотите, чтобы изменить размер растрового изображения и не хотите, чтобы отобразить его, я бы сделал что-то вроде этого:

  1. Загрузите Bitmap с inJustDecodeBounds, чтобы увидеть его оригинальную высоту и ширину (код из here)

    final BitmapFactory.Options options = new BitmapFactory.Options(); 
    options.inJustDecodeBounds = true; 
    BitmapFactory.decodeResource(res, resId, options); 
    
    // Calculate inSampleSize 
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); 
    
  2. в зависимости от размера и памяти у вас есть, вы можете непосредственно обрабатывать его оттуда (т.е. загрузить Bitmap) или продолжить для загрузки нескольких блоков указанного битового массива с помощью метода Bitmap.createBitmap, который позволяет загружать только кусок данных. Необязательно: рассмотрите возможность преобразования его в байтовый массив (см. Код ниже) и null + recycle() перед обработкой блока.

код

ByteArrayOutputStream byteArrayBitmapStream = new ByteArrayOutputStream(); 
bitmapPicture.compress(Bitmap.CompressFormat.PNG, COMPRESSION_QUALITY, byteArrayBitmapStream); 
byte[] b = byteArrayBitmapStream.toByteArray(); 
0

Я использую для работы с классом Bitmap WeakReference и после того, как я всегда называю рецикл на экземпляр объекта WeakReference, код сниппета для поворота изображения:

BitmapFactory.Options options = new BitmapFactory.Options(); 
options.inScaled = false; 
options.inPurgeable = true; 
options.inInputShareable = true; 
options.inPreferredConfig = Bitmap.Config.RGB_565; 
WeakReference<Bitmap> imageBitmapReference = new WeakReference<Bitmap>(BitmapFactory.decodeByteArray(params[0], 0, params[0].length, options)); 

Matrix mat = new Matrix(); 
mat.postRotate(90.0f); 
imageBitmapReference = new WeakReference<Bitmap (Bitmap.createBitmap(imageBitmapReference.get(), 0, 0, resolution[0], resolution[1], mat, true)); 

FileOutputStream fos = new FileOutputStream(filename); 
imageBitmapReference.get().compress(Bitmap.CompressFormat.PNG, 100, fos); 
fos.flush(); 
fos.close(); 
imageBitmapReference.get().recycle(); 

И второе решение, как работать с Bitmap и не получить OutOfMemory Exception - использовать библиотеку Universal Image Loader

(Разумеется, это третье решение, установленное в вашем AndroidManifest свойстве android: largeHeap = "true" и действительно НЕ ИСПОЛЬЗУЙТЕ ЭТОТ свойство).

Идеальный материал на http://developer.android.com/training/displaying-bitmaps/index.html и видео https://www.youtube.com/watch?v=_CruQY55HOk

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