Как загрузить большие изображения в 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)
Чтобы resize изображения, я делаю это:
- Как установить время на устройство программно
- Контекстные стили Actionbar
- как читать значение из string.xml в android?
- Android: Как получить уровень сигнала GSM для всех доступных сетевых операторов
- findViewById () возвращает null для настраиваемого компонента в макете XML, а не для других компонентов
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 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); } }
- Загрузка Android из URL в Bitmap
- Недостаточно памяти при создании растровых изображений на устройстве
- Как создать пользовательскую кнопку формы с селектором в android?
- Android java.util.concurrent.TimeUnit Конвертирует миллисекунды в минуты
- Android ImageView настраивает высоту и ширину установки родителя
- Намерение запустить приложение часов на Android
- Отслеживание продолжительности звонка
- При выходе из системы очистите стек истории событий, не позволяя кнопке «Назад» открывать только зарегистрированные.
Вы можете использовать другой bitmap-config для значительного уменьшения размера изображений. По умолчанию используется RGB-config ARGB8888, что означает использование четырех 8-битных каналов (красный, зеленый, синий, alhpa). Альфа – прозрачность растрового изображения. Это занимает много памяти – изображение X 4. Так что если изображение будет 4 мегапикселя, 16 мегабайт будет сразу же распределено на куче – быстро изнурительная память.
Вместо этого используйте RGB_565, которые в какой-то мере ухудшают качество, но чтобы компенсировать это, вы можете сгладить изображения.
Итак – к вашему методу decodeSampledBitmapFromResource – добавьте следующие fragmentы:
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
У устройств с высоким разрешением, таких как S4, обычно заканчивается память, если у вас нет изображения в соответствующей папке, которая является drawable-xxhdpi
. Вы также можете поместить свое изображение в drawable-nodpi
. Причина, по которой она закончилась бы из воспоминаний, если бы ваш образ просто мог бы сделать это, чтобы андроид масштабировал изображение, думая, что изображение было спроектировано для низкого разрешения.
Вы можете использовать эту красивую библиотеку https://github.com/davemorrissey/subsampling-scale-image-view
Here is how I'm adding each item to the FrameLayout
, это проблема, код продолжает добавлять и добавлять больше изображений, и не имеет значения, насколько хорошо вы изменяете размер или объем памяти устройства, в какой-то момент он закончится Память. Это потому, что каждое изображение, которое вы добавляете, хранится в памяти.
Для такого типа ситуаций приложениями является использование ViewGroup, которое может перерабатывать представления. Я не знаю вашего макета, но обычно это ListView
, GridView
или ViewPager
, перерабатывая представления, которые вы повторно используете макет, и при необходимости можете повторно загружать изображения.
Для конкретной цели загрузки и изменения размеров изображений я настоятельно рекомендую использовать библиотеку Picasso, поскольку она ОЧЕНЬ хорошо написана, проста в использовании и стабильна.
Вам по-прежнему потребуется управлять растровой памятью, так как я бы не пытался выделять общее пространство более чем в 3 раза по размеру экрана (если вы думаете об этом имеет смысл для поведения прокрутки). Если вы накладываете одно изображение поверх другого, в какой-то момент вы получаете ошибку Out Of Memory. Возможно, вам придется посмотреть на захват предыдущего изображения экрана в виде одного фонового изображения, чтобы убедиться, что вы все еще вписываетесь в доступную память. Или когда новое изображение перекрывает существующее изображение, загружает только и визуализирует видимую часть. Если производительность становится проблемой, вам может потребоваться рассмотреть OpenGL Textures, но проблема с распределением памяти остается прежней.
Пройдите через все показ обучения Bitmaps, так как он должен дать вам дополнительные идеи о том, как обращаться с дисплеем.
Использование библиотеки Fresco для загрузки больших изображений позволит избежать этой ошибки. в XML-макете
и в джавакоде
Uri uri = Uri.parse("http://sofru.miximages.com/android/image.png"); SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view); draweeView.setImageURI(uri);