Stworzyłem aplikację, która wykorzystuje wiele obrazów na Androida.
Aplikacja działa raz, wypełnia informacje na ekranie ( Layouts
, Listviews
, Textviews
, ImageViews
, etc) i użytkownik odczytuje informacje.
Nie ma animacji, żadnych efektów specjalnych ani niczego, co mogłoby wypełnić pamięć. Czasami przedmioty do rysowania mogą się zmienić. Niektóre z nich to zasoby Androida, a niektóre to pliki zapisane w folderze na karcie SDCARD.
Następnie użytkownik kończy pracę ( onDestroy
metoda jest wykonywana, a aplikacja pozostaje w pamięci przez maszynę wirtualną), a następnie w pewnym momencie użytkownik wchodzi ponownie.
Za każdym razem, gdy użytkownik wchodzi do aplikacji, widzę, jak pamięć rośnie coraz bardziej, aż użytkownik otrzyma plik java.lang.OutOfMemoryError
.
Jaki jest więc najlepszy / prawidłowy sposób obsługi wielu obrazów?
Czy powinienem umieścić je w metodach statycznych, aby nie były ładowane przez cały czas? Czy muszę w specjalny sposób czyścić układ lub obrazy użyte w układzie?
źródło
Odpowiedzi:
Wygląda na to, że masz wyciek pamięci. Problem nie dotyczy obsługi wielu obrazów, polega na tym, że obrazy nie są zwalniane po zniszczeniu aktywności.
Trudno powiedzieć, dlaczego tak się dzieje, nie patrząc na swój kod. Jednak ten artykuł zawiera kilka wskazówek, które mogą pomóc:
http://android-developers.blogspot.de/2009/01/avoiding-memory-leaks.html
W szczególności użycie zmiennych statycznych może pogorszyć sytuację, a nie poprawić. Może zajść potrzeba dodania kodu, który usuwa wywołania zwrotne podczas ponownego rysowania aplikacji - ale znowu nie ma tu wystarczających informacji, aby powiedzieć na pewno.
źródło
Jednym z najczęstszych błędów, które znalazłem podczas tworzenia aplikacji na Androida, jest błąd „java.lang.OutOfMemoryError: rozmiar mapy bitowej przekracza budżet maszyny wirtualnej”. Znalazłem ten błąd często w działaniach wykorzystujących wiele bitmap po zmianie orientacji: działanie jest niszczone, tworzone ponownie, a układy są „zawyżane” z pliku XML zużywającego pamięć maszyny wirtualnej dostępną dla map bitowych.
Mapy bitowe w poprzednim układzie działania nie są prawidłowo usuwane przez moduł odśmiecania pamięci, ponieważ krzyżowały się z nimi odniesienia do ich działania. Po wielu eksperymentach znalazłem całkiem dobre rozwiązanie tego problemu.
Najpierw ustaw atrybut „id” w widoku nadrzędnym układu XML:
Następnie w
onDestroy()
metodzie swojego działania wywołajunbindDrawables()
metodę przekazującą odwołanie do widoku nadrzędnego, a następnie wykonajSystem.gc()
.Ta
unbindDrawables()
metoda bada drzewo widoków rekurencyjnie i:źródło
Aby uniknąć tego problemu, możesz użyć metody natywnej
Bitmap.recycle()
przednull
-ingBitmap
obiektu (lub ustawić inną wartość). Przykład:A następnie możesz zmienić bez
myBitmap
dzwonienia naSystem.gc()
przykład:źródło
Natknąłem się dokładnie na ten problem. Sterta jest dość mała, więc te obrazy mogą dość szybko wymknąć się spod kontroli w odniesieniu do pamięci. Jednym ze sposobów jest wskazanie modułowi odśmiecania pamięci, aby zebrał pamięć na mapie bitowej, wywołując jego metodę recycle .
Ponadto wywołanie metody onDestroy nie jest gwarantowane. Możesz przenieść tę logikę / czyszczenie do działania onPause. Zapoznaj się z diagramem / tabelą cyklu życia działania na tej stronie, aby uzyskać więcej informacji.
źródło
onPause
sugestię. Używam 4 zakładek zBitmap
obrazami w każdym z nich, więc zniszczenie aktywności może nie nastąpić zbyt wcześnie (jeśli użytkownik przegląda wszystkie karty).Może pomóc to wyjaśnienie: http://code.google.com/p/android/issues/detail?id=8488#c80
„Szybkie wskazówki:
1) NIGDY nie wywołuj samodzielnie System.gc (). Zostało to propagowane jako poprawka tutaj i nie działa. Nie rób tego. Jeśli zauważyłeś w moim wyjaśnieniu, przed otrzymaniem OutOfMemoryError, JVM już uruchamia czyszczenie pamięci, więc nie ma powodu, aby robić to ponownie (spowalnia to twój program). Wykonanie jednego pod koniec zajęć to tylko zatuszowanie problemu. Może to powodować szybsze umieszczanie mapy bitowej w kolejce finalizatora, ale nie ma powodu, aby zamiast tego po prostu wywołać funkcję recycle na każdej mapie bitowej.
2) Zawsze wywołuj recycle () w przypadku map bitowych, których już nie potrzebujesz. Przynajmniej w onDestroy swojej aktywności przejrzyj i poddaj recyklingowi wszystkie używane mapy bitowe. Ponadto, jeśli chcesz, aby instancje bitmapy były szybciej zbierane ze sterty dalvik, wyczyszczenie jakichkolwiek odniesień do mapy bitowej nie zaszkodzi.
3) Wywołanie recycle (), a następnie System.gc () nadal może nie usunąć mapy bitowej ze stosu Dalvik. NIE PRZESTAWAJ SIĘ tym. recycle () wykonał swoją pracę i zwolnił natywną pamięć, po prostu zajmie trochę czasu, aby przejść przez kroki, które nakreśliłem wcześniej, aby faktycznie usunąć mapę bitową ze stosu Dalvik. To NIE jest wielka sprawa, ponieważ duża część pamięci natywnej jest już wolna!
4) Zawsze zakładaj, że błąd we frameworku jest ostatni. Dalvik robi dokładnie to, co powinien. Może nie być tym, czego oczekujesz lub czego chcesz, ale tak to działa. "
źródło
Miałem dokładnie ten sam problem. Po kilku testach stwierdziłem, że ten błąd pojawia się przy skalowaniu dużego obrazu. Zmniejszyłem skalowanie obrazu i problem zniknął.
PS Na początku próbowałem zmniejszyć rozmiar obrazu bez zmniejszania go. To nie powstrzymało błędu.
źródło
Poniższe punkty bardzo mi pomogły. Mogą być też inne punkty, ale są one bardzo ważne:
źródło
Proponuję wygodny sposób rozwiązania tego problemu. Po prostu przypisz wartość atrybutu „android: configChanges”, jak następuje w pliku Mainfest.xml dla błędnej aktywności. lubię to:
pierwsze rozwiązanie, które podałem, naprawdę zmniejszyło częstotliwość błędów OOM do niskiego poziomu. Ale to nie rozwiązało całkowicie problemu. A potem podam drugie rozwiązanie:
Jak podano w OOM, zużyłem zbyt dużo pamięci wykonawczej. Więc zmniejszam rozmiar obrazu w ~ / res / drawable mojego projektu. Na przykład przekwalifikowany obraz, który ma rozdzielczość 128X128, może zostać przeskalowany do 64x64, co również byłoby odpowiednie dla mojej aplikacji. A po tym, jak zrobiłem to ze stosem zdjęć, błąd OOM nie występuje ponownie.
źródło
Ja też jestem sfrustrowany błędem braku pamięci. I tak, ja też odkryłem, że ten błąd często pojawia się podczas skalowania obrazów. Na początku próbowałem tworzyć rozmiary obrazów dla wszystkich gęstości, ale stwierdziłem, że znacznie zwiększyło to rozmiar mojej aplikacji. Więc teraz używam tylko jednego obrazu dla wszystkich gęstości i skaluję moje obrazy.
Moja aplikacja wyrzucała błąd poza pamięcią za każdym razem, gdy użytkownik przechodził z jednej czynności do drugiej. Ustawienie moich drawables na null i wywołanie System.gc () nie działało, podobnie jak recykling moich obiektów bitmapDrawables za pomocą getBitMap (). Recycle (). Android nadal wyrzucałby błąd poza pamięcią przy pierwszym podejściu i wyrzucałby komunikat o błędzie kanwy za każdym razem, gdy próbowałby użyć odzyskanej mapy bitowej w drugim podejściu.
Podjąłem jeszcze trzecie podejście. Ustawiłem wszystkie widoki na zero, a tło na czarne. Robię to czyszczenie w mojej metodzie onStop (). Jest to metoda, która jest wywoływana, gdy działanie nie jest już widoczne. Jeśli zrobisz to w metodzie onPause (), użytkownicy zobaczą czarne tło. Nieidealny. Jeśli chodzi o zrobienie tego w metodzie onDestroy (), nie ma gwarancji, że zostanie wywołana.
Aby zapobiec pojawieniu się czarnego ekranu, jeśli użytkownik naciśnie przycisk Wstecz na urządzeniu, ponownie ładuję aktywność w metodzie onRestart (), wywołując metody startActivity (getIntent ()), a następnie finish ().
Uwaga: zmiana tła na czarne nie jest naprawdę konieczna.
źródło
Metody BitmapFactory.decode *, omówione w lekcji Skutecznie ładuj duże mapy bitowe , nie powinny być wykonywane w głównym wątku interfejsu użytkownika, jeśli dane źródłowe są odczytywane z dysku lub lokalizacji sieciowej (lub w rzeczywistości dowolnego źródła innego niż pamięć). Czas ładowania tych danych jest nieprzewidywalny i zależy od wielu czynników (szybkość odczytu z dysku lub sieci, rozmiar obrazu, moc procesora itp.). Jeśli jedno z tych zadań blokuje wątek interfejsu użytkownika, system oznacza aplikację jako niereagującą, a użytkownik ma możliwość jej zamknięcia (więcej informacji można znaleźć w sekcji Projektowanie pod kątem responsywności).
źródło
Cóż, wypróbowałem wszystko, co znalazłem w Internecie i żaden z nich nie działał. Wywołanie System.gc () tylko zmniejsza prędkość aplikacji. Recykling bitmap w onDestroy też nie działał.
Jedyne, co teraz działa, to mieć statyczną listę wszystkich bitmap, aby mapy bitowe przetrwały po ponownym uruchomieniu. I po prostu użyj zapisanych bitmap zamiast tworzyć nowe za każdym razem, gdy działanie zostanie ponownie uruchomione.
W moim przypadku kod wygląda tak:
źródło
Miałem ten sam problem z przełączaniem obrazów tła na rozsądne rozmiary. Osiągnąłem lepsze wyniki, ustawiając ImageView na null przed umieszczeniem nowego obrazu.
źródło
FWIW, oto lekka pamięć podręczna bitmap, którą zakodowałem i używam przez kilka miesięcy. To nie są wszystkie dzwonki i gwizdki, więc przeczytaj kod, zanim go użyjesz.
źródło