Dziwny problem braku pamięci podczas ładowania obrazu do obiektu bitmapowego

Mam widok listy z kilkoma przyciskami obrazu w każdym wierszu. Po kliknięciu wiersza listy uruchamia się nowa aktywność. Musiałem zbudować własne karty z powodu problemu z układem kamery. Aktywność, która zostanie uruchomiona dla wyniku, to mapa. Jeśli kliknę na mój przycisk, aby uruchomić podgląd obrazu (załadować obraz z karty SD), aplikacja powróci z aktywności z powrotem do aktywności listview do funkcji obsługi wyniku, aby ponownie uruchomić moją nową aktywność, która jest niczym więcej niż obrazem widget.

Podgląd obrazu w widoku listy jest wykonywany 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 bitu, a nie piksel jako src dla przycisku obrazu w locie. Zmieniłem Rozmiar obrazu z aparatu.

Problem polega na tym, że dostaję błąd z pamięci, gdy próbuje wrócić i ponownie uruchomić drugą aktywność.

  • czy jest sposób na łatwe zbudowanie adaptera listy wiersz po wierszu, gdzie mogę zmienić rozmiar w locie (bit wise )?

Byłoby to korzystne, ponieważ muszę również wprowadzić pewne zmiany we właściwościach widżetów / elementów w każdym wierszu, ponieważ nie jestem w stanie wybrać wiersz z Ekranem dotykowym z powodu problemu z ostrością. (mogę użyć roller ball.)

  • wiem, że mogę zrobić poza pasmem zmianę rozmiaru i zapisanie mojego obrazu, ale nie jest to naprawdę to, co chcę zrobić, ale jakiś przykładowy kod do tego byłby miły.

Jako jak tylko wyłączyłem obraz w widoku listy to działało dobrze ponownie.

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
Author: Chrispix, 2009-01-25

30 answers

The Android Training class, " displaying bitmaps Efficiently", oferuje kilka świetnych informacji do zrozumienia i radzenia sobie z wyjątkiem java.lang.OutOfMemoryError: bitmap size exceeds VM budget podczas ładowania Bitmap.


Odczyt wymiarów i typu bitmapy

Klasa BitmapFactory zapewnia kilka metod dekodowania (decodeByteArray(), decodeFile(), decodeResource(), itd.) do tworzenia Bitmap z różnych źródeł. Wybierz najbardziej odpowiednią metodę dekodowania na podstawie źródła danych obrazu. Metody te próbują przydzielić pamięć dla zbudowanej bitmapy i dlatego może łatwo spowodować wyjątek OutOfMemory. Każdy typ metody decode posiada dodatkowe sygnatury, które pozwalają określić opcje dekodowania za pomocą klasy BitmapFactory.Options. Ustawienie właściwości inJustDecodeBounds na true podczas dekodowania unika się alokacji pamięci, zwracając null dla obiektu bitmapowego, ale ustawienie outWidth, outHeight i outMimeType. Technika ta pozwala odczytać wymiary i typ danych obrazu przed budową (i alokacją pamięci) bitmap.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

Aby uniknąć WYJĄTKÓW, sprawdź wymiary bitmapy przed jej dekodowaniem, chyba że bezwzględnie ufasz źródłu, które dostarczy Ci przewidywalnych rozmiarów danych obrazu, które wygodnie mieszczą się w dostępnej pamięci.


Załaduj skalowaną wersję do pamięci

Teraz, gdy wymiary obrazu są już znane, można ich użyć do określenia, czy pełny obraz powinien zostać załadowany do pamięci,czy zamiast niego powinna zostać załadowana wersja próbkowana. Oto niektóre czynniki do rozważenia:

  • szacowane zużycie pamięci podczas ładowania pełnego obrazu w pamięci.
  • ilość pamięci, którą chcesz zobowiązać się do załadowania tego obrazu, biorąc pod uwagę inne wymagania dotyczące pamięci twojej aplikacji.
  • Wymiary docelowego komponentu ImageView lub interfejsu użytkownika, do którego ma zostać załadowany obraz.
  • Rozmiar ekranu i gęstość aktualnego urządzenia.

Na przykład, nie warto ładować obrazu 1024x768 pikseli do pamięci, jeśli zostanie wyświetlona w miniaturze 128x96 pikseli w ImageView.

Aby powiedzieć dekoderowi, aby podsamplował obraz, ładując mniejszą wersję do pamięci, Ustaw inSampleSize na true w obiekcie BitmapFactory.Options. Na przykład obraz o Rozdzielczości 2048x1536, który jest dekodowany inSampleSize 4, tworzy bitmapę o rozdzielczości około 512x384. Wczytywanie tego do pamięci używa 0,75 MB zamiast 12 MB dla pełnego obrazu(zakładając konfigurację bitmapy ARGB_8888). Oto metoda obliczania próbki wartość rozmiaru, która jest potęgą dwóch na podstawie docelowej szerokości i wysokości:

public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

Uwaga : moc dwóch wartości jest obliczana, ponieważ dekoder używa wartość końcowa przez zaokrąglenie w dół do najbliższej potęgi dwóch, zgodnie z inSampleSize dokumentacja.

Aby użyć tej metody, najpierw dekoduj z inJustDecodeBounds ustawioną na true, przeprowadź opcje, a następnie ponownie dekoduj używając nowej wartości inSampleSize i inJustDecodeBounds ustawionej na false:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
    int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

Ta metoda sprawia, że w ten sposób można łatwo załadować bitmapę o arbitralnie dużym rozmiarze do ImageView, która wyświetla miniaturę 100x100 pikseli, jak pokazano w poniższym przykładowym kodzie:

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

Możesz wykonać podobny proces dekodowania bitmap z innych źródeł, zastępując odpowiednią metodę BitmapFactory.decode* w razie potrzeby.

 572
Author: Sazid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-11 00:58:18

Aby naprawić błąd OutOfMemory, powinieneś zrobić coś takiego:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap = BitmapFactory.decodeStream(is, null, options);

Ta opcja inSampleSize zmniejsza zużycie pamięci.

Oto pełna metoda. Najpierw odczytuje Rozmiar obrazu bez dekodowania samej zawartości. Następnie znajduje najlepszą wartość inSampleSize, powinna mieć moc 2, a na koniec obraz jest dekodowany.
// Decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
    try {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f), null, o);

        // The new size we want to scale to
        final int REQUIRED_SIZE=70;

        // Find the correct scale value. It should be the power of 2.
        int scale = 1;
        while(o.outWidth / scale / 2 >= REQUIRED_SIZE && 
              o.outHeight / scale / 2 >= REQUIRED_SIZE) {
            scale *= 2;
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {}
    return null;
}
 865
Author: Fedor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-28 21:54:24

