Подтвердить что ты не робот

Как загрузить большие изображения в Android и избежать ошибки из памяти?

Я работаю над приложением, которое использует большие изображения (1390 × 870: 150kb - 50kb). Я добавляю изображения при нажатии триггера /ImageView.

В какой-то момент у меня возникает ошибка в памяти:

java.lang.OutOfMemoryError
E/AndroidRuntime(23369): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
E/AndroidRuntime(23369): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:613)
E/AndroidRuntime(23369): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:378)

Чтобы изменить размер изображения, я делаю это:

Bitmap productIndex = null;
final String imageLoc = IMAGE_LOCATION;
InputStream imageStream;
try {
     imageStream = new FileInputStream(imageLoc);
     productIndex = decodeSampledBitmapFromResource(getResources(), imageLoc, 400, 400);

     productIV.setImageBitmap(productIndex);
     } catch (FileNotFoundException e1) {
          // TODO Auto-generated catch block
          e1.printStackTrace();
     }
}


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

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

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

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

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 / 3;
    final int halfWidth = width / 3;

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

У меня есть такой способ изменения размера, чтобы сэкономить место в Android-документах: Эффективная загрузка больших растровых изображений

В соответствии с журналом это похоже на виновника в методе decodeSampledBitmapFromResource:

return BitmapFactory.decodeFile(resId, options);

----- edit ----- Вот как я добавляю каждый элемент в FrameLayout.

for(int ps=0;ps<productSplit.size();ps++){
    //split each product by the equals sign
    List<String> productItem = Arrays.asList(productSplit.get(ps).split("="));

    String tempCarID = productItem.get(0);
    tempCarID = tempCarID.replace(" ", "");
    if(String.valueOf(carID).equals(tempCarID)){

        ImageView productIV = new ImageView(Configurator.this);
        LayoutParams productParams = new LayoutParams(
                LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        productIV.setId(Integer.parseInt(partIdsList.get(x)));
        productIV.setLayoutParams(productParams);

        final String imageLoc = productItem.get(2);

        InputStream imageStream;
        try {
            imageStream = new FileInputStream(imageLoc);
            productIndex = decodeSampledBitmapFromResource(getResources(), imageLoc, 400, 400);
            productIV.setImageBitmap(productIndex);
        } catch (FileNotFoundException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        productLayers.addView(productIV);

    }
}
4b9b3361

Ответ 1

Вы можете использовать другой bitmap-config для значительного уменьшения размера изображений. По умолчанию используется RGB-config ARGB8888, что означает использование четырех 8-битных каналов (красный, зеленый, синий, alhpa). Альфа - прозрачность растрового изображения. Это занимает много памяти - изображение X 4. Так что если изображение будет 4 мегапикселя, 16 мегабайт будет сразу же распределено на куче - быстро изнурительная память.

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

Итак - к вашему методу decodeSampledBitmapFromResource - добавьте следующие фрагменты:

 options.inPreferredConfig = Config.RGB_565;
 options.inDither = true;

В вашем коде:

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

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

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

 // Decode bitmap with inSampleSize set
 options.inJustDecodeBounds = false;
 options.inPreferredConfig = Config.RGB_565;
 options.inDither = true;
 return BitmapFactory.decodeFile(resId, options);
 }

Литература:

http://developer.android.com/reference/android/graphics/Bitmap.Config.html#ARGB_8888

Ответ 2

У устройств с высоким разрешением, таких как S4, обычно заканчивается память, если у вас нет изображения в соответствующей папке drawable-xxhdpi. Вы также можете поместить свое изображение в drawable-nodpi. Причина, по которой у него закончилось бы воспоминание, если бы ваш образ только в drawable, чтобы андроид масштабировал изображение, думая, что изображение было спроектировано для низкого разрешения.

Ответ 4

Here is how I'm adding each item to the FrameLayout что проблема, код продолжает добавлять и добавлять больше изображений, и не имеет значения, насколько хорошо вы изменяете размер или объем памяти устройства, в какой-то момент у него закончится память. Это потому, что каждое изображение добавляется в память.

Для такого типа ситуаций приложениями является использование ViewGroup, которое может перерабатывать представления. Я не знаю вашего макета, но обычно это ListView, GridView или ViewPager, путем повторного использования представлений, которые вы повторно используете в макете, и при необходимости можете повторно загружать изображения.

В целях загрузки и изменения размеров изображений я настоятельно рекомендую использовать библиотеку Picasso, поскольку она ОЧЕНЬ хорошо написана, проста в использовании и стабильна.

Ответ 5

Вам все равно понадобится управлять растровой памятью, так как я бы не попытался выделить общее пространство более чем в 3 раза по размеру экрана (если вы думаете, что это имеет смысл для поведения прокрутки). Если вы накладываете одно изображение поверх другого, в какой-то момент вы получаете ошибку Out Of Memory. Возможно, вам придется посмотреть на захват предыдущего изображения экрана в виде одного фонового изображения, чтобы убедиться, что вы все еще вписываетесь в доступную память. Или когда новое изображение перекрывает существующее изображение, загружает только и визуализирует видимую часть. Если производительность становится проблемой, вам может потребоваться рассмотреть OpenGL Textures, но проблема с распределением памяти остается прежней.

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

Ответ 6

Использование библиотеки Fresco для загрузки больших изображений позволит избежать этой ошибки. в макете xml

<com.facebook.drawee.view.SimpleDraweeView
    android:id="@+id/my_image_view"
    android:layout_width="1300dp"
    android:layout_height="1300dp"
    fresco:placeholderImage="@drawable/my_drawable"
  />

и в javacode

Uri uri = Uri.parse("https://image.png");
SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
draweeView.setImageURI(uri);