java.lang.OutOfMemoryError: размер растрового изображения превышает бюджет виртуальной машины - Android - PullRequest
157 голосов
/ 22 декабря 2009

Я разработал приложение, которое использует много изображений на Android.

Приложение запускается один раз, заполняет информацию на экране (Layouts, Listviews, Textviews, ImageViews и т. Д.), И пользователь читает информацию.

Нет анимации, никаких спецэффектов или чего-либо, что может заполнить память. Иногда ничья может измениться. Некоторые из них - ресурсы Android, а некоторые - файлы, сохраненные в папке на SDCARD.

Затем пользователь завершает работу (выполняется метод onDestroy, и приложение остается в памяти виртуальной машиной), а затем в какой-то момент пользователь снова входит.

Каждый раз, когда пользователь входит в приложение, я могу видеть, как увеличивается и увеличивается объем памяти, пока пользователь не получит java.lang.OutOfMemoryError.

Так, каков наилучший / правильный способ обработки множества изображений?

Должен ли я поместить их в статические методы, чтобы они не загружались все время? Нужно ли чистить макет или изображения, используемые в макете, особым образом?

Ответы [ 13 ]

0 голосов
/ 10 ноября 2013

FWIW, вот легкий битовый кэш, который я кодировал и использовал в течение нескольких месяцев. Это не все навороты, поэтому прочитайте код, прежде чем использовать его.

/**
 * Lightweight cache for Bitmap objects. 
 * 
 * There is no thread-safety built into this class. 
 * 
 * Note: you may wish to create bitmaps using the application-context, rather than the activity-context. 
 * I believe the activity-context has a reference to the Activity object. 
 * So for as long as the bitmap exists, it will have an indirect link to the activity, 
 * and prevent the garbaage collector from disposing the activity object, leading to memory leaks. 
 */
public class BitmapCache { 

    private Hashtable<String,ArrayList<Bitmap>> hashtable = new Hashtable<String, ArrayList<Bitmap>>();  

    private StringBuilder sb = new StringBuilder(); 

    public BitmapCache() { 
    } 

    /**
     * A Bitmap with the given width and height will be returned. 
     * It is removed from the cache. 
     * 
     * An attempt is made to return the correct config, but for unusual configs (as at 30may13) this might not happen.  
     * 
     * Note that thread-safety is the caller's responsibility. 
     */
    public Bitmap get(int width, int height, Bitmap.Config config) { 
        String key = getKey(width, height, config); 
        ArrayList<Bitmap> list = getList(key); 
        int listSize = list.size();
        if (listSize>0) { 
            return list.remove(listSize-1); 
        } else { 
            try { 
                return Bitmap.createBitmap(width, height, config);
            } catch (RuntimeException e) { 
                // TODO: Test appendHockeyApp() works. 
                App.appendHockeyApp("BitmapCache has "+hashtable.size()+":"+listSize+" request "+width+"x"+height); 
                throw e ; 
            }
        }
    }

    /**
     * Puts a Bitmap object into the cache. 
     * 
     * Note that thread-safety is the caller's responsibility. 
     */
    public void put(Bitmap bitmap) { 
        if (bitmap==null) return ; 
        String key = getKey(bitmap); 
        ArrayList<Bitmap> list = getList(key); 
        list.add(bitmap); 
    }

    private ArrayList<Bitmap> getList(String key) {
        ArrayList<Bitmap> list = hashtable.get(key);
        if (list==null) { 
            list = new ArrayList<Bitmap>(); 
            hashtable.put(key, list); 
        }
        return list;
    } 

    private String getKey(Bitmap bitmap) {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        Config config = bitmap.getConfig();
        return getKey(width, height, config);
    }

    private String getKey(int width, int height, Config config) {
        sb.setLength(0); 
        sb.append(width); 
        sb.append("x"); 
        sb.append(height); 
        sb.append(" "); 
        switch (config) {
        case ALPHA_8:
            sb.append("ALPHA_8"); 
            break;
        case ARGB_4444:
            sb.append("ARGB_4444"); 
            break;
        case ARGB_8888:
            sb.append("ARGB_8888"); 
            break;
        case RGB_565:
            sb.append("RGB_565"); 
            break;
        default:
            sb.append("unknown"); 
            break; 
        }
        return sb.toString();
    }

}
0 голосов
/ 21 мая 2013

У меня была такая же проблема только с переключением фоновых изображений с разумными размерами. Я получил лучшие результаты, установив для ImageView значение NULL, прежде чем вставлять новое изображение.

ImageView ivBg = (ImageView) findViewById(R.id.main_backgroundImage);
ivBg.setImageDrawable(null);
ivBg.setImageDrawable(getResources().getDrawable(R.drawable.new_picture));
0 голосов
/ 18 апреля 2012

Ну, я перепробовал все, что нашел в интернете, и ни один из них не сработал. Вызов System.gc () только снижает скорость приложения. Утилизация растровых изображений в onDestroy не работает для меня тоже.

Единственное, что сейчас работает, - это наличие статического списка всех растровых изображений, чтобы растровые изображения сохранялись после перезапуска. И просто используйте сохраненные растровые изображения вместо создания новых при каждом перезапуске действия.

В моем случае код выглядит так:

private static BitmapDrawable currentBGDrawable;

if (new File(uriString).exists()) {
    if (!uriString.equals(currentBGUri)) {
        freeBackground();
        bg = BitmapFactory.decodeFile(uriString);

        currentBGUri = uriString;
        bgDrawable = new BitmapDrawable(bg);
        currentBGDrawable = bgDrawable;
    } else {
        bgDrawable = currentBGDrawable;
    }
}
...