Как решить проблему java.lang.OutOfMemoryError в Android

Хотя у меня очень маленькое изображение размера в папке с возможностью переноса, я получаю эту ошибку от пользователей. И я не использую какую-либо функцию растрового изображения в коде. По крайней мере, преднамеренно:)

java.lang.OutOfMemoryError
    at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:683)
    at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:513)
    at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:889)
    at android.content.res.Resources.loadDrawable(Resources.java:3436)
    at android.content.res.Resources.getDrawable(Resources.java:1909)
    at android.view.View.setBackgroundResource(View.java:16251)
    at com.autkusoytas.bilbakalim.SoruEkrani.cevapSecimi(SoruEkrani.java:666)
    at com.autkusoytas.bilbakalim.SoruEkrani$9$1.run(SoruEkrani.java:862)
    at android.os.Handler.handleCallback(Handler.java:733)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:146)
    at android.app.ActivityThread.main(ActivityThread.java:5602)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
    at dalvik.system.NativeStart.main(Native Method)

В соответствии с этим stackTrace я получаю эту ошибку в этой строке ( "tv" - это textView):

tv.setBackgroundResource(R.drawable.yanlis);

В чем проблема? Если вам нужна другая информация о коде, я могу добавить ее. Спасибо!

Ответ 1

Вы не можете увеличить размер кучи динамически, но вы можете запросить больше использовать с помощью.

Android: largeHeap = "истина"

в manifest.xml, вы можете добавить в свой манифест эти строки, которые он работает для некоторых ситуаций.

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:largeHeap="true"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

Должны ли быть созданы ваши прикладные процессы с большой кучей Dalvik. Это относится ко всем процессам, созданным для приложения. Он применяется только к первому приложению, загружаемому в процесс; если вы используете общий идентификатор пользователя, чтобы разрешить нескольким приложениям использовать процесс, все они должны использовать эту опцию последовательно или у них будут непредсказуемые результаты. Большинство приложений не должны этого нуждаться и вместо этого должны сосредоточиться на сокращении общего использования памяти для повышения производительности. Включение этого также не гарантирует фиксированного увеличения доступной памяти, поскольку некоторые устройства ограничены их общей доступной памятью.


Чтобы запросить доступный размер памяти во время выполнения, используйте методы getMemoryClass() или getLargeMemoryClass().

Если все еще стоит проблема, тогда это также должно работать

 BitmapFactory.Options options = new BitmapFactory.Options();
 options.inSampleSize = 8;
 mBitmapInsurance = BitmapFactory.decodeFile(mCurrentPhotoPath,options);

Если установлено значение > 1, запрос декодера подвыражает исходное изображение, возвращая меньшее изображение для сохранения памяти.

Это оптимальное использование BitmapFactory.Options.inSampleSize относительно скорости отображения изображения. В документации упоминаются значения, которые имеют мощность 2, поэтому я работаю с 2, 4, 8, 16 и т.д.

Давайте углубимся в выборку изображений:

Например, его не стоит загружать в память изображение с разрешением 1024x768 пикселей, если оно в конечном итоге будет отображаться в миниатюре размером 128 × 128 пикселей в ImageView.

Чтобы декодер подсуммировал изображение, загрузив меньшую версию в память, установите inSampleSize в true в свой BitmapFactory.Options объект. Например, изображение с разрешением 2100 x 1500 пикселей, декодированное с помощью inSampleSize of 4, создает растровое изображение размером приблизительно 512x384. Загрузка этого в память использует 0,75 МБ, а не 12 МБ для полного изображения (при условии конфигурации растрового изображения ARGB_8888). Heres метод для вычисления значения размера выборки, который является мощностью двух на основе ширины и высоты цели:

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;
}

Примечание: вычисляется мощность двух значений, поскольку декодер использует конечное значение, округляя до ближайшей силы двух, согласно inSampleSize.

Чтобы использовать этот метод, сначала декодируйте с inJustDecodeBounds, установленным на true, передайте параметры через, а затем снова декодируйте, используя новое значение inSampleSize и inJustDecodeBounds, установленное на 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);
}