Dokonałem niewielkiej poprawy kodu Fedora. Zasadniczo robi to samo, ale bez (moim zdaniem) brzydkiej pętli while I zawsze skutkuje mocą dwóch. Chwała Fedorowi za zrobienie oryginalnego rozwiązania, utknąłem, dopóki nie znalazłem jego, a potem udało mi się zrobić to:)

 private Bitmap decodeFile(File f){
    Bitmap b = null;

        //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;

    FileInputStream fis = new FileInputStream(f);
    BitmapFactory.decodeStream(fis, null, o);
    fis.close();

    int scale = 1;
    if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
        scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / 
           (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
    }

    //Decode with inSampleSize
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize = scale;
    fis = new FileInputStream(f);
    b = BitmapFactory.decodeStream(fis, null, o2);
    fis.close();

    return b;
}
 356
Author: Thomas Vervest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-20 06:18:29

Pochodzę z doświadczenia iOS i byłem sfrustrowany odkryciem problemu z czymś tak podstawowym, jak ładowanie i wyświetlanie obrazu. W końcu każdy, kto ma ten problem, stara się wyświetlać obrazy o rozsądnych rozmiarach. W każdym razie, oto dwie zmiany, które naprawiły mój problem (i sprawiły, że moja aplikacja była bardzo responsywna).

1) za każdym razem, gdy robisz BitmapFactory.decodeXYZ(), upewnij się, że pasujesz BitmapFactory.Options z inPurgeable ustawionym na true (a najlepiej z inInputShareable również ustawionym na true).

2) nigdy nie używaj Bitmap.createBitmap(width, height, Config.ARGB_8888). To znaczy Nigdy! Nigdy nie miałem tego czegoś, co nie podnosi błędu pamięci po kilku przejściach. Brak recycle(), System.gc(), cokolwiek pomogło. To zawsze powodowało wyjątek. Innym sposobem, który faktycznie działa, jest posiadanie atrapy obrazu w tablicach rysunkowych (lub innej bitmapy, którą zdekodowałeś za pomocą kroku 1 powyżej), przeskaluj ją do czego chcesz ,a następnie manipuluj wynikową bitmapą (np. przekazując ją na płótno dla większej zabawy). Więc zamiast tego powinieneś użyć: Bitmap.createScaledBitmap(srcBitmap, width, height, false). Jeśli z jakiegoś powodu musisz użyć brutala Wymuś metodę create, a następnie przynajmniej przepuść Config.ARGB_4444.

Jest to prawie gwarantowane, aby zaoszczędzić godziny, jeśli nie dni. Wszystko to mówi o skalowaniu obrazu itp. tak naprawdę nie działa (chyba że rozważasz uzyskanie niewłaściwego rozmiaru lub zdegradowanego obrazu rozwiązaniem).

 222
Author: Ephraim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-02 12:03:47

Jest to znany błąd , nie jest to spowodowane dużymi plikami. Ponieważ Android buforuje obiekty 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:

public static Drawable getAssetImage(Context context, String filename) throws IOException {
    AssetManager assets = context.getResources().getAssets();
    InputStream buffer = new BufferedInputStream((assets.open("drawable/" + filename + ".png")));
    Bitmap bitmap = BitmapFactory.decodeStream(buffer);
    return new BitmapDrawable(context.getResources(), bitmap);
}
 85
Author: Anto Binish Kaspar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-15 10:05:32

Miałem ten sam problem i rozwiązałem go, unikając BitmapFactory.funkcje decodeStream lub decodeFile i zamiast tego używane BitmapFactory.decodeFileDescriptor

decodeFileDescriptor wygląda na to, że wywołuje inne natywne metody niż decodeStream/decodeFile.

W każdym razie, to, co działało, było to (zauważ, że dodałem kilka opcji, jak niektóre miały powyżej, ale to nie jest to, co robi różnicę. Kluczowe jest wywołanie BitmapFactory.decodeFileDescriptor zamiast decodeStream lub decodeFile):

