2011-11-28 8 views
10

Ho un'app che visualizza un numero di immagini per l'utente, e abbiamo visto un sacco di segnalazioni di errori con l'eccezione OutOfMemoryError.Rotazione di immagini su Android. C'è un modo migliore?

Quello che attualmente facciamo è questo:

// 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); 

Il problema evidente con questo è che dobbiamo ricreare la bitmap dall'immagine sulla memoria e ruotare la matrice, questo è piuttosto costoso per la memoria.

mia domanda è semplice:

C'è un modo migliore per ruotare le immagini senza causare OutOfMemoryError?

+0

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

+0

Su quale riga viene lanciata l'eccezione – ingsaurabh

+0

@ Dr.nik Quindi, stai suggerendo di ruotare l'immagine con un'animazione anche se non si suppone che sia una rotazione animata? – Draiken

risposta

0

si può provare:

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

Come indicato in un commento nella domanda, l'errore si sta verificando nella riga 'Bitmap.createBitmap'. – Draiken

+0

grazie, non ho visto che – Caner

0

Quando si lavora con un sacco di bitmap assicurarsi di chiamare il riciclo() su di loro, non appena non sono necessari. Questa chiamata libererà immediatamente la memoria associata a una particolare bitmap.

Nel tuo caso se non hai bisogno della bitmap originale dopo la rotazione, quindi riciclalo. Qualcosa sulla falsariga di:

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

Bello, non lo sapevo. Il problema è che ho bisogno della bitmap originale per ruotarla e l'errore si verifica prima che possa liberarlo dalla memoria:/ – Draiken

+0

In realtà ho provato il riciclo e mostra solo ora una schermata nera invece della bitmap. Immagino che internamente sia un puntatore e non posso davvero scherzare. – Draiken

+0

Hm. Bitmap.createBitmap() dovrebbe creare Bitmap completamente nuovo, quindi liberare quello originale dovrebbe essere sicuro. In realtà io uso questo approccio molto nel mio codice e funziona abbastanza bene. Sei sicuro di riciclare la bitmap corretta e solo * dopo * l'assegnazione del riferimento alla bitmap ruotata restituita da createBitmap() ad altri riferimenti (come nel mio esempio). Potrebbe generare un errore in createBitmap() se hai già esaurito tutta la memoria. Ma se ogni volta che lo si libera correttamente con recycle() dovrebbe essere ok per le successive chiamate ... – dimsuz

6

2 metodi di ruotare una grande immagine:

  1. usare JNI, come on this post.

  2. utilizzando un file: è un modo molto lento (a seconda dell'input e del dispositivo, ma ancora molto lento), che mette l'immagine ruotata decodificata nel disco prima, invece di metterla in memoria.

codice di utilizzo di un file è qui sotto:

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. soluzione di file, penso che valga la pena il tempo di evitare tutti i problemi di memoria. –

+0

@PeterFile Penso che dovresti farlo in più fallback dal più veloce (ma più pericoloso) al più lento (ma più sicuro): prima prova usando il modo normale (usando l'heap). Se ciò non riesce (memoria insufficiente), utilizzare la soluzione JNI. Se ciò non riesce (memoria insufficiente), utilizzare la soluzione di archiviazione. Anche la soluzione di archiviazione potrebbe non riuscire (non abbastanza spazio di archiviazione), poiché il file creato è la bitmap decodificata e non compressa, ma potrebbe essere una cosa rara. –

+0

@androiddeveloper La soluzione mantiene la dimensione e la qualità dell'immagine originale? –

Problemi correlati