Этот метод упрощает загрузку растрового изображения произвольно большого размера в ImageView, который отображает миниатюру размером 100x100 пикселей, как показано в следующем примере кода:

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

Вы можете выполнить аналогичный процесс для декодирования растровых изображений из других источников, заменив соответствующий метод BitmapFactory.decode* по мере необходимости.


Я нашел этот код интересным:

private Bitmap getBitmap(String path) {

Uri uri = getImageUri(path);
InputStream in = null;
try {
    final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
    in = mContentResolver.openInputStream(uri);

    // Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(in, null, o);
    in.close();

    int scale = 1;
    while ((o.outWidth * o.outHeight) * (1 / Math.pow(scale, 2)) > 
          IMAGE_MAX_SIZE) {
       scale++;
    }
    Log.d(TAG, "scale = " + scale + ", orig-width: " + o.outWidth + ", 
       orig-height: " + o.outHeight);

    Bitmap bitmap = null;
    in = mContentResolver.openInputStream(uri);
    if (scale > 1) {
        scale--;
        // scale to max possible inSampleSize that still yields an image
        // larger than target
        o = new BitmapFactory.Options();
        o.inSampleSize = scale;
        bitmap = BitmapFactory.decodeStream(in, null, o);

        // resize to desired dimensions
        int height = bitmap.getHeight();
        int width = bitmap.getWidth();
        Log.d(TAG, "1th scale operation dimenions - width: " + width + ",
           height: " + height);

        double y = Math.sqrt(IMAGE_MAX_SIZE
                / (((double) width) / height));
        double x = (y / height) * width;

        Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, (int) x, 
           (int) y, true);
        bitmap.recycle();
        bitmap = scaledBitmap;

        System.gc();
    } else {
        bitmap = BitmapFactory.decodeStream(in);
    }
    in.close();

    Log.d(TAG, "bitmap size - width: " +bitmap.getWidth() + ", height: " + 
       bitmap.getHeight());
    return bitmap;
} catch (IOException e) {
    Log.e(TAG, e.getMessage(),e);
    return null;
}

Как управлять памятью приложения: ссылка


Не рекомендуется использовать android:largeHeap="true" здесь выдержку из Google, которая объясняет это,

Однако способность запрашивать большую кучу предназначена только для небольшой набор приложений, которые могут оправдать необходимость потреблять больше оперативной памяти (например, как большое приложение для редактирования фотографий). Никогда не просите большую кучу просто потому что у вас закончилась нехватка памяти, и вам нужно быстрое исправление - вы должны используйте его только тогда, когда вы точно знаете, где находится вся ваша память и почему он должен быть сохранен. Тем не менее, даже если вы уверены ваше приложение может оправдать большую кучу, вам следует избегать запроса насколько возможно. Использование дополнительной памяти будет все чаще в ущерб общему опыту пользователя, поскольку мусор сбор займет больше времени, а производительность системы может быть медленнее, если переключение задач или выполнение других общих операций.

После работы excrutiatingly с out of memory errors я бы сказал, добавив это в манифест, чтобы избежать проблемы с oom, не является грехом


Проверка поведения приложения в Android Runtime (ART)

Время выполнения Android (ART) - это среда исполнения по умолчанию для устройств под управлением Android 5.0 (API уровня 21) и выше. Эта среда исполнения предлагает ряд функций, которые повышают производительность и плавность платформы Android и приложений. Вы можете найти дополнительную информацию о новых функциях ART в Представлять ART.

Однако некоторые методы, которые работают на Дальвике, не работают на АРТ. Этот документ позволяет вам узнать о том, что нужно посмотреть при переносе существующего приложения для совместимости с АРТ. Большинство приложений должны работать только при работе с ART.


Проблемы с сборкой мусора (GC)

В Dalvik приложениям часто бывает полезно явно вызвать System.gc(), чтобы вызвать сборку мусора (GC). Это должно быть гораздо менее необходимо для АРТ, особенно если вы вызываете сбор мусора, чтобы предотвратить появление типа GC_FOR_ALLOC или уменьшить фрагментацию. Вы можете проверить, какая среда исполнения используется, вызывая System.getProperty( "java.vm.version" ). Если используется АРТ, значение свойства "2.0.0" или выше.

Кроме того, в Android Open-Source Project (AOSP) разрабатывается компактный сборщик мусора для улучшения управления памятью. Из-за этого вам следует избегать использования методов, которые несовместимы с уплотнением GC (например, сохранение указателей на данные экземпляра объекта). Это особенно важно для приложений, которые используют интерфейс Java Native Interface (JNI). Дополнительные сведения см. В разделе Предупреждение проблем JNI.


Предотвращение проблем JNI

ART JNI несколько более строг, чем у Dalvik's. Особенно полезно использовать режим CheckJNI для обнаружения общих проблем. Если ваше приложение использует код C/С++, вы должны просмотреть следующую статью:


Кроме того, вы можете использовать встроенную память (NDK и JNI), поэтому вы фактически обходите ограничение размера кучи.

Вот несколько сообщений об этом:

и здесь создана библиотека для него:

Ответ 2

Я вижу только два варианта:

  • У вас есть утечки памяти в приложении.
  • У устройств недостаточно памяти при запуске приложения.

Ответ 3

Вы должны реализовать диспетчер кэша LRU при работе с растровым изображением

http://developer.android.com/reference/android/util/LruCache.html http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html Когда следует перерабатывать растровое изображение с использованием LRUCache?

ИЛИ

Используйте библиотеку уровней, например Universal Image Loader:

https://github.com/nostra13/Android-Universal-Image-Loader

ИЗМЕНИТЬ:

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

https://github.com/bumptech/glide

Ответ 4

Несколько советов по обработке такой ошибки/исключения для приложений Android:

  1. Деятельность и применение имеют такие методы, как:

    • onLowMemory
    • onTrimMemory Обработайте эти методы, чтобы следить за использованием памяти.
  2. тег в манифесте может иметь атрибут "largeHeap", установленный в значение "ИСТИНА", что требует дополнительной кучи для изолированной программной среды приложения

  3. Управление кэшированием в памяти и диском:

    • Изображения и другие данные могли кэшироваться в памяти во время работы приложения (локально в действиях/фрагментах и глобально); следует управлять или удалить.
  4. Использование WeakReference, SoftReference создания экземпляров Java, в частности, для файлов.

  5. Если изображений так много, используйте правильную библиотеку/структуру данных, которая может управлять памятью, использовать выборку загруженных изображений, обрабатывать кеширование диска.

  6. Обработать исключение OutOfMemory

  7. Следуйте рекомендациям по кодированию

    • Утечка памяти (Не держите все под строгим контролем)
  8. Минимизируйте стек действий, например, количество действий в стеке (не держите все в контексте/активности)

    • Контекст имеет смысл, те данные/экземпляры, которые не требуются вне области действия (действия и фрагменты), удерживают их в соответствующем контексте вместо глобального хранения ссылок.
  9. Минимизируйте использование статики, еще много синглетонов.

  10. Позаботьтесь об основных фондах памяти ОС

    • Проблемы фрагментации памяти
  11. Иногда вызывайте GC.Collect() вручную, если вы уверены, что кэширование в памяти больше не требуется.

Ответ 5

Если вы получаете эту ошибку java.lang.OutOfMemoryError, это самая распространенная проблема, возникающая в Android. Эта ошибка выдается виртуальной машиной Java (JVM), когда объект не может быть выделен из-за недостатка места в памяти.

Попробуйте следующее android:hardwareAccelerated="false", android:largeHeap="true" в файле manifest.xml в приложении, например:

<application
  android:name=".MyApplication"
  android:allowBackup="true"
  android:icon="@mipmap/ic_launcher"
  android:label="@string/app_name"
  android:theme="@style/AppTheme"
  android:hardwareAccelerated="false"
  android:largeHeap="true" />