private void showImage(String path)   {
    Log.i("showImage","loading:"+path);
    BitmapFactory.Options bfOptions=new BitmapFactory.Options();
    bfOptions.inDither=false;                     //Disable Dithering mode
    bfOptions.inPurgeable=true;                   //Tell to gc that whether it needs free memory, the Bitmap can be cleared
    bfOptions.inInputShareable=true;              //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
    bfOptions.inTempStorage=new byte[32 * 1024]; 


    File file=new File(path);
    FileInputStream fs=null;
    try {
        fs = new FileInputStream(file);
    } catch (FileNotFoundException e) {
        //TODO do something intelligent
        e.printStackTrace();
    }

    try {
        if(fs!=null) bm=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
    } catch (IOException e) {
        //TODO do something intelligent
        e.printStackTrace();
    } finally{ 
        if(fs!=null) {
            try {
                fs.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    //bm=BitmapFactory.decodeFile(path, bfOptions); This one causes error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget

    im.setImageBitmap(bm);
    //bm.recycle();
    bm=null;



}

Myślę, że jest problem z natywną funkcją używaną w decodeStream/decodeFile. Potwierdziłem, że podczas używania decodeFileDescriptor wywoływana jest inna natywna metoda. Również to, co przeczytałem, to "że obrazy (bitmapy) nie są przydzielane w standardowy sposób Java, ale poprzez natywne wywołania; alokacje są wykonywane poza wirtualną stertą, ale są / align = "left" / "

 70
Author: Fraggle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-31 08:33:36

Myślę, że najlepszym sposobem na uniknięcie OutOfMemoryError jest stawienie temu czoła i zrozumienie.

Zrobiłem aplikację aby celowo wywołać OutOfMemoryError i monitorować zużycie pamięci.

Po wielu eksperymentach z tą aplikacją, mam następujące wnioski:

Będę mówić o wersjach SDK przed Honey Comb pierwszy.

  1. Bitmap jest przechowywana w natywnej stercie, ale automatycznie pobiera śmieci, wywołanie recycle() jest niepotrzebne.

  2. Jeśli {VM heap size} + {alloced native heap memory} > = {VM heap size limit for the device}, a ty próbujesz utworzyć bitmapę, OOM zostanie wyrzucony.

    Uwaga: rozmiar sterty maszyny Wirtualnej jest liczony zamiast przydzielonej pamięci.

  3. Rozmiar sterty maszyny wirtualnej nigdy nie zmniejszy się po powiększeniu, nawet jeśli przydzielona pamięć maszyny wirtualnej zostanie zmniejszona.

  4. Tak więc musisz utrzymać szczytową pamięć VM na jak najniższym poziomie, aby rozmiar sterty maszyn wirtualnych nie był zbyt duży, aby zapisać dostępną pamięć Bitmapy.

  5. Ręczne Wywołanie Systemu.gc () jest bez znaczenia, system wywoła ją najpierw, zanim spróbuje zwiększyć rozmiar sterty.

  6. Natywny rozmiar sterty nigdy się nie zmniejszy, ale nie jest liczony dla OOM, więc nie musisz się tym martwić.

Następnie, porozmawiajmy o SDK zaczyna się od Honey Comb.

  1. Bitmap jest przechowywany w VM heap, pamięć natywna nie jest liczona dla OOM.

  2. Warunek OOM jest znacznie prostszy: {VM heap rozmiar} > = {limit rozmiaru sterty VM dla urządzenia}.

  3. Więc masz więcej dostępnej pamięci do tworzenia bitmapy z tym samym limitem rozmiaru sterty, OOM jest mniej prawdopodobne, aby zostać wyrzuconym.

Oto niektóre z moich obserwacji na temat zbierania śmieci i wycieku pamięci.

Możesz zobaczyć to samodzielnie w aplikacji. Jeśli działanie wykonało zadanie asynchroniczne, które nadal było uruchomione po zniszczeniu zadania, działanie nie zostanie pobrane do momentu wykonania zadania asynchronicznego skończ.

Dzieje się tak dlatego, że AsyncTask jest instancją anonimowej klasy wewnętrznej, posiada odniesienie do aktywności.

Wywołanie AsyncTask.cancel (true) nie zatrzyma wykonania, jeśli zadanie jest 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ęć zostanie wycieknięta.

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ęć zostanie wycieknięta.

 65
Author: coocood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-09 12:45:12

Widziałem ostatnio wiele pytań dotyczących WYJĄTKÓW OOM i buforowania. Poradnik dla programistów zawiera naprawdę dobry artykuł na ten temat, ale niektóre z nich mają tendencję do niepowodzenia we wdrażaniu go w odpowiedni sposób.

Z tego powodu napisałem przykładową aplikację, która demonstruje buforowanie w środowisku Android. Ta implementacja nie otrzymała jeszcze OOM.

Spójrz na koniec tej odpowiedzi, aby znaleźć link do kodu źródłowego.

Wymagania:

  • Android API 2.1 lub nowsze (po prostu nie udało mi się uzyskać dostępnej pamięci dla aplikacji w API 1.6 - jest to jedyny fragment kodu, który nie działa w API 1.6) {]}
  • Android support package

Zrzut ekranu

Cechy:

  • W przypadku zmiany orientacji, użycie singletonu
  • użyj jednej ósmej przypisanej pamięci podręcznej aplikacji (zmodyfikuj, jeśli chcesz)
  • duży bitmapy są skalowane (możesz zdefiniować maksymalne piksele, na które chcesz zezwolić)
  • kontroluje czy jest dostępne połączenie z Internetem przed pobraniem bitmap
  • upewnia się, że tworzysz tylko jedno zadanie W wierszu
  • Jeśli nie chcesz pobierać bitmap pomiędzy
  • , to nie pobierasz bitmap pomiędzy

Nie dotyczy to:

  • buforowanie dysku. Powinno to być łatwe do implementacja w każdym razie-po prostu wskaż inne zadanie, które chwyta bitmapy z dysku

Przykładowy kod:

Pobierane obrazy to obrazy (75x75) z Flickr. Umieść jednak adresy URL obrazów, które chcesz przetworzyć, a aplikacja skaluje je w dół, jeśli przekroczy maksimum. W tej aplikacji adresy URL są po prostu w tablicy String.

The LruCache ma dobry sposób na radzenie sobie z bitmapami. Jednak w tej aplikacji umieściłem instancja LruCache wewnątrz innej klasy pamięci podręcznej, którą stworzyłem, aby aplikacja była bardziej wykonalna.

Cache.kluczowe rzeczy Javy (metoda loadBitmap() Jest Najważniejsza):

public Cache(int size, int maxWidth, int maxHeight) {
    // Into the constructor you add the maximum pixels
    // that you want to allow in order to not scale images.
    mMaxWidth = maxWidth;
    mMaxHeight = maxHeight;

    mBitmapCache = new LruCache<String, Bitmap>(size) {
        protected int sizeOf(String key, Bitmap b) {
            // Assuming that one pixel contains four bytes.
            return b.getHeight() * b.getWidth() * 4;
        }
    };

    mCurrentTasks = new ArrayList<String>();    
}

/**
 * Gets a bitmap from cache. 
 * If it is not in cache, this method will:
 * 
 * 1: check if the bitmap url is currently being processed in the
 * BitmapLoaderTask and cancel if it is already in a task (a control to see
 * if it's inside the currentTasks list).
 * 
 * 2: check if an internet connection is available and continue if so.
 * 
 * 3: download the bitmap, scale the bitmap if necessary and put it into
 * the memory cache.
 * 
 * 4: Remove the bitmap url from the currentTasks list.
 * 
 * 5: Notify the ListAdapter.
 * 
 * @param mainActivity - Reference to activity object, in order to
 * call notifyDataSetChanged() on the ListAdapter.
 * @param imageKey - The bitmap url (will be the key).
 * @param imageView - The ImageView that should get an
 * available bitmap or a placeholder image.
 * @param isScrolling - If set to true, we skip executing more tasks since
 * the user probably has flinged away the view.
 */
public void loadBitmap(MainActivity mainActivity, 
        String imageKey, ImageView imageView,
        boolean isScrolling) {
    final Bitmap bitmap = getBitmapFromCache(imageKey); 

    if (bitmap != null) {
        imageView.setImageBitmap(bitmap);
    } else {
        imageView.setImageResource(R.drawable.ic_launcher);
        if (!isScrolling && !mCurrentTasks.contains(imageKey) && 
                mainActivity.internetIsAvailable()) {
            BitmapLoaderTask task = new BitmapLoaderTask(imageKey,
                    mainActivity.getAdapter());
            task.execute();
        }
    } 
}

Nie powinieneś edytować niczego w pamięci podręcznej.plik java, chyba że chcesz zaimplementować buforowanie dysku.

Główna aktywność.Java ' s critical stuff:

public void onScrollStateChanged(AbsListView view, int scrollState) {
    if (view.getId() == android.R.id.list) {
        // Set scrolling to true only if the user has flinged the       
        // ListView away, hence we skip downloading a series
        // of unnecessary bitmaps that the user probably
        // just want to skip anyways. If we scroll slowly it
        // will still download bitmaps - that means
        // that the application won't wait for the user
        // to lift its finger off the screen in order to
        // download.
        if (scrollState == SCROLL_STATE_FLING) {
            mIsScrolling = true;
        } else {
            mIsScrolling = false;
            mListAdapter.notifyDataSetChanged();
        }
    } 
}

// Inside ListAdapter...
@Override
public View getView(final int position, View convertView, ViewGroup parent) {           
    View row = convertView;
    final ViewHolder holder;

    if (row == null) {
        LayoutInflater inflater = getLayoutInflater();
        row = inflater.inflate(R.layout.main_listview_row, parent, false);  
        holder = new ViewHolder(row);
        row.setTag(holder);
    } else {
        holder = (ViewHolder) row.getTag();
    }   

    final Row rowObject = getItem(position);

    // Look at the loadBitmap() method description...
    holder.mTextView.setText(rowObject.mText);      
    mCache.loadBitmap(MainActivity.this,
            rowObject.mBitmapUrl, holder.mImageView,
            mIsScrolling);  

    return row;
}

getView() często dzwoni. Zwykle nie jest to dobry pomysł, aby pobrać tam obrazy, jeśli nie zaimplementowaliśmy upewnij się, że nie uruchomimy nieskończonej ilości wątków na wiersz. Cache.java sprawdza, czy rowObject.mBitmapUrl jest już w zadaniu, a jeśli tak, to nie uruchomi kolejnego. W związku z tym najprawdopodobniej nie przekroczymy ograniczenia kolejki roboczej z puli AsyncTask.

Pobierz:

Możesz pobrać kod źródłowy z https://www.dropbox.com/s/pvr9zyl811tfeem/ListViewImageCache.zip .


Ostatnie słowa:

Testowałem to przez kilka tygodni nie dostałem jeszcze żadnego wyjątku. Przetestowałem to na emulatorze, na moim Nexus One i na moim Nexus S. przetestowałem adresy URL obrazów, które zawierają obrazy, które były w 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 OOM pojawi się, a to jest, jeśli pobierzemy 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 to i tak nie jest nawet idealna sytuacja i najprawdopodobniej nie będzie możliwe rozwiązanie w bardziej wykonalny sposób.

Zgłoś błędy w komentarzach! :-)

 59
Author: Wroclai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-01 20:25:26

Wykonałem następujące czynności, aby zrobić zdjęcie i zmienić jego rozmiar w locie. Mam nadzieję, że to pomoże

Bitmap bm;
bm = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(filepath), 100, 100, true);
mPicture = new ImageView(context);
mPicture.setImageBitmap(bm);    
 38
