Mam widok listy z kilkoma przyciskami obrazów w każdym rzędzie. Kliknięcie wiersza listy uruchamia nową aktywność. Musiałem zbudować własne zakładki z powodu problemu z układem aparatu. Aktywnością uruchamianą dla wyniku jest mapa. Jeśli kliknę mój przycisk, aby uruchomić podgląd obrazu (załaduj obraz z karty SD), aplikacja powróci z działania z powrotem do listview
działania do modułu obsługi wyników, aby ponownie uruchomić moją nową aktywność, która jest niczym więcej niż widżetem obrazu.
Podgląd obrazu w widoku listy odbywa się za pomocą kursora i ListAdapter
. To sprawia, że jest to dość proste, ale nie jestem pewien, jak mogę umieścić obraz o zmienionym rozmiarze (tj. Mniejszy rozmiar bitowy, a nie piksel jak src
przycisk obrazu w locie. Więc po prostu zmieniłem rozmiar obrazu, który wyszedł z aparatu telefonu.
Problem polega na tym, że pojawia się błąd braku pamięci, gdy próbuje wrócić i ponownie uruchomić 2. działanie.
- Czy istnieje sposób, w jaki mogę łatwo zbudować adapter listy rząd po rzędzie, w którym mogę zmieniać rozmiar w locie ( nieco mądrzej )?
Byłoby to preferowane, ponieważ muszę wprowadzić pewne zmiany we właściwościach widgetów / elementów w każdym rzędzie, ponieważ nie mogę wybrać wiersza z ekranem dotykowym z powodu problemu z ostrością. ( Mogę użyć kulki. )
- Wiem, że mogę wykonać zmianę rozmiaru poza pasmem i zapisać mój obraz, ale to nie jest tak naprawdę to, co chcę zrobić, ale jakiś przykładowy kod do tego byłby miły.
Gdy tylko wyłączyłem obraz w widoku listy, znów działał dobrze.
FYI: Tak to robiłem:
String[] from = new String[] { DBHelper.KEY_BUSINESSNAME,DBHelper.KEY_ADDRESS,DBHelper.KEY_CITY,DBHelper.KEY_GPSLONG,DBHelper.KEY_GPSLAT,DBHelper.KEY_IMAGEFILENAME + ""};
int[] to = new int[] {R.id.businessname,R.id.address,R.id.city,R.id.gpslong,R.id.gpslat,R.id.imagefilename };
notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);
Gdzie R.id.imagefilename
jest ButtonImage
.
Oto mój LogCat:
01-25 05:05:49.877: ERROR/dalvikvm-heap(3896): 6291456-byte external allocation too large for this process.
01-25 05:05:49.877: ERROR/(3896): VM wont let us allocate 6291456 bytes
01-25 05:05:49.877: ERROR/AndroidRuntime(3896): Uncaught handler: thread main exiting due to uncaught exception
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:304)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:149)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:174)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.drawable.Drawable.createFromPath(Drawable.java:729)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ImageView.resolveUri(ImageView.java:484)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ImageView.setImageURI(ImageView.java:281)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:183)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:129)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.CursorAdapter.getView(CursorAdapter.java:150)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.AbsListView.obtainView(AbsListView.java:1057)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ListView.makeAndAddView(ListView.java:1616)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ListView.fillSpecific(ListView.java:1177)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ListView.layoutChildren(ListView.java:1454)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.AbsListView.onLayout(AbsListView.java:937)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1108)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.onLayout(LinearLayout.java:922)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:999)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.onLayout(LinearLayout.java:920)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.ViewRoot.performTraversals(ViewRoot.java:771)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.ViewRoot.handleMessage(ViewRoot.java:1103)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.os.Handler.dispatchMessage(Handler.java:88)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.os.Looper.loop(Looper.java:123)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.app.ActivityThread.main(ActivityThread.java:3742)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at java.lang.reflect.Method.invokeNative(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at java.lang.reflect.Method.invoke(Method.java:515)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at dalvik.system.NativeStart.main(Native Method)
01-25 05:10:01.127: ERROR/AndroidRuntime(3943): ERROR: thread attach failed
Mam również nowy błąd podczas wyświetlania obrazu:
01-25 22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d
01-25 22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri:
01-25 22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process.
01-25 22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes
01-25 22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed
źródło
Odpowiedzi:
Android szkolenie klasy „ Wyświetlanie bitmapy Skutecznie ”, oferuje kilka świetnych informacji dla zrozumienia i radzenia sobie z wyjątkiem
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
podczas ładowania bitmapy.Przeczytaj wymiary i typ mapy bitowej
BitmapFactory
Klasy zapewnia kilka sposobów dekodowania (decodeByteArray()
,decodeFile()
,decodeResource()
, itd.) Do wykonywaniaBitmap
z różnych źródeł. Wybierz najbardziej odpowiednią metodę dekodowania na podstawie źródła danych obrazu. Te metody próbują przydzielić pamięć dla skonstruowanej mapy bitowej i dlatego mogą łatwo doprowadzić doOutOfMemory
wyjątku. Każdy typ metody dekodowania ma dodatkowe podpisy, które pozwalają określić opcje dekodowania za pośrednictwemBitmapFactory.Options
klasy. UstawienieinJustDecodeBounds
właściwości natrue
podczas dekodowania pozwala uniknąć przydziału pamięci, powrótnull
do obiektu bitmapy, ale ustawienieoutWidth
,outHeight
ioutMimeType
. Ta technika pozwala odczytać wymiary i typ danych obrazu przed budową (i alokacją pamięci) mapy bitowej.Aby uniknąć
java.lang.OutOfMemory
wyjątków, sprawdź wymiary bitmapy przed jej zdekodowaniem, chyba że absolutnie ufasz źródłu, że dostarczy ci przewidywanych rozmiarów danych obrazu, które wygodnie mieszczą się w dostępnej pamięci.Załaduj zmniejszoną wersję do pamięci
Teraz, gdy wymiary obrazu są znane, można ich użyć, aby zdecydować, czy pełny obraz powinien zostać załadowany do pamięci, czy zamiast tego powinna zostać załadowana wersja podpróbkowana. Oto kilka czynników, które należy wziąć pod uwagę:
Na przykład nie warto ładować do pamięci obrazu o wymiarach 1024 x 768 pikseli, jeśli ostatecznie zostanie on wyświetlony w miniaturze 128 x 96 pikseli
ImageView
.Aby nakazać dekoderowi podpróbkowanie obrazu, ładując mniejszą wersję do pamięci, ustaw
inSampleSize
natrue
swójBitmapFactory.Options
obiekt. Na przykład obraz o rozdzielczości 2048 x 1536, który jest dekodowany za pomocąinSampleSize
4, tworzy mapę bitową o wielkości około 512 x 384. Ładowanie tego do pamięci zajmuje 0,75 MB zamiast 12 MB dla pełnego obrazu (przy założeniu konfiguracji bitmapyARGB_8888
). Oto metoda obliczenia wartości wielkości próbki, która jest potęgą dwóch, na podstawie docelowej szerokości i wysokości:Aby użyć tej metody, najpierw zdekoduj z
inJustDecodeBounds
ustawionym natrue
, przekaż opcje, a następnie ponownie zdekoduj przy użyciu nowejinSampleSize
wartości iinJustDecodeBounds
ustaw nafalse
:Ta metoda ułatwia załadowanie mapy bitowej o dowolnym rozmiarze do
ImageView
wyświetlanej miniatury 100 x 100 pikseli, jak pokazano w poniższym przykładowym kodzie:Możesz wykonać podobny proces dekodowania map bitowych z innych źródeł, zastępując odpowiednią
BitmapFactory.decode*
metodę w razie potrzeby.źródło
Aby naprawić błąd OutOfMemory, powinieneś zrobić coś takiego:
Ta
inSampleSize
opcja zmniejsza zużycie pamięci.Oto kompletna metoda. Najpierw odczytuje rozmiar obrazu bez dekodowania samej treści. Następnie znajduje najlepszą
inSampleSize
wartość, powinna być potęgą 2, a na końcu obraz jest dekodowany.źródło
Wprowadziłem niewielką poprawę w kodzie Fedora. Zasadniczo robi to samo, ale bez (moim zdaniem) brzydkiej pętli while i zawsze daje potęgę dwóch. Uznanie dla Fedora za stworzenie oryginalnego rozwiązania, utknąłem, dopóki nie znalazłem jego, a potem udało mi się to zrobić :)
źródło
BitmapFactory.decodeStream()
. Czy nie musisz zapisywać odniesienia do każdego z nich, aby można je było zamknąć wfinally
bloku?Pochodzę z iOS i byłem sfrustrowany, gdy odkryłem problem z czymś tak podstawowym, jak ładowanie i wyświetlanie obrazu. W końcu wszyscy, którzy mają ten problem, starają się wyświetlać obrazy o rozsądnych rozmiarach. Tak czy inaczej, oto dwie zmiany, które rozwiązały mój problem (i sprawiły, że moja aplikacja bardzo szybko reaguje).
1) Za każdym razem, gdy to zrobisz
BitmapFactory.decodeXYZ()
, upewnij się, że przekazujesz zestawBitmapFactory.Options
zinPurgeable
ustawionym natrue
(a najlepiejinInputShareable
również z ustawionym natrue
).2) NIGDY nie używaj
Bitmap.createBitmap(width, height, Config.ARGB_8888)
. Mam na myśli NIGDY! Nigdy nie miałem tej rzeczy, aby nie podnieść błędu pamięci po kilku przejściach. Żadna ilośćrecycle()
,System.gc()
cokolwiek pomogło. Zawsze budziło wyjątek. Jedynym innym sposobem, który faktycznie działa, jest umieszczenie obrazu zastępczego w twoich plikach rysunkowych (lub innej bitmapie, którą zdekodowałeś przy użyciu kroku 1 powyżej), przeskaluj to, co chcesz, a następnie zmanipuluj uzyskaną bitmapę (na przykład przekazując ją do obszaru roboczego dla większej zabawy). Więc, co należy użyć zamiast tego jest:Bitmap.createScaledBitmap(srcBitmap, width, height, false)
. Jeśli z jakiegokolwiek powodu MUSISZ użyć metody tworzenia brutalnej siły, to przynajmniej zaliczConfig.ARGB_4444
.Jest to prawie na pewno zaoszczędzić godziny, jeśli nie dni. Wszystko, co mówi o skalowaniu obrazu itp., Tak naprawdę nie działa (chyba że rozważenie niewłaściwego rozmiaru lub zdegradowanego obrazu jest rozwiązaniem).
źródło
BitmapFactory.Options options = new BitmapFactory.Options(); options.inPurgeable = true;
iBitmap.createScaledBitmap(srcBitmap, width, height, false);
rozwiązałem mój problem z wyjątkiem braku pamięci w Androidzie 4.0.0. Dzięki stary!BitmapFactory.Options.inPurgeable
iBitmapFactory.Options.inInputShareable
są przestarzałe developer.android.com/reference/android/graphics/…To znany błąd , który nie wynika z dużych plików. Ponieważ Android buforuje tabele rysunkowe, po użyciu kilku obrazów traci pamięć. Ale znalazłem alternatywny sposób, pomijając domyślny system pamięci podręcznej Androida.
Rozwiązanie : Przenieś obrazy do folderu „zasoby” i użyj następującej funkcji, aby uzyskać BitmapDrawable:
źródło
Miałem ten sam problem i rozwiązałem go, unikając funkcji BitmapFactory.decodeStream lub decodeFile, i zamiast tego użyłem
BitmapFactory.decodeFileDescriptor
decodeFileDescriptor
wygląda na to, że wywołuje inne metody rodzime niż decodeStream / decodeFile.W każdym razie zadziałało to (zauważ, że dodałem niektóre opcje, jak niektóre powyżej), ale to nie to, co zrobiło różnicę. Najważniejsze jest wywołanie BitmapFactory.decodeFileDescriptor zamiast decodeStream lub decodeFile ):
Myślę, że jest problem z natywną funkcją używaną w decodeStream / decodeFile. Potwierdziłem, że przy użyciu decodeFileDescriptor wywoływana jest inna natywna metoda. Również to, co czytałem to „że obrazy (bitmapy) nie są przydzielane w sposób standardowy, ale poprzez wywołania Java rodzimych; przydziały są wykonywane poza wirtualnym sterty, ale są ! Wliczane nim ”
źródło
Myślę, że najlepszym sposobem na uniknięcie tego
OutOfMemoryError
jest stawienie temu czoła i zrozumienie tego.Zrobiłem aplikację, aby celowo powodować
OutOfMemoryError
i monitorować użycie pamięci.Po przeprowadzeniu wielu eksperymentów z tą aplikacją doszedłem do następujących wniosków:
Najpierw opowiem o wersjach SDK przed Honey Comb.
Bitmapa jest przechowywana w natywnej stercie, ale automatycznie zbiera śmieci, wywoływanie recycle () jest niepotrzebne.
Jeśli {rozmiar sterty maszyny wirtualnej} + {przydzielona natywna pamięć sterty}> = {limit wielkości sterty maszyny wirtualnej dla urządzenia} i próbujesz utworzyć bitmapę, zostanie wygenerowane OOM.
POUCZENIE: ROZMIAR HEAP VM jest liczony, a nie PAMIĘĆ PRZYDZIELONA VM.
Rozmiar stosu maszyny wirtualnej nigdy nie zmniejszy się po powiększeniu, nawet jeśli przydzielona pamięć maszyny wirtualnej zostanie zmniejszona.
Musisz więc utrzymywać szczytową pamięć VM tak nisko, jak to możliwe, aby nie dopuścić do zbyt dużego wzrostu sterty VM, aby zaoszczędzić dostępną pamięć dla map bitowych.
Ręczne wywołanie System.gc () nie ma znaczenia, system wywoła go najpierw, zanim spróbuje zwiększyć rozmiar sterty.
Rozmiar stosu natywnego nigdy się nie zmniejszy, ale nie jest liczony dla OOM, więc nie musisz się o to martwić.
Porozmawiajmy o SDK Starts od Honey Comb.
Mapa bitowa jest przechowywana na stercie maszyny wirtualnej, pamięć natywna nie jest liczona dla OOM.
Warunek OOM jest znacznie prostszy: {rozmiar sterty maszyny wirtualnej}> = {limit wielkości sterty maszyny wirtualnej dla urządzenia}.
Więc masz więcej dostępnej pamięci do tworzenia bitmapy z tym samym limitem wielkości sterty, OOM rzadziej zostanie wyrzucony.
Oto niektóre z moich spostrzeżeń na temat usuwania śmieci i wycieku pamięci.
Możesz to zobaczyć sam w aplikacji. Jeśli działanie wykonało zadanie AsyncTask, które nadal działało po tym, jak działanie zostało zniszczone, działanie nie będzie zbierać śmieci do czasu zakończenia zadania AsyncTask.
Wynika to z faktu, że AsyncTask jest instancją anonimowej klasy wewnętrznej i zawiera odwołanie do działania.
Wywołanie AsyncTask.cancel (true) nie zatrzyma wykonywania, jeśli zadanie zostanie zablokowane w operacji IO w wątku w tle.
Wywołania zwrotne są również anonimowymi klasami wewnętrznymi, więc jeśli statyczna instancja w twoim projekcie je przechowuje i nie zwalnia, pamięć wycieknie.
Jeśli zaplanowałeś powtarzające się lub opóźnione zadanie, na przykład Timer, i nie wywołujesz cancel () i purge () w onPause (), pamięć wyciekłaby.
źródło
private static class
w tej samej klasie. Nie będą zawierać żadnych odniesień do działania (chyba, że dasz im je oczywiście)Ostatnio widziałem wiele pytań na temat wyjątków OOM i buforowania. Przewodnik dla programistów zawiera naprawdę dobry artykuł na ten temat, ale niektórzy mają tendencję do niepowodzenia w odpowiednim wdrażaniu.
Z tego powodu napisałem przykładową aplikację, która demonstruje buforowanie w środowisku Androida. Ta implementacja nie otrzymała jeszcze OOM.
Spójrz na końcu tej odpowiedzi na link do kodu źródłowego.
Wymagania:
Funkcje:
ListView
dalej, to po prostu nie będzie pobierał między bitmapNie obejmuje to:
Przykładowy kod:
Pobrane obrazy to obrazy (75x75) z Flickr. Jednak umieść dowolne adresy URL obrazów, które chcesz przetworzyć, a aplikacja zmniejszy je, jeśli przekroczy maksimum. W tej aplikacji adresy URL są po prostu w
String
tablicy.LruCache
Ma dobry sposób na radzenie sobie z bitmapy. Jednak w tej aplikacji umieściłem instancjęLruCache
wewnątrz innej klasy pamięci podręcznej, którą utworzyłem, aby aplikacja była bardziej wykonalna.Krytyczne rzeczy Cache.java (
loadBitmap()
metoda jest najważniejsza):Nie trzeba edytować niczego w pliku Cache.java, chyba że chcesz wdrożyć buforowanie dysku.
Najważniejsze rzeczy z MainActivity.java:
getView()
dzwoni bardzo często. Zwykle nie jest dobrym pomysłem pobieranie tam zdjęć, jeśli nie wdrożyliśmy kontroli, która zapewni nam, że nie zaczniemy nieskończonej ilości wątków na wiersz. Cache.java sprawdza, czyrowObject.mBitmapUrl
już jest w zadaniu, a jeśli tak, to nie uruchomi innego. Dlatego najprawdopodobniej nie przekroczymy ograniczenia kolejki roboczej zAsyncTask
puli.Pobieranie:
Możesz pobrać kod źródłowy z https://www.dropbox.com/s/pvr9zyl811tfeem/ListViewImageCache.zip .
Ostatnie słowa:
Testowałem to od kilku tygodni, nie dostałem jeszcze żadnego wyjątku OOM. Przetestowałem to na emulatorze, na moim Nexusie One i na moim Nexusie S. Testowałem adresy URL obrazów zawierające obrazy o jakości HD. Jedynym wąskim gardłem jest to, że pobieranie zajmuje więcej czasu.
Jest tylko jeden możliwy scenariusz, w którym mogę sobie wyobrazić, że pojawi się OOM, a mianowicie, jeśli pobieramy wiele, naprawdę dużych obrazów, a zanim zostaną skalowane i umieszczone w pamięci podręcznej, jednocześnie zajmą więcej pamięci i spowodują OOM. Ale i tak nie jest to idealna sytuacja i najprawdopodobniej nie będzie możliwe rozwiązanie w bardziej wykonalny sposób.
Zgłoś błędy w komentarzach! :-)
źródło
Wykonałem następujące czynności, aby zrobić zdjęcie i zmienić jego rozmiar w locie. Mam nadzieję że to pomoże
źródło
Wydaje się, że jest to bardzo długotrwały problem, z wieloma różnymi wyjaśnieniami. Skorzystałem z dwóch najczęściej przedstawianych odpowiedzi tutaj, ale żadna z nich nie rozwiązała moich problemów z maszyną wirtualną, twierdząc, że nie stać jej na bajty na wykonanie części procesu dekodowania . Po kilku kopaniach dowiedziałem się, że prawdziwym problemem jest proces dekodowania zabierający ze sterty NATIVE .
Zobacz tutaj: BitmapFactory OOM doprowadza mnie do szału
Doprowadziło mnie to do kolejnego wątku do dyskusji, w którym znalazłem jeszcze kilka rozwiązań tego problemu. Jednym z nich jest
System.gc();
ręczne wywołanie po wyświetleniu obrazu. Ale to tak naprawdę powoduje, że Twoja aplikacja korzysta z WIĘCEJ pamięci, starając się zmniejszyć natywną stertę. Lepszym rozwiązaniem od wydania 2.0 (Donut) jest użycie opcji BitmapFactory „inPurgeable”. Więc po prostu dodałemo2.inPurgeable=true;
zarazo2.inSampleSize=scale;
.Więcej na ten temat tutaj: Czy limit sterty pamięci to tylko 6 MB?
Teraz, powiedziawszy to wszystko, jestem kompletnym dunce z Javą i Androidem. Więc jeśli uważasz, że to okropny sposób na rozwiązanie tego problemu, prawdopodobnie masz rację. ;-) Ale zadziałało to dla mnie cuda i nie mogłem teraz uruchomić maszyny wirtualnej z pamięci podręcznej sterty. Jedyną wadą, jaką mogę znaleźć, jest to, że niszczysz swój narysowany obraz w pamięci podręcznej. Co oznacza, że jeśli wrócisz PRAWO do tego obrazu, przerysujesz go za każdym razem. W przypadku tego, jak działa moja aplikacja, nie jest to tak naprawdę problemem. Twój przebieg może się różnić.
źródło
niestety, jeśli żadna z powyższych nie działa, to dodaj to do pliku manifestu . Wewnątrz tagu aplikacji
źródło
Użyj tego
bitmap.recycle();
Pomaga to bez problemu z jakością obrazu.źródło
Mam o wiele bardziej skuteczne rozwiązanie, które nie wymaga żadnego skalowania. Wystarczy zdekodować bitmapę tylko raz, a następnie buforować ją na mapie w stosunku do jej nazwy. Następnie po prostu pobierz mapę bitową dla nazwy i ustaw ją w ImageView. Nie trzeba nic więcej robić.
Działa to, ponieważ rzeczywiste dane binarne zdekodowanej mapy bitowej nie są przechowywane w stercie maszyny wirtualnej Dalvik. Jest przechowywany na zewnątrz. Dlatego za każdym razem, gdy dekodujesz bitmapę, alokuje ona pamięć poza stertą VM, która nigdy nie jest odzyskiwana przez GC
Aby lepiej to docenić, wyobraź sobie, że trzymałeś swój obraz w folderze, który można wyciągnąć. Po prostu dostajesz obraz, wykonując getResources (). GetDrwable (R.drawable.). To NIE dekoduje twojego obrazu za każdym razem, ale ponownie użyje już zdekodowanej instancji za każdym razem, gdy ją wywołasz. Zasadniczo jest on buforowany.
Ponieważ twój obraz znajduje się gdzieś w pliku (lub może nawet pochodzić z zewnętrznego serwera), Twoim obowiązkiem jest buforowanie zdekodowanej instancji bitmapy w celu ponownego użycia tam, gdzie jest to potrzebne.
Mam nadzieję że to pomoże.
źródło
Ten sam problem rozwiązałem w następujący sposób.
źródło
Są tutaj dwa problemy ....
źródło
To zadziałało dla mnie!
źródło
Żadna z powyższych odpowiedzi nie działała dla mnie, ale wpadłem na strasznie brzydkie obejście, które rozwiązało problem. Dodałem bardzo mały obraz 1 x 1 piksel do mojego projektu jako zasób i załadowałem go do ImageView przed wywołaniem odśmiecania. Myślę, że być może ImageView nie wypuściło mapy bitowej, więc GC nigdy jej nie podniosła. To brzydkie, ale na razie wydaje się, że działa.
źródło
bitmap
w powyższym kodzie znajduje się poprzednio już wyświetlany obraz, musisz go przetworzyć, wyczyścić wszelkie odniesienia do niego,imageView
zapomnij o nim również, ustawiając niewielki zamiennikgc()
; a po tym wszystkim: załaduj NOWY obrazbitmap
i wyświetl go...
w powyższym kodzie.Świetne odpowiedzi tutaj, ale chciałem, aby klasa w pełni użyteczna rozwiązała ten problem .. więc zrobiłem jedną.
Oto moja klasa BitmapHelper, która jest odporna na OutOfMemoryError :-)
źródło
To działa dla mnie.
i to jest na C # monodroid. możesz łatwo zmienić ścieżkę obrazu. ważne są tutaj opcje do ustawienia.
źródło
Wydaje się, że jest to odpowiednie miejsce do dzielenia się moją klasą narzędziową do ładowania i przetwarzania obrazów ze społecznością, możesz go używać i dowolnie modyfikować.
źródło
W jednej z moich aplikacji muszę zrobić zdjęcie
Camera/Gallery
. Jeśli użytkownik kliknie obraz z aparatu (może to być 2MP, 5MP lub 8MP), rozmiar obrazu zmienia się odkB
s doMB
s. Jeśli rozmiar obrazu jest mniejszy (lub do 1-2 MB) powyżej kodu działa dobrze, ale jeśli mam obraz powyżej 4 MB lub 5 MB, toOOM
pojawia się w ramce :(potem pracowałem nad rozwiązaniem tego problemu i na koniec wprowadziłem poniższe ulepszenie do kodu Fedora (All Credit to Fedor za stworzenie tak fajnego rozwiązania) :)
Mam nadzieję, że pomoże to kumplom zmierzyć się z tym samym problemem!
po więcej zapoznaj się z tym
źródło
Właśnie natknąłem się na ten problem kilka minut temu. Rozwiązałem to, wykonując lepszą pracę w zarządzaniu moim adapterem listview. Myślałem, że to problem z setkami zdjęć 50 x 50 pikseli, których używałem, okazuje się, że próbowałem zawyżać swój niestandardowy widok za każdym razem, gdy wyświetlany był wiersz. Po prostu testując, czy wiersz został zawyżony, wyeliminowałem ten błąd i używam setek map bitowych. To jest właściwie dla Spinnera, ale adapter podstawowy działa tak samo dla ListView. Ta prosta poprawka znacznie poprawiła również wydajność adaptera.
źródło
Cały dzień spędziłem na testowaniu tych rozwiązań, a jedyną rzeczą, która działała dla mnie, były powyższe metody uzyskiwania obrazu i ręcznego wywoływania GC, które, jak wiem, nie powinny być konieczne, ale tylko to działało kiedy poddaję moją aplikację testom pod dużym obciążeniem, przełączając się między czynnościami. Moja aplikacja ma listę miniatur w widoku listy w (powiedzmy aktywność A), a kiedy klikniesz jeden z tych obrazów, przeniesie Cię do innej aktywności (powiedzmy aktywność B), która pokazuje główny obraz dla tego elementu. Kiedy przełączałem się między tymi dwiema czynnościami, w końcu otrzymywałem błąd OOM i aplikacja wymuszała zamknięcie.
Gdy dostanę się w połowie widoku listy, nastąpi awaria.
Teraz, gdy zaimplementuję następujące działanie B, mogę bez problemu przejrzeć cały widok listy i kontynuować pracę, pracę i pracę ... i to szybko.
źródło
Ten problem występuje tylko w emulatorach Androida. Napotkałem również ten problem w emulatorze, ale kiedy sprawdziłem urządzenie, działało dobrze.
Więc proszę sprawdzić w urządzeniu. Można go uruchomić w urządzeniu.
źródło
Moje 2 centy: Rozwiązałem moje błędy OOM w bitmapach przez:
a) skalowanie moich zdjęć 2 razy
b) użycie biblioteki Picasso w moim niestandardowym adapterze dla ListView, z jednym wywołaniem w getView w następujący sposób:
Picasso.with(context).load(R.id.myImage).into(R.id.myImageView);
źródło
użyj tego kodu dla każdego obrazu wybranego z SdCard lub dreorable do konwersji obiektu bitmapowego.
użyj ścieżki obrazu instend ImageData_Path.get (img_pos) .getPath () .
źródło
Zasadniczo rozmiar sterty urządzeń z systemem Android wynosi tylko 16 MB (różni się w zależności od urządzenia / systemu operacyjnego, patrz Post Heap Sizes ), jeśli ładujesz obrazy i przekracza on rozmiar 16 MB, spowoduje to wyrzucenie wyjątku pamięci zamiast użycia mapy bitowej do ładowania obrazy z karty SD lub z zasobów, a nawet z sieci, spróbuj użyć getImageUri , ładowanie bitmapy wymaga więcej pamięci lub możesz ustawić bitmapę na zero, jeśli wykonałeś pracę z tą bitmapą.
źródło
Wszystkie rozwiązania tutaj wymagają ustawienia IMAGE_MAX_SIZE. Ogranicza to urządzenia z mocniejszym sprzętem, a jeśli rozmiar obrazu jest zbyt niski, wygląda brzydko na ekranie HD.
Wynalazłem rozwiązanie, które działa z moim Samsung Galaxy S3 i kilkoma innymi urządzeniami, w tym mniej wydajnymi, z lepszą jakością obrazu, gdy używane jest mocniejsze urządzenie.
Jego celem jest obliczenie maksymalnej pamięci przydzielonej dla aplikacji na konkretnym urządzeniu, a następnie ustawienie najniższej możliwej skali bez przekraczania tej pamięci. Oto kod:
Ustawiłem maksymalną pamięć używaną przez tę bitmapę na 25% maksymalnej przydzielonej pamięci, być może będziesz musiał dostosować ją do swoich potrzeb i upewnić się, że ta bitmapa jest wyczyszczona i nie pozostanie w pamięci po zakończeniu jej używania. Zazwyczaj używam tego kodu do obracania obrazu (bitmapa źródłowa i docelowa), więc moja aplikacja musi jednocześnie ładować 2 bitmapy do pamięci, a 25% daje mi dobry bufor bez wyczerpania pamięci podczas obracania obrazu.
Mam nadzieję, że to pomoże komuś tam ..
źródło
Taki
OutofMemoryException
nie może być całkowicie rozwiązany przez wywołanieSystem.gc()
i tak dalej.Odnosząc się do cyklu życia aktywnościStany aktywności są określane przez sam system operacyjny, z zastrzeżeniem użycia pamięci dla każdego procesu i priorytetu każdego procesu.
Możesz wziąć pod uwagę rozmiar i rozdzielczość każdego z użytych obrazów bitmapowych. Zalecam zmniejszenie rozmiaru, ponowne próbkowanie do niższej rozdzielczości, zapoznaj się z projektem galerii (jeden mały obraz PNG i jeden oryginalny obraz).
źródło
Ten kod pomoże załadować dużą mapę bitową z rysowalnej
źródło
Bitmap.Config.ARGB_565
? Jeśli wysoka jakość nie jest krytyczna.