Android: Jak działa funkcja Bitmap recycle ()?

89

Powiedzmy, że załadowałem obraz w obiekcie bitmapowym, takim jak

Bitmap myBitmap = BitmapFactory.decodeFile(myFile);

Co się stanie, jeśli załaduję kolejną mapę bitową, np

myBitmap = BitmapFactory.decodeFile(myFile2);

Co stanie się z pierwszą myBitmapą? Czy pobiera garbage Collected, czy też muszę ręcznie zbierać śmieci przed załadowaniem kolejnej bitmapy, np. myBitmap.recycle()?

Czy istnieje lepszy sposób na ładowanie dużych obrazów i wyświetlanie ich jeden po drugim podczas recyklingu w drodze?

Anuj Tenani
źródło

Odpowiedzi:

23

Myślę, że problem jest następujący: w wersjach Androida wcześniejszych niż Honeycomb rzeczywiste surowe dane bitmapowe nie są przechowywane w pamięci maszyny wirtualnej, ale zamiast tego w pamięci natywnej. Ta pamięć natywna jest zwalniana, gdy odpowiadający jej Bitmapobiekt Java jest GC.

Jednak gdy zabraknie pamięci natywnej, dalvik GC nie jest wyzwalany, więc możliwe jest, że Twoja aplikacja zużywa bardzo mało pamięci Java, więc dalvik GC nigdy nie jest wywoływany, ale wykorzystuje mnóstwo pamięci natywnej dla map bitowych co ostatecznie powoduje błąd OOM.

Tak przynajmniej przypuszczam. Na szczęście w Honeycomb i późniejszych, wszystkie dane bitmapowe są przechowywane na maszynie wirtualnej, więc nie powinieneś w ogóle ich używać recycle(). Ale dla milionów użytkowników 2.3 (fragmentacja trzęsie się pięścią ), powinieneś używać recycle()wszędzie, gdzie to możliwe (ogromny kłopot). Alternatywnie możesz zamiast tego wywołać GC.

Timmmm
źródło
21

Będziesz musiał wywołać myBitmap.recycle () przed załadowaniem następnego obrazu.

W zależności od źródła twojego myFile (np. Jeśli jest to coś, na co nie masz kontroli nad oryginalnym rozmiarem), podczas ładowania obrazu zamiast po prostu ponownego próbkowania jakiejś dowolnej liczby, powinieneś przeskalować obraz do rozmiaru wyświetlacza.

if (myBitmap != null) {
    myBitmap.recycle();
    myBitmap = null;
}
Bitmap original = BitmapFactory.decodeFile(myFile);
myBitmap = Bitmap.createScaledBitmap(original, displayWidth, displayHeight, true);
if (original != myBitmap)
    original.recycle();
original = null;

Buforuję displayWidth i displayHeight w statycznej, którą zainicjowałem na początku mojej aktywności.

Display display = getWindowManager().getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();
djunod
źródło
3
Nie musisz wywoływać funkcji recycle (), to po prostu dobry pomysł, jeśli chcesz od razu zwolnić pamięć.
Karu
13
Zaakceptowana odpowiedź brzmi „Jeśli chcesz jak najszybciej zwolnić pamięć, powinieneś wywołać funkcję recycle ()”. Twoja odpowiedź brzmi: „Będziesz musiał zadzwonić do myBitmap.recycle ()”. Istnieje różnica między „powinno” i „trzeba”, a to drugie jest w tym przypadku niepoprawne.
Karu
1
Kontekst jest ważny. Pytanie brzmiało: „Czy jest też lepszy sposób na ładowanie dużych obrazów i wyświetlanie ich jeden po drugim w drodze do recyklingu”.
djunod
3
Począwszy od Androida 4.1, powyższy przykład może się zepsuć, ponieważ createScaledBitmap może zwrócić w niektórych przypadkach to samo wystąpienie, co oryginalne. Oznacza to, że przed recyklingiem oryginału musisz sprawdzić ten oryginał! = MyBitmap.
Jeremyfa
1
@Jeremyfa Zwraca oryginalny obraz tylko wtedy, gdy określisz szerokość i wysokość, które są identyczne z oryginalnym. W takim przypadku skalowanie jest dyskusyjne, więc równie dobrze może uratować niektóre procesy, pomijając je i zwracając zamiast tego oryginalny obraz. Nie powinno to jednak niczego "zepsuć" ...
Jabari
11

Po załadowaniu mapy bitowej do pamięci, w rzeczywistości została utworzona z dwóch części danych. Pierwsza część zawiera informacje o bitmapie, druga część zawiera informacje o pikselach bitmapy (jest to układane przez tablicę bajtów). Pierwsza część istnieje w używanej pamięci Java, druga część w używanej pamięci C ++. Mogą bezpośrednio korzystać z pamięci drugiej osoby. Bitmap.recycle () służy do zwalniania pamięci C ++. Jeśli tylko to zrobisz, GC zbierze część Java i pamięć C jest zawsze używana.

Allen
źródło
+1 za ciekawy, ale bardzo dobry sposób na opisanie, dlaczego pamięć nie jest dostępna dla natychmiastowego GC - ładny.
Richard Le Mesurier