Author: Chrispix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-24 20:31:37

Niestety Jeśli żadne z powyższych nie działa, dodaj to do swojego pliku Manifest . Inside application tag

 <application
         android:largeHeap="true"
 32
Author: Himanshu Mori,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-22 12:18:30

Wydaje się, że jest to bardzo długi problem, z wieloma różnymi wyjaśnieniami. Skorzystałem z rad dwóch najczęściej prezentowanych tutaj odpowiedzi, ale żadna z nich nie rozwiązała moich problemów z maszyną wirtualną, twierdząc, że nie stać go na bajty do wykonania dekodowania części procesu. Po pewnym kopaniu dowiedziałem się, że prawdziwym problemem jest tutaj proces dekodowania odbierający natywny sterta.

Zobacz tutaj: BitmapFactory OOM driving me nuts

To doprowadziło mnie do kolejnego wątku Dyskusyjnego, w którym znalazłem jeszcze kilka rozwiązań tego problemu. Jednym z nich jest ręczne wywołanieSystem.gc(); po wyświetleniu obrazu. Ale to sprawia, że aplikacja zużywa więcej pamięci, w celu zmniejszenia natywnej sterty. Lepszym rozwiązaniem od wydania 2.0 (Donut) jest użycie opcji BitmapFactory "inpureable". Więc po prostu dodałem o2.inPurgeable=true; tuż po o2.inSampleSize=scale;.

Więcej na ten temat tutaj: czy limit pamięci to tylko 6M?

Teraz, mówiąc to wszystko, jestem kompletnym głupkiem z Javą i Androidem. Więc jeśli uważasz, że jest to straszny sposób na rozwiązanie tego problemu, prawdopodobnie masz rację. ;- ) Ale to działa cuda dla mnie, i znalazłem to niemożliwe, aby uruchomić maszynę wirtualną z pamięci podręcznej sterty teraz. Jedyną wadą, jaką mogę znaleźć, jest to, że niszczysz swój buforowany narysowany obraz. Co oznacza, że jeśli wrócisz do tego obrazu, będziesz go przerysowywał za każdym razem. W przypadku jak mój aplikacja działa, to nie jest naprawdę problem. Przebieg może się różnić.
 31
Author: RayHaque,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:38

Użyj tego bitmap.recycle(); to pomaga bez żadnych problemów z jakością obrazu.

 29
Author: Arsalan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-21 19:29:20

Mam znacznie skuteczniejsze rozwiązanie, które nie wymaga skalowania. Po prostu dekoduj bitmapę tylko raz, a następnie buforuj ją na mapie zgodnie z jej nazwą. Następnie po prostu pobierz bitmapę z nazwą i ustaw ją w widoku ImageView. Nic więcej nie trzeba robić.

To zadziała, ponieważ rzeczywiste dane binarne dekodowanej bitmapy nie są przechowywane w stercie maszyn wirtualnych dalvik. Jest przechowywany zewnętrznie. Więc za każdym razem, gdy dekodujesz bitmapę, przydziela ona pamięć poza z VM heap, który nigdy nie jest odzyskiwany przez GC

Aby pomóc ci lepiej to docenić, wyobraź sobie, że przechowałeś obraz ur w folderze drawable. Po prostu uzyskasz obraz wykonując getResources ().getDrwable (R. drawable.). Nie będzie to dekodować obrazu za każdym razem, ale ponownie użyć już zdekodowanego wystąpienia za każdym razem, gdy go wywołasz. Tak więc w istocie jest on buforowany.

Teraz, ponieważ twój obraz jest gdzieś w pliku (lub może nawet pochodzić z zewnętrznego serwera), Twoim obowiązkiem jest buforowanie zdekodowana instancja bitmapowa do ponownego użycia wszędzie tam, gdzie jest to potrzebne.

Mam nadzieję, że to pomoże.

 25
Author: Parth Mehta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-02 18:17:45

Rozwiązałem ten sam problem w następujący sposób.

Bitmap b = null;
Drawable d;
ImageView i = new ImageView(mContext);
try {
    b = Bitmap.createBitmap(320,424,Bitmap.Config.RGB_565);
    b.eraseColor(0xFFFFFFFF);
    Rect r = new Rect(0, 0,320 , 424);
    Canvas c = new Canvas(b);
    Paint p = new Paint();
    p.setColor(0xFFC0C0C0);
    c.drawRect(r, p);
    d = mContext.getResources().getDrawable(mImageIds[position]);
    d.setBounds(r);
    d.draw(c);

    /*   
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inTempStorage = new byte[128*1024];
        b = BitmapFactory.decodeStream(mContext.getResources().openRawResource(mImageIds[position]), null, o2);
        o2.inSampleSize=16;
        o2.inPurgeable = true;
    */
} catch (Exception e) {

}
i.setImageBitmap(b);
 25
