2011-11-28 18 views
10

Mam aplikację, która wyświetla wiele zdjęć dla użytkownika, a my widzieliśmy wiele raportów o błędach z wyjątkiem OutOfMemoryError.Obracanie obrazów na Androidzie. Czy istnieje lepszy sposób?

Co możemy obecnie zrobić to w ten sposób:

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

Oczywistym Problem polega na tym, że musimy odtworzyć bitmapy z obrazem na pamięć i obrócić matrycę, to jest dość drogie dla pamięci.

Moje pytanie jest prosta:

Czy istnieje lepszy sposób na obracanie zdjęć bez powodowania OutOfMemoryError?

+0

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

+0

Na której linii wyrzuca się wyjątek – ingsaurabh

+0

@ Drnik Więc sugerujesz, że obracam obraz animacją nawet, jeśli nie ma być animowaną rotacją? – Draiken

Odpowiedz

0

można spróbować:

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

Jak stwierdzono po dodaniu komentarza w pytaniu, błąd występuje w linii 'Bitmap.createBitmap'. – Draiken

+0

dziękuję, nie widziałem, że – Caner

0

Podczas pracy z dużą bitmap należy zadzwonić recyklingu() na nich tak szybko, jak nie są one potrzebne. To połączenie natychmiast zwolni pamięć powiązaną z konkretną bitmapą.

Jeśli nie potrzebujesz oryginalnej bitmapy po obróceniu, wróć do niej. Coś na wzór:

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

Nice, nie wiedziałem o tym. Problem polega na tym, że potrzebuję oryginalnej mapy bitowej, aby ją obrócić, a błąd wystąpi, zanim będę mógł go uwolnić z pamięci:/ – Draiken

+0

Właściwie próbowałem odtworzyć i po prostu pokazuje teraz czarny ekran zamiast bitmapy. Sądzę, że wewnętrznie to wskaźnik i nie mogę z tym sobie poradzić. – Draiken

+0

Hm. Bitmap.createBitmap() powinien stworzyć całkowicie nową bitmapę, więc uwolnienie oryginalnej wersji powinno być bezpieczne. Właściwie to używam tego podejścia w moim własnym kodzie i działa całkiem dobrze. Czy jesteś pewien, że przetwarzasz właściwą bitmapę i tylko * po * przypisaniu odniesienia do obróconej mapy bitowej zwróconej przez createBitmap() do innego odniesienia (jak w moim przykładzie). Mogłoby to spowodować błąd w createBitmap(), jeśli już zużyłeś całą pamięć. Ale jeśli za każdym razem, gdy poprawnie go zwolnisz przy pomocy funkcji recycle(), powinno być OK przy następnej inwokacji ... – dimsuz

6

2 metody obrót dużego obrazu:

  1. pomocą JNI jak on this post.

  2. użycie pliku: to bardzo powolny sposób (w zależności od wejścia i urządzenia, ale wciąż bardzo powolny), który najpierw umieszcza zdekodowany obrócony obraz na dysku zamiast wstawiania go do pamięci.

kod z użyciem pliku znajduje się poniżej:

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 Loved your rozwiązanie pliku, myślę, że warto czasu, aby uniknąć wszystkich problemów z pamięcią. –

+0

@PeterFile Myślę, że powinieneś zrobić to w wielokrotnych awariach od najszybszego (a jednocześnie najbardziej niebezpiecznego) do najlżejszego (ale najbezpieczniejszego): najpierw spróbuj używając normalnego sposobu (używając sterty). Jeśli to zawiedzie (za mało pamięci), użyj rozwiązania JNI. Jeśli to zawiedzie (za mało pamięci), użyj rozwiązania pamięci masowej. Rozwiązanie pamięci masowej może również zawieść (za mało pamięci), ponieważ tworzony plik jest dekodowaną bitmapą, a nie skompresowaną, ale może to być rzadka sytuacja. –

+0

@androiddeveloper Czy Twoje rozwiązanie zachowuje oryginalny rozmiar i jakość obrazu? –

Powiązane problemy