Android - восстановление из состояния низкой памяти

Я разрабатываю приложение с очень интенсивной обработкой изображений, где у меня есть несколько ListFragments в горизонтальном FragmentStatePagerAdapter. Я настойчиво применяю практически все трюки и предложения, которые я смог найти здесь и в других местах. Загрузить растровые изображения и сохранить их на SD и как мягкий кэш ссылки памяти.

Тем не менее, когда я использую приложение в определенный момент, я начинаю видеть сообщения в LogCat, как показано ниже

11-05 15:57:43.951: I/dalvikvm-heap(5449): Clamp target GC heap from 32.655MB to 32.000MB
11-05 15:57:43.951: D/dalvikvm(5449): GC_FOR_MALLOC freed 0K, 24% free 11512K/14983K, external 17446K/17896K, paused 64ms
11-05 15:57:44.041: D/dalvikvm(5449): GC_EXTERNAL_ALLOC freed <1K, 24% free 11511K/14983K, external 17258K/17896K, paused 77ms

Если я продолжу, сообщения выше станут более срочными

11-05 16:02:09.590: D/dalvikvm(5449): GC_FOR_MALLOC freed 0K, 23% free 11872K/15239K, external 17497K/17896K, paused 71ms
11-05 16:02:09.700: D/dalvikvm(5449): GC_EXTERNAL_ALLOC freed <1K, 23% free 11872K/15239K, external 17497K/17896K, paused 84ms
11-05 16:02:09.720: E/dalvikvm-heap(5449): 192816-byte external allocation too large for this process.
11-05 16:02:09.800: I/dalvikvm-heap(5449): Clamp target GC heap from 33.057MB to 32.000MB
11-05 16:02:09.800: D/dalvikvm(5449): GC_FOR_MALLOC freed 0K, 23% free 11872K/15239K, external 17497K/17896K, paused 68ms
11-05 16:02:09.800: E/GraphicsJNI(5449): VM won't let us allocate 192816 bytes

И неизбежно приложение будет аварийно отключено OutOfMemoryException Симптомы - это классическая утечка памяти. Тем не менее, в моем методе Fragment#onDestroy я отменяю все ожидающие задачи, отвязываю и аннулирую виды и вызываю Bitmap # recycle. Интересно, что я вижу вызовы GC в LogCat, но даже если я приостанавливаюсь в течение длительного периода времени, память не восстанавливается.

У меня возникает ощущение, что он постоянно перечитывает изображения из SD, что вызывает деградацию и неизбежную кончину

Вот мой метод очистки, который я использую, пытаясь избавиться от drawables (там, как я сказал, отменить ожидающие/запущенные задачи и пустые адаптеры ListView)

public static void unbindDrawables(View view, final boolean agressive) {
        if (view instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
                unbindDrawables(((ViewGroup) view).getChildAt(i), agressive);
            }
            if (!AdapterView.class.isInstance(view)) {
                ((ViewGroup) view).removeAllViews();
            }
        } else {
            Drawable bmp = view.getBackground();
            if (bmp == null && ImageView.class.isInstance(view)) bmp = ((ImageView) view).getDrawable();
            if (bmp != null) {
                bmp.setCallback(null);
                if (agressive && (TaggedDrawable.class.isInstance(bmp))) {
                    Bitmap bm = ((BitmapDrawable) bmp).getBitmap();
                    if (bm != null) {
                        if (DEBUG) Log.i(TAG, "Forcing bitmap recycle for " + bmp);
                        bm.recycle();
                        bm = null;
                        view.destroyDrawingCache();
                        view = null;
                    }
                }
            }
        }
    }

Излишне говорить, что я серьезно обеспокоен в этот момент и буду очень признателен за любые предложения

Ответ 1

Во-первых, GC не будет перерабатывать память сразу после вызова. Вот несколько советов с сайта разработчика Android:

  • Не сохраняйте долговечные ссылки на контекст-активность (ссылка к деятельности должен иметь тот же жизненный цикл, что и деятельность Сам)

  • Попробуйте использовать контекстное приложение вместо контекстной активности

  • Избегайте нестатических внутренних классов в активности, если вы не контролируете их жизненный цикл, использовать статический внутренний класс и сделать слабую ссылку к деятельности внутри. Решение этой проблемы заключается в использовании статический внутренний класс с WeakReference во внешний класс, как это сделано в ViewRoot и его внутреннем классе W, например

  • Сборщик мусора не является страхованием от утечек памяти

Во-вторых, попробуйте использовать BitmapFactory.options, если вы не очень заботитесь о качестве растрового изображения.

В-третьих, используйте try catch для обработки исключения OutOfMemory в блоке catch.

Наконец, используйте Memory Analyzer. Откройте DDMS в Eclipse, на панели инструментов появится кнопка кучи обновления. вы можете использовать это для генерации hprof файла, а затем использовать инструмент hprof-conv в каталоге andorid-sdk-tools для преобразования файла в указанный файл формата, который может считывать Memory Analyzer. Теперь вы можете использовать анализатор памяти для анализа возможной утечки памяти. Это действительно хороший инструмент, который даст вам много предложений, чтобы избежать outofmemory.

Надеюсь, это поможет вам, если вы найдете несколько лучших методов, скажите, пожалуйста, я также столкнулся с outofmemory в своем приложении.

Ответ 2

Попробуйте, где высота и ширина зависит от вас, в настоящее время m использует 1024 для обоих.

public class BitmapUtils {

public static Bitmap resizeBitmap( Bitmap input, int destWidth, int destHeight )
{
    int srcWidth = input.getWidth();
    int srcHeight = input.getHeight();
    boolean needsResize = false;
    float p;
    if ( srcWidth > destWidth || srcHeight > destHeight ) {
        needsResize = true;
        if ( srcWidth > srcHeight && srcWidth > destWidth ) {
            p = (float)destWidth / (float)srcWidth;
            destHeight = (int)( srcHeight * p );
        } else {
            p = (float)destHeight / (float)srcHeight;
            destWidth = (int)( srcWidth * p );
        }
    } else {
        destWidth = srcWidth;
        destHeight = srcHeight;
    }
    if ( needsResize ) {
        Bitmap output = Bitmap.createScaledBitmap( input, destWidth, destHeight, true );
        return output;
    } else {
        return input;
    }
 }
}

Ответ 3

В то время как битмап потребляет много памяти, перезвоните в растровое изображение, если это не требуется.

Также см. этот Thread

вы не будете утечки памяти, если вы не вызываете Bitmap.recycle, если вы не храните жесткие ссылки случайно. Вы можете столкнуться с OutOfMemoryErrors, если попытаетесь выделить слишком много растровых изображений сразу или слишком больших растровых изображений без вызова .recycle