Author: Prerna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-24 20:34:42

Są tu dwie kwestie....

    Pamięć Bitmap nie znajduje się w stercie maszyny wirtualnej, ale raczej w stercie natywnej-Zobacz BitmapFactory OOM doprowadza mnie do szału
  • Garbage collecting for the native heap is lazier than the VM heap - więc musisz być dość agresywny w robieniu bitmap.recycle and bitmap =null every time you going through an Activity ' s onPause or onDestroy
 24
Author: Torid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:34

To zadziałało na mnie!

public Bitmap readAssetsBitmap(String filename) throws IOException {
    try {
        BitmapFactory.Options options = new BitmapFactory.Options(); 
        options.inPurgeable = true;
        Bitmap bitmap = BitmapFactory.decodeStream(assets.open(filename), null, options);
        if(bitmap == null) {
            throw new IOException("File cannot be opened: It's value is null");
        } else {
            return bitmap;
        }
    } catch (IOException e) {
        throw new IOException("File cannot be opened: " + e.getMessage());
    }
}
 24
Author: Luke Taylor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-24 20:32:58

Świetne odpowiedzi tutaj, ale chciałem w pełni użyteczna klasa aby rozwiązać ten problem.. więc zrobiłem jeden.

Oto moja Klasa BitmapHelper , która jest OutOfMemoryError proof : -)

import java.io.File;
import java.io.FileInputStream;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

public class BitmapHelper
{

    //decodes image and scales it to reduce memory consumption
    public static Bitmap decodeFile(File bitmapFile, int requiredWidth, int requiredHeight, boolean quickAndDirty)
    {
        try
        {
            //Decode image size
            BitmapFactory.Options bitmapSizeOptions = new BitmapFactory.Options();
            bitmapSizeOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapSizeOptions);

            // load image using inSampleSize adapted to required image size
            BitmapFactory.Options bitmapDecodeOptions = new BitmapFactory.Options();
            bitmapDecodeOptions.inTempStorage = new byte[16 * 1024];
            bitmapDecodeOptions.inSampleSize = computeInSampleSize(bitmapSizeOptions, requiredWidth, requiredHeight, false);
            bitmapDecodeOptions.inPurgeable = true;
            bitmapDecodeOptions.inDither = !quickAndDirty;
            bitmapDecodeOptions.inPreferredConfig = quickAndDirty ? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888;

            Bitmap decodedBitmap = BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapDecodeOptions);

            // scale bitmap to mathc required size (and keep aspect ratio)

            float srcWidth = (float) bitmapDecodeOptions.outWidth;
            float srcHeight = (float) bitmapDecodeOptions.outHeight;

            float dstWidth = (float) requiredWidth;
            float dstHeight = (float) requiredHeight;

            float srcAspectRatio = srcWidth / srcHeight;
            float dstAspectRatio = dstWidth / dstHeight;

            // recycleDecodedBitmap is used to know if we must recycle intermediary 'decodedBitmap'
            // (DO NOT recycle it right away: wait for end of bitmap manipulation process to avoid
            // java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@416ee7d8
            // I do not excatly understand why, but this way it's OK

            boolean recycleDecodedBitmap = false;

            Bitmap scaledBitmap = decodedBitmap;
            if (srcAspectRatio < dstAspectRatio)
            {
                scaledBitmap = getScaledBitmap(decodedBitmap, (int) dstWidth, (int) (srcHeight * (dstWidth / srcWidth)));
                // will recycle recycleDecodedBitmap
                recycleDecodedBitmap = true;
            }
            else if (srcAspectRatio > dstAspectRatio)
            {
                scaledBitmap = getScaledBitmap(decodedBitmap, (int) (srcWidth * (dstHeight / srcHeight)), (int) dstHeight);
                recycleDecodedBitmap = true;
            }

            // crop image to match required image size

            int scaledBitmapWidth = scaledBitmap.getWidth();
            int scaledBitmapHeight = scaledBitmap.getHeight();

            Bitmap croppedBitmap = scaledBitmap;

            if (scaledBitmapWidth > requiredWidth)
            {
                int xOffset = (scaledBitmapWidth - requiredWidth) / 2;
                croppedBitmap = Bitmap.createBitmap(scaledBitmap, xOffset, 0, requiredWidth, requiredHeight);
                scaledBitmap.recycle();
            }
            else if (scaledBitmapHeight > requiredHeight)
            {
                int yOffset = (scaledBitmapHeight - requiredHeight) / 2;
                croppedBitmap = Bitmap.createBitmap(scaledBitmap, 0, yOffset, requiredWidth, requiredHeight);
                scaledBitmap.recycle();
            }

            if (recycleDecodedBitmap)
            {
                decodedBitmap.recycle();
            }
            decodedBitmap = null;

