2011-11-28 16 views
10

Tengo una aplicación que muestra bastantes imágenes para el usuario, y hemos estado viendo una gran cantidad de informes de errores con la excepción OutOfMemoryError.Rotar imágenes en android. ¿Hay una mejor manera?

Lo que actualmente lo que hacemos es la siguiente:

// Check if image is a landscape image 
if (bmp.getWidth() > bmp.getHeight()) { 
    // Rotate it to show as a landscape 
    Matrix m = image.getImageMatrix(); 
    m.postRotate(90); 
    bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true); 
} 
image.setImageBitmap(bmp); 

El problema obvio con esto es que tenemos que volver a crear el mapa de bits de la imagen en la memoria y rotar la matriz, esto es bastante caro para la memoria.

Mi pregunta es simple:

¿Hay una mejor manera de girar las imágenes sin causar OutOfMemoryError?

+0

http://www.twintechs.com/2008/06/frame-by-frame-xml-animation-with-google-android/ –

+0

En qué línea se lanza la excepción – ingsaurabh

+0

@ Dr.nik Entonces, ¿sugieres rotar la imagen con una animación aunque no se suponga que sea una rotación animada? – Draiken

Respuesta

0

puede probar:

image.setImageBitmap(null); 
// Check if image is a landscape image 
if (bmp.getWidth() > bmp.getHeight()) { 
    // Rotate it to show as a landscape 
    Matrix m = image.getImageMatrix(); 
    m.postRotate(90); 
    bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true); 
} 
BitmapDrawable bd = new BitmapDrawable(mContext.getResources(), bmp); 
bmp.recycle(); 
bmp = null; 
setImageDrawable(bd); 
bd = null; 
+0

Como se indicó en un comentario en la pregunta, el error está ocurriendo en la línea 'Bitmap.createBitmap'. – Draiken

+0

gracias, no vi que – Caner

0

Cuando se trabaja con una gran cantidad de mapas de bits Asegúrese de llamar a reciclar() sobre ellos tan pronto como éstos no son necesarios. Esta llamada liberará instantáneamente la memoria asociada con un mapa de bits particular.

En su caso, si no necesita el mapa de bits original después de la rotación, recíclelo. Algo a lo largo de las líneas de:

Bitmap result = bmp; 

// Check if image is a landscape image 
if (bmp.getWidth() > bmp.getHeight()) { 
    // Rotate it to show as a landscape 
    Matrix m = image.getImageMatrix(); 
    m.postRotate(90); 
    result = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true); 
    // rotating done, original not needed => recycle() 
    bmp.recycle(); 
} 

image.setImageBitmap(result); 
+0

bien, no sabía nada de eso. El problema es que necesito el mapa de bits original para rotarlo y el error ocurre antes de poder liberarlo de la memoria:/ – Draiken

+0

En realidad, probé el reciclaje y ahora solo muestra una pantalla negra en lugar del mapa de bits. Supongo que internamente es un puntero y no puedo meterme con eso. – Draiken

+0

Hm. Bitmap.createBitmap() debería crear Bitmap completamente nuevo, por lo que liberar uno original debería ser seguro. De hecho, utilizo mucho este enfoque en mi propio código y funciona bastante bien. ¿Está seguro de que está reciclando el mapa de bits correcto y solo * después de asignar la referencia al mapa de bits girado devuelto por createBitmap() a otra referencia (como en mi ejemplo). Podría generar un error en createBitmap() si ya usó toda la memoria. Pero si cada vez que lo liberas correctamente con recycle() debería estar bien en las siguientes invocaciones ... – dimsuz

6

2 métodos de la rotación de una imagen de gran tamaño:

  1. usando JNI, como on this post.

  2. usando un archivo: es un camino muy lento (dependiendo de la entrada y el dispositivo, pero aún muy lento), que coloca primero la imagen decodificada girada en el disco, en lugar de ponerla en la memoria.

código de la utilización de un archivo es el siguiente:

private void rotateCw90Degrees() 
    { 
    Bitmap bitmap=BitmapFactory.decodeResource(getResources(),INPUT_IMAGE_RES_ID); 
    // 12 => 7531 
    // 34 => 8642 
    // 56 => 
    // 78 => 
    final int height=bitmap.getHeight(); 
    final int width=bitmap.getWidth(); 
    try 
    { 
    final DataOutputStream outputStream=new DataOutputStream(new BufferedOutputStream(openFileOutput(ROTATED_IMAGE_FILENAME,Context.MODE_PRIVATE))); 
    for(int x=0;x<width;++x) 
     for(int y=height-1;y>=0;--y) 
     { 
     final int pixel=bitmap.getPixel(x,y); 
     outputStream.writeInt(pixel); 
     } 
    outputStream.flush(); 
    outputStream.close(); 
    bitmap.recycle(); 
    final int newWidth=height; 
    final int newHeight=width; 
    bitmap=Bitmap.createBitmap(newWidth,newHeight,bitmap.getConfig()); 
    final DataInputStream inputStream=new DataInputStream(new BufferedInputStream(openFileInput(ROTATED_IMAGE_FILENAME))); 
    for(int y=0;y<newHeight;++y) 
     for(int x=0;x<newWidth;++x) 
     { 
     final int pixel=inputStream.readInt(); 
     bitmap.setPixel(x,y,pixel); 
     } 
    inputStream.close(); 
    new File(getFilesDir(),ROTATED_IMAGE_FILENAME).delete(); 
    saveBitmapToFile(bitmap); //for checking the output 
    } 
    catch(final IOException e) 
    { 
    e.printStackTrace(); 
    } 
    } 
+0

@android_developer. solución de archivos, creo que vale la pena el tiempo para evitar todos los problemas de memoria. –

+0

@PeterFile Creo que deberías hacerlo en múltiples retrocesos, desde los más rápidos (aunque más peligrosos) hasta los más lentos (aunque más seguros): primero intente usando la forma normal (usando el montón). Si eso falla (no hay suficiente memoria), use la solución JNI. Si eso falla (no hay suficiente memoria), use la solución de almacenamiento. La solución de almacenamiento también puede fallar (no hay suficiente almacenamiento), ya que el archivo que se crea es el mapa de bits decodificado, y no comprimido, pero esto podría ser algo raro que ocurra. –

+0

@androiddeveloper ¿Mantiene la solución el tamaño y la calidad de la imagen original? –

Cuestiones relacionadas