            scaledBitmap = null;
            return croppedBitmap;
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * compute powerOf2 or exact scale to be used as {@link BitmapFactory.Options#inSampleSize} value (for subSampling)
     * 
     * @param requiredWidth
     * @param requiredHeight
     * @param powerOf2
     *            weither we want a power of 2 sclae or not
     * @return
     */
    public static int computeInSampleSize(BitmapFactory.Options options, int dstWidth, int dstHeight, boolean powerOf2)
    {
        int inSampleSize = 1;

        // Raw height and width of image
        final int srcHeight = options.outHeight;
        final int srcWidth = options.outWidth;

        if (powerOf2)
        {
            //Find the correct scale value. It should be the power of 2.

            int tmpWidth = srcWidth, tmpHeight = srcHeight;
            while (true)
            {
                if (tmpWidth / 2 < dstWidth || tmpHeight / 2 < dstHeight)
                    break;
                tmpWidth /= 2;
                tmpHeight /= 2;
                inSampleSize *= 2;
            }
        }
        else
        {
            // Calculate ratios of height and width to requested height and width
            final int heightRatio = Math.round((float) srcHeight / (float) dstHeight);
            final int widthRatio = Math.round((float) srcWidth / (float) dstWidth);

            // Choose the smallest ratio as inSampleSize value, this will guarantee
            // a final image with both dimensions larger than or equal to the
            // requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }

        return inSampleSize;
    }

    public static Bitmap drawableToBitmap(Drawable drawable)
    {
        if (drawable instanceof BitmapDrawable)
        {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    }

    public static Bitmap getScaledBitmap(Bitmap bitmap, int newWidth, int newHeight)
    {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;

        // CREATE A MATRIX FOR THE MANIPULATION
        Matrix matrix = new Matrix();
        // RESIZE THE BIT MAP
        matrix.postScale(scaleWidth, scaleHeight);

        // RECREATE THE NEW BITMAP
        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
        return resizedBitmap;
    }

}
 19
Author: Pascal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-13 06:43:01

Żadna z powyższych odpowiedzi nie zadziałała dla mnie, ale wymyśliłem strasznie brzydkie obejście, które rozwiązało problem. Dodałem bardzo mały, pikselowy obraz 1x1 do mojego projektu jako zasób i załadowałem go do mojego ImageView przed wywołaniem do garbage collection. Myślę, że możliwe, że ImageView nie wypuszczał bitmapy, więc GC nigdy jej nie podniósł. Jest brzydki, ale na razie działa.

if (bitmap != null)
{
  bitmap.recycle();
  bitmap = null;
}
if (imageView != null)
{
  imageView.setImageResource(R.drawable.tiny); // This is my 1x1 png.
}
System.gc();

imageView.setImageBitmap(...); // Do whatever you need to do to load the image you want.
 18
Author: Mike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-07 12:32:34

To mi pasuje.

Bitmap myBitmap;

BitmapFactory.Options options = new BitmapFactory.Options(); 
options.InPurgeable = true;
options.OutHeight = 50;
options.OutWidth = 50;
options.InSampleSize = 4;

File imgFile = new File(filepath);
myBitmap = BitmapFactory.DecodeFile(imgFile.AbsolutePath, options);

A to jest na C # monodroid. możesz łatwo zmienić ścieżkę obrazu. ważne są tutaj opcje, które należy ustawić.

 18
Author: Dobermaxx99,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-02 10:33:28

Wydaje się to odpowiednim miejscem do dzielenia się moją klasą narzędzi do ładowania i przetwarzania obrazów ze społecznością, możesz z niej korzystać i dowolnie ją modyfikować.

package com.emil;

import java.io.IOException;
import java.io.InputStream;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

/**
 * A class to load and process images of various sizes from input streams and file paths.
 * 
 * @author Emil http://stackoverflow.com/users/220710/emil
 *
 */
public class ImageProcessing {

    public static Bitmap getBitmap(InputStream stream, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
        Bitmap bm = BitmapFactory.decodeStream(stream,null,options);
        if(ImageProcessing.checkDecode(options)){
            return bm;
        }else{
            throw new IOException("Image decoding failed, using stream.");
        }
    }

    public static Bitmap getBitmap(String imgPath, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
        Bitmap bm = BitmapFactory.decodeFile(imgPath,options);
        if(ImageProcessing.checkDecode(options)){
            return bm;
        }else{
            throw new IOException("Image decoding failed, using file path.");
        }
    }

    public static Dimensions getDimensions(InputStream stream) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
        BitmapFactory.decodeStream(stream,null,options);
        if(ImageProcessing.checkDecode(options)){
            return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
        }else{
            throw new IOException("Image decoding failed, using stream.");
        }
    }

    public static Dimensions getDimensions(String imgPath) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
        BitmapFactory.decodeFile(imgPath,options);
        if(ImageProcessing.checkDecode(options)){
            return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
        }else{
            throw new IOException("Image decoding failed, using file path.");
        }
    }

    private static boolean checkDecode(BitmapFactory.Options options){
        // Did decode work?
        if( options.outWidth<0 || options.outHeight<0 ){
            return false;
        }else{
            return true;
        }
    }

    /**
     * Creates a Bitmap that is of the minimum dimensions necessary
     * @param bm
     * @param min
     * @return
     */
    public static Bitmap createMinimalBitmap(Bitmap bm, ImageProcessing.Minimize min){
        int newWidth, newHeight;
        switch(min.type){
        case WIDTH:
            if(bm.getWidth()>min.minWidth){
                newWidth=min.minWidth;
                newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
            }else{
                // No resize
                newWidth=bm.getWidth();
                newHeight=bm.getHeight();
            }
            break;
        case HEIGHT:
            if(bm.getHeight()>min.minHeight){
                newHeight=min.minHeight;
                newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
            }else{
                // No resize
                newWidth=bm.getWidth();
                newHeight=bm.getHeight();
            }
            break;
        case BOTH: // minimize to the maximum dimension
        case MAX:
            if(bm.getHeight()>bm.getWidth()){
                // Height needs to minimized
                min.minDim=min.minDim!=null ? min.minDim : min.minHeight;
                if(bm.getHeight()>min.minDim){
                    newHeight=min.minDim;
                    newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
                }else{
                    // No resize
                    newWidth=bm.getWidth();
                    newHeight=bm.getHeight();
                }
            }else{
                // Width needs to be minimized
                min.minDim=min.minDim!=null ? min.minDim : min.minWidth;
                if(bm.getWidth()>min.minDim){
                    newWidth=min.minDim;
                    newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
                }else{
                    // No resize
                    newWidth=bm.getWidth();
                    newHeight=bm.getHeight();
                }
            }
            break;
        default:
            // No resize
            newWidth=bm.getWidth();
            newHeight=bm.getHeight();
        }
        return Bitmap.createScaledBitmap(bm, newWidth, newHeight, true);
    }

    public static int getScaledWidth(int height, Bitmap bm){
        return (int)(((double)bm.getWidth()/bm.getHeight())*height);
    }

    public static int getScaledHeight(int width, Bitmap bm){
        return (int)(((double)bm.getHeight()/bm.getWidth())*width);
    }

    /**
     * Get the proper sample size to meet minimization restraints
     * @param dim
     * @param min
     * @param multipleOf2 for fastest processing it is recommended that the sample size be a multiple of 2
     * @return
     */
    public static int getSampleSize(ImageProcessing.Dimensions dim, ImageProcessing.Minimize min, boolean multipleOf2){
        switch(min.type){
        case WIDTH:
            return ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
        case HEIGHT:
            return ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
        case BOTH:
            int widthMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
            int heightMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
            // Return the smaller of the two
            if(widthMaxSampleSize<heightMaxSampleSize){
                return widthMaxSampleSize;
            }else{
                return heightMaxSampleSize;
            }
        case MAX:
            // Find the larger dimension and go bases on that
            if(dim.width>dim.height){
                return ImageProcessing.getMaxSampleSize(dim.width, min.minDim, multipleOf2);
            }else{
                return ImageProcessing.getMaxSampleSize(dim.height, min.minDim, multipleOf2);
            }
        }
        return 1;
    }

    public static int getMaxSampleSize(int dim, int min, boolean multipleOf2){
        int add=multipleOf2 ? 2 : 1;
        int size=0;
        while(min<(dim/(size+add))){
            size+=add;
        }
        size = size==0 ? 1 : size;
        return size;        
    }

    public static class Dimensions {
        int width;
        int height;

        public Dimensions(int width, int height) {
            super();
            this.width = width;
            this.height = height;
        }

        @Override
        public String toString() {
            return width+" x "+height;
        }
    }

    public static class Minimize {
        public enum Type {
            WIDTH,HEIGHT,BOTH,MAX
        }
        Integer minWidth;
        Integer minHeight;
        Integer minDim;
        Type type;

        public Minimize(int min, Type type) {
            super();
            this.type = type;
            switch(type){
            case WIDTH:
                this.minWidth=min;
                break;
            case HEIGHT:
                this.minHeight=min;
                break;
            case BOTH:
                this.minWidth=min;
                this.minHeight=min;
                break;
            case MAX:
                this.minDim=min;
                break;
            }
        }

        public Minimize(int minWidth, int minHeight) {
            super();
            this.type=Type.BOTH;
            this.minWidth = minWidth;
            this.minHeight = minHeight;
        }

    }

    /**
     * Estimates size of Bitmap in bytes depending on dimensions and Bitmap.Config
     * @param width
     * @param height
     * @param config
     * @return
     */
    public static long estimateBitmapBytes(int width, int height, Bitmap.Config config){
        long pixels=width*height;
        switch(config){
        case ALPHA_8: // 1 byte per pixel
            return pixels;
        case ARGB_4444: // 2 bytes per pixel, but depreciated
            return pixels*2;
        case ARGB_8888: // 4 bytes per pixel
            return pixels*4;
        case RGB_565: // 2 bytes per pixel
            return pixels*2;
        default:
            return pixels;
        }
    }

    private static BitmapFactory.Options getOptionsForDimensions(){
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds=true;
        return options;
    }

    private static BitmapFactory.Options getOptionsForSampling(int sampleSize, Bitmap.Config bitmapConfig){
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inSampleSize = sampleSize;
        options.inScaled = false;
        options.inPreferredConfig = bitmapConfig;
        return options;
    }
}
 14
Author: Emil Davtyan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-02 23:41:44

W Jednym z moich aplikacji muszę zrobić zdjęcie albo z Camera/Gallery. Jeśli użytkownik kliknie obraz z aparatu (może to być 2MP, 5MP lub 8MP), Rozmiar obrazu waha się od kBS do MB s. Jeśli rozmiar obrazu jest mniejszy (lub do 1-2MB) powyżej kodu działa dobrze, ale jeśli mam obraz o rozmiarze powyżej 4MB lub 5MB, to OOM wchodzi w klatkę: (

Potem pracowałem nad rozwiązaniem tego problemu i w końcu zrobiłem poniższą poprawkę do kodu Fedora (cała zasługa Fedora za stworzenie tak ładnego rozwiązania):)

private Bitmap decodeFile(String fPath) {
    // Decode image size
    BitmapFactory.Options opts = new BitmapFactory.Options();
    /*
     * If set to true, the decoder will return null (no bitmap), but the
     * out... fields will still be set, allowing the caller to query the
     * bitmap without having to allocate the memory for its pixels.
     */
    opts.inJustDecodeBounds = true;
    opts.inDither = false; // Disable Dithering mode
    opts.inPurgeable = true; // Tell to gc that whether it needs free
                                // memory, the Bitmap can be cleared
    opts.inInputShareable = true; // Which kind of reference will be used to
                                    // recover the Bitmap data after being
                                    // clear, when it will be used in the
                                    // future

    BitmapFactory.decodeFile(fPath, opts);

    // The new size we want to scale to
    final int REQUIRED_SIZE = 70;

    // Find the correct scale value. 
    int scale = 1;

    if (opts.outHeight > REQUIRED_SIZE || opts.outWidth > REQUIRED_SIZE) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) opts.outHeight
                / (float) REQUIRED_SIZE);
        final int widthRatio = Math.round((float) opts.outWidth
                / (float) REQUIRED_SIZE);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        scale = heightRatio < widthRatio ? heightRatio : widthRatio;//
    }

    // Decode bitmap with inSampleSize set
    opts.inJustDecodeBounds = false;

    opts.inSampleSize = scale;

    Bitmap bm = BitmapFactory.decodeFile(fPath, opts).copy(
            Bitmap.Config.RGB_565, false);

    return bm;

}

I hope pomoże to kumplom borykającym się z tym samym problemem!

Aby uzyskać więcej, zapoznaj się to

 14
Author: Rupesh Yadav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-06 14:53:59

Wpadłem na ten problem kilka minut temu. Rozwiązałem go, wykonując lepszą pracę w zarządzaniu moją listview adapter. Myślałem, że to problem z setkami obrazów 50x50px, których używałem, okazało się, że próbowałem nadmuchać mój niestandardowy widok za każdym razem, gdy wyświetlany był wiersz. Po prostu testując, aby sprawdzić, czy wiersz został napompowany, wyeliminowałem ten błąd i używam setek bitmap. W rzeczywistości jest to dla spinnera, ale Adapter bazowy działa tak samo dla ListView. This simple poprawka znacznie poprawiła również wydajność adaptera.

@Override
public View getView(final int position, View convertView, final ViewGroup parent) {

    if(convertView == null){
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.spinner_row, null);
    }
...
 13
Author: BajaBob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-24 19:08:16

Ten problem występuje tylko w emulatorach Androida. Miałem również do czynienia z tym problemem w emulatorze, ale kiedy sprawdziłem w urządzeniu to działało dobrze.

Więc proszę sprawdzić urządzenie. Może być uruchomiony w urządzeniu.

 13
Author: hackjutsu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-10 00:53:29

Spędziłem cały dzień testując te rozwiązania i jedyną rzeczą, która działała dla mnie, jest powyższe podejście do uzyskania obrazu i ręcznego wywołania GC, które Wiem, że nie powinno być konieczne, ale jest to jedyna rzecz, która działała, gdy umieszczam moją aplikację pod dużym obciążeniem testowania przełączania między działaniami. Moja aplikacja ma listę miniatur obrazów w widoku listy w (powiedzmy aktywność A), a po kliknięciu jednego z tych obrazów przenosi cię do innej aktywności (powiedzmy aktywność B) pokazuje główny obraz tego elementu. Kiedy chciałbym przełączyć się tam iz powrotem między dwoma działaniami, w końcu uzyskać błąd OOM i aplikacja wymusi zamknąć.

Kiedy dostałbym się w połowie listview to się zawiesił.

Teraz, kiedy zaimplementuję następujące czynności w ćwiczeniu B, mogę przejść przez cały listview bez problemu i kontynuować i kontynuować...i to szybko.

@Override
public void onDestroy()
{   
    Cleanup();
    super.onDestroy();
}

private void Cleanup()
{    
    bitmap.recycle();
    System.gc();
    Runtime.getRuntime().gc();  
}
 12
Author: Jesse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-27 19:33:09

Moje 2 grosze: rozwiązałem błędy OOM z bitmapami przez:

A) skalowanie moich obrazów o współczynnik 2

B) korzystanie z biblioteki Picasso w moim niestandardowym adapterze dla ListView, z jednym wywołaniem w getView Tak: Picasso.with(context).load(R.id.myImage).into(R.id.myImageView);

 11
Author: matsoftware,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-14 09:08:14

Użyj tego kodu dla każdego obrazu w Wybierz z SdCard lub drewable do konwersji obiektu bitmapy.

Resources res = getResources();
WindowManager window = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = window.getDefaultDisplay();
@SuppressWarnings("deprecation")
int width = display.getWidth();
@SuppressWarnings("deprecation")
int height = display.getHeight();
try {
    if (bitmap != null) {
        bitmap.recycle();
        bitmap = null;
        System.gc();
    }
    bitmap = Bitmap.createScaledBitmap(BitmapFactory
        .decodeFile(ImageData_Path.get(img_pos).getPath()),
        width, height, true);
} catch (OutOfMemoryError e) {
    if (bitmap != null) {
        bitmap.recycle();
        bitmap = null;
        System.gc();
    }
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Config.RGB_565;
    options.inSampleSize = 1;
    options.inPurgeable = true;
    bitmapBitmap.createScaledBitmap(BitmapFactory.decodeFile(ImageData_Path.get(img_pos)
        .getPath().toString(), options), width, height,true);
}
return bitmap;

Użyj instend ścieżki obrazu ImageData_Path.get(img_pos).getPath () .

 11
Author: Gaurav Pansheriya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-24 20:36:02

Takie OutofMemoryException nie mogą być całkowicie rozwiązane przez wywołanie System.gc() i tak dalej .

odwołując się do cyklu życia aktywności

Stany aktywności są określane przez sam system operacyjny, z zastrzeżeniem wykorzystania pamięci dla każdego procesu i priorytetu każdego procesu.

Możesz wziąć pod uwagę rozmiar i rozdzielczość każdego z używanych obrazów bitmapowych. Polecam zmniejszyć rozmiar, resample do niższej rozdzielczości, odnoszą się do projektowania galerii (jeden mały obraz PNG i jedno oryginalne zdjęcie.)

 10
Author: Raju Gujarati,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-27 09:32:45

Ogólnie rozmiar sterty urządzenia android jest tylko 16MB (różni się od urządzenia/OS patrz post rozmiar sterty), jeśli ładujesz obrazy i przekracza rozmiar 16MB , wyrzuci wyjątek pamięci, zamiast używać bitmapy dla , ładowanie obrazów 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 null, jeśli Twoja praca jest wykonywana z bitmap.

 10
Author: Rohit Sharma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-09 11:42:42

Ten kod pomoże załadować dużą bitmapę z drawable

public class BitmapUtilsTask extends AsyncTask<Object, Void, Bitmap> {

    Context context;

    public BitmapUtilsTask(Context context) {
        this.context = context;
    }

    /**
     * Loads a bitmap from the specified url.
     * 
     * @param url The location of the bitmap asset
     * @return The bitmap, or null if it could not be loaded
     * @throws IOException
     * @throws MalformedURLException
     */
    public Bitmap getBitmap() throws MalformedURLException, IOException {       

        // Get the source image's dimensions
        int desiredWidth = 1000;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;

        BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);

        int srcWidth = options.outWidth;
        int srcHeight = options.outHeight;

        // Only scale if the source is big enough. This code is just trying
        // to fit a image into a certain width.
        if (desiredWidth > srcWidth)
            desiredWidth = srcWidth;

        // Calculate the correct inSampleSize/scale value. This helps reduce
        // memory use. It should be a power of 2
        int inSampleSize = 1;
        while (srcWidth / 2 > desiredWidth) {
            srcWidth /= 2;
            srcHeight /= 2;
            inSampleSize *= 2;
        }
        // Decode with inSampleSize
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inSampleSize = inSampleSize;
        options.inScaled = false;
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        options.inPurgeable = true;
        Bitmap sampledSrcBitmap;

        sampledSrcBitmap =  BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);

        return sampledSrcBitmap;
    }

    /**
     * The system calls this to perform work in a worker thread and delivers
     * it the parameters given to AsyncTask.execute()
     */
    @Override
    protected Bitmap doInBackground(Object... item) {
        try { 
          return getBitmap();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
 10
Author: vineet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-10 09:26:33

Wszystkie rozwiązania wymagają ustawienia IMAGE_MAX_SIZE. Ogranicza to Urządzenia z mocniejszym sprzętem i jeśli rozmiar obrazu jest zbyt niski, wygląda brzydko na ekranie HD.

Wyszedłem z rozwiązaniem, które współpracuje z moim Samsungiem Galaxy S3 i kilkoma innymi urządzeniami, w tym mniej wydajnymi, z lepszą jakością obrazu, gdy używane jest urządzenie o większej mocy.

Chodzi o to, aby obliczyć maksymalną pamięć przydzieloną dla aplikacji na danym urządzeniu, a następnie ustawić skalę być możliwie najniższa bez przekroczenia tej pamięci. Oto kod:

public static Bitmap decodeFile(File f)
{
    Bitmap b = null;
    try
    {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;

        FileInputStream fis = new FileInputStream(f);
        try
        {
            BitmapFactory.decodeStream(fis, null, o);
        }
        finally
        {
            fis.close();
        }

        // In Samsung Galaxy S3, typically max memory is 64mb
        // Camera max resolution is 3264 x 2448, times 4 to get Bitmap memory of 30.5mb for one bitmap
        // If we use scale of 2, resolution will be halved, 1632 x 1224 and x 4 to get Bitmap memory of 7.62mb
        // We try use 25% memory which equals to 16mb maximum for one bitmap
        long maxMemory = Runtime.getRuntime().maxMemory();
        int maxMemoryForImage = (int) (maxMemory / 100 * 25);

        // Refer to
        // http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
        // A full screen GridView filled with images on a device with
        // 800x480 resolution would use around 1.5MB (800*480*4 bytes)
        // When bitmap option's inSampleSize doubled, pixel height and
        // weight both reduce in half
        int scale = 1;
        while ((o.outWidth / scale) * (o.outHeight / scale) * 4 > maxMemoryForImage)
        scale *= 2;

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        fis = new FileInputStream(f);
        try
        {
            b = BitmapFactory.decodeStream(fis, null, o2);
        }
        finally
        {
            fis.close();
        }
    }
    catch (IOException e)
    {
    }
    return b;
}

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 oczyszczona i nie pozostaje w pamięci, gdy skończysz jej używać. Zazwyczaj używam tego kodu do wykonywania rotacji obrazu (bitmapy źródłowej i docelowej), więc moja aplikacja musi załadować 2 bitmapy w pamięci w tym samym czasie, a 25% daje mi dobry bufor bez kończy się pamięć podczas obracania obrazu.

Mam nadzieję, że to komuś pomoże..
 9
Author: Bruce,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-31 03:04:47