Проблема с отсутствием памяти Ошибка ImageView

Я новичок в программировании на Android, и я получил сообщение об ошибке, в котором говорится, что у моего приложения закончилась нехватка памяти, и я просмотрел эту книгу, и она работает с разрешением небольших изображений, но когда я добавил несколько снимков с большим разрешением ошибка памяти появляется, может быть, я делаю что-то неправильно или просто не знаю, все, что мне еще нужно, чтобы работать с изображениями, если кто-то знает, что я должен изменить, чтобы эта ошибка больше не появлялась, пожалуйста, помогите. Спасибо, предвидим!

Исходный код:

public class ImageViewsActivity extends Activity { //the images to display Integer[] imageIDs={ R.drawable.pic1, R.drawable.pic2, R.drawable.pic3, R.drawable.pic4, R.drawable.pic5 }; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final ImageView iv=(ImageView) findViewById(R.id.image1); Gallery gallery=(Gallery) findViewById(R.id.gallery); gallery.setAdapter(new ImageAdapter(this)); gallery.setOnItemClickListener(new OnItemClickListener(){ public void onItemClick(AdapterView parent, View v, int position, long id){ Toast.makeText(getBaseContext(), "pic"+(position+1)+" selected", Toast.LENGTH_SHORT).show(); //display the image selected try{iv.setScaleType(ImageView.ScaleType.FIT_CENTER); iv.setImageResource(imageIDs[position]);}catch(OutOfMemoryError e){ iv.setImageBitmap(null); } } }); } public class ImageAdapter extends BaseAdapter{ private Context context; private int itemBackground; public ImageAdapter(Context c){ context=c; //setting the style TypedArray a = obtainStyledAttributes(R.styleable.Gallery1); itemBackground = a.getResourceId(R.styleable.Gallery1_android_galleryItemBackground, 0); a.recycle(); } //returns the number of images public int getCount() { // TODO Auto-generated method stub return imageIDs.length; } //returns the ID of an item public Object getItem(int position) { // TODO Auto-generated method stub return position; } //returns the ID of an item public long getItemId(int position) { // TODO Auto-generated method stub return position; } //returns an ImageView view public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub ImageView iv= new ImageView(context); iv.setImageResource(imageIDs[position]); iv.setScaleType(ImageView.ScaleType.FIT_XY); iv.setLayoutParams(new Gallery.LayoutParams(150,120)); iv.setBackgroundResource(itemBackground); return iv; } }} 

ОШИБКА ЗДЕСЬ:

 04-18 10:38:31.661: D/dalvikvm(10152): Debugger has detached; object registry had 442 entries 04-18 10:38:31.661: D/AndroidRuntime(10152): Shutting down VM 04-18 10:38:31.661: W/dalvikvm(10152): threadid=1: thread exiting with uncaught exception (group=0x4001d820) 04-18 10:38:31.691: E/AndroidRuntime(10152): FATAL EXCEPTION: main 04-18 10:38:31.691: E/AndroidRuntime(10152): java.lang.OutOfMemoryError: bitmap size exceeds VM budget 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.Bitmap.nativeCreate(Native Method) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.Bitmap.createBitmap(Bitmap.java:499) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.Bitmap.createBitmap(Bitmap.java:466) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:371) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:539) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:508) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:365) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:728) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.content.res.Resources.loadDrawable(Resources.java:1740) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.content.res.Resources.getDrawable(Resources.java:612) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.ImageView.resolveUri(ImageView.java:520) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.ImageView.setImageResource(ImageView.java:305) 04-18 10:38:31.691: E/AndroidRuntime(10152): at image.view.GalleryView$ImageAdapter.getView(GalleryView.java:95) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.Gallery.makeAndAddView(Gallery.java:776) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.Gallery.fillToGalleryLeft(Gallery.java:695) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.Gallery.trackMotionScroll(Gallery.java:406) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.widget.Gallery$FlingRunnable.run(Gallery.java:1397) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.os.Handler.handleCallback(Handler.java:618) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.os.Handler.dispatchMessage(Handler.java:123) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.os.Looper.loop(Looper.java:154) 04-18 10:38:31.691: E/AndroidRuntime(10152): at android.app.ActivityThread.main(ActivityThread.java:4668) 04-18 10:38:31.691: E/AndroidRuntime(10152): at java.lang.reflect.Method.invokeNative(Native Method) 04-18 10:38:31.691: E/AndroidRuntime(10152): at java.lang.reflect.Method.invoke(Method.java:552) 04-18 10:38:31.691: E/AndroidRuntime(10152): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:917) 04-18 10:38:31.691: E/AndroidRuntime(10152): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:674) 04-18 10:38:31.691: E/AndroidRuntime(10152): at dalvik.system.NativeStart.main(Native Method) 

Чтобы добавить ответ Кена, который является прочным кодом, мне показалось, что я сбил его после того, как он установил его:

  if(imageView != null) { ((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle(); } imageView = (ImageView) view.findViewById(R.id.imageView); imageView.setImageResource(resID); 

ПРИМЕЧАНИЕ. Это не будет работать, если вы попытаетесь поменять образ, который вы уже переработали. Вы получите что-то подобное в LOGCAT

Холст: попытка использования переработанного растрового изображения

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

 @Override public void onDestroy() { super.onDestroy(); imageView.setImageDrawable(null); } 

использование

 ((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle(); 

Перед изменением на новое изображение!

Для тех, кто использует библиотеку загрузки изображений Glide , которые все еще сталкиваются с этими OutOfMemory Exception , есть много вещей, которые вы можете сделать, чтобы сделать Glide меньше памяти и, надеюсь, исправить вашу проблему. Вот несколько из них:

  • Не используйте android:scaleType="fitXY" внутри вашего ImageView . Итак, если вы ImageView выглядите так:

      /> 

    Измените ImageView для использования другого android:scaleType , желательно: fitCenter или centerCrop .

  • Не используйте wrap_content в ImageView , вместо этого используйте match_parent или укажите width / height явно используя размер в dp . Если вы действительно настаиваете на использовании wrap_content в своем ImageView , по крайней мере, установите android:maxHeight / android:maxWidth .
  • Отключите анимацию с помощью: dontAnimate() в Glide.with()...
  • Если вы загружаете большое количество потенциально больших изображений (как и в списке / сетке), укажите в своем запросе задание thumbnail(float sizeMultiplier) . Пример:

     Glide.with(context) .load(imageUri) .thumbnail(0.5f) .dontAnimate() .into(iconImageView); 
  • Временно уменьшите объем памяти Glide некоторых этапах вашего приложения, используя: Glide.get(context).setMemoryCategory(MemoryCategory.LOW) .

  • Если вам нужна только кеш-память, вы можете отключить ее с помощью: skipMemoryCache(true) в вашем Glide.with()... Это все равно будет кэшировать изображения на диск, которые вы, вероятно, захотите, так как вы отказываетесь от кэша в памяти.
  • Если вы загружаете Drawable из своих локальных ресурсов, убедитесь, что изображение, которое вы пытаетесь загрузить, не является SUPER HUGE. В Интернете доступно множество инструментов сжатия изображений. Эти инструменты уменьшат размеры ваших изображений, сохраняя при этом их внешний вид.
  • Если загрузка из локальных ресурсов использует .diskCacheStrategy(DiskCacheStrategy.NONE) .
  • onTrimMemory(int level) к onTrimMemory(int level) который Android предоставляет для обрезки кеша Glide при необходимости. Ex.

     @Override public void onTrimMemory(int level) { super.onTrimMemory(level); Glide.get(this).trimMemory(level); } 
  • Если вы показываете изображения в RecyclerView вы можете явно очистить Glide когда виды переработаны, например:

     @Override public void onViewRecycled(MyAdapter.MyViewHolder holder) { super.onViewRecycled(holder); Glide.clear(holder.imageView); } 
  • Если это все еще происходит, даже после того, как вы «попробуете все», проблема может быть вашим приложением (GASP!), А Glide – это единственное, что подталкивает его в OutOfMemory Exception … Поэтому убедитесь, что вы У вас нет утечек памяти в вашем приложении. Android Studio предоставляет инструменты для определения проблем с потреблением памяти в вашем приложении.
  • Наконец, проверьте страницу проблемы на GitHub Glide , по тем же вопросам, которые могут помочь вам в определении вашей проблемы. Репо управляется очень хорошо, и они очень полезны.

Изображения бывают всех форм и размеров. Во многих случаях они больше, чем требуется для типичного пользовательского интерфейса приложения (UI). Например, приложение системы Gallery отображает фотографии, сделанные с использованием камеры Android-устройств, которые, как правило, намного выше, чем плотность экрана вашего устройства.

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

Источник: загрузка больших растровых изображений

Основываясь на приведенной выше информации, я бы рекомендовал вам вместо установки изображения следующим образом:

 setImageResource(resId); 

чтобы установить его так:

 setScaledImage(yourImageView, resId); 

и скопируйте и вставьте следующие методы:

  private void setScaledImage(ImageView imageView, final int resId) { final ImageView iv = imageView; ViewTreeObserver viewTreeObserver = iv.getViewTreeObserver(); viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { public boolean onPreDraw() { iv.getViewTreeObserver().removeOnPreDrawListener(this); int imageViewHeight = iv.getMeasuredHeight(); int imageViewWidth = iv.getMeasuredWidth(); iv.setImageBitmap( decodeSampledBitmapFromResource(getResources(), resId, imageViewWidth, imageViewHeight)); return true; } }); } private 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); } private 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; } 

Вы можете оставить его в сторонних библиотеках, таких как Glide

 // imageView.setImageResource(imageId); Glide.with(this) // Activity or Fragment .load(imageId) .into(imageView); 

Вот как добавить его в свой build.gradle :

 compile group: 'com.github.bumptech.glide', name: 'glide', version: '3.7.0' 

Square Picasso делает это тоже Пикассо загружает ресурсы с их URI

Google имеет правильный (идеальный) ответ:

https://developer.android.com/training/displaying-bitmaps/load-bitmap.html

Пример того, как я использую его в fragmentах:

 private ImageView mImageView; private View view; private int viewWidth; private int viewHeight; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = inflater.inflate(R.layout.fragment_episode_list, container, false); mImageView = (ImageView) view.findViewById(R.id.ImageView); ViewTreeObserver viewTreeObserver = view.getViewTreeObserver(); if (viewTreeObserver.isAlive()) { viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { view.getViewTreeObserver().removeOnGlobalLayoutListener(this); viewWidth = view.getMeasuredWidth(); viewHeight = view.getMeasuredHeight(); mImageView.setImageBitmap(Methods.decodeSampledBitmapFromResource(getResources(), R.drawable.YourImageName, viewWidth, viewHeight)); } }); } return view; } 

Я поместил эти методы Google в свой class «Методы» (любым другим полезным методам):

 public class Methods { ... 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; } 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); } } 

Дополнительные примечания к @Sakiboy отвечают. Хотя я, вероятно, слишком поздно для ответа, но вот мое решение, я нашел, что он работает без необходимости делать многое из изменений кода.

  • используйте Glide для обработки всего кэширования.
  • Чтобы очистить больше памяти, вы должны вручную удалить все views и установить любое bitmap ImageView / drawable в null и очистить все обработчики и прослушиватели событий.
  • Установите все переменные, которые у вас есть в вашей activity или fragment null .
  • Вам нужно ввести свою логику в onDestroy и вам должно быть хорошо идти.
  • Дополнительным шагом является добавление System.gc() в конец вашего кода.

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

У меня была такая же проблема, когда я показывал большое изображение в режиме просмотра изображений в режиме LANDSCAPE. поэтому я решил, используя этот код

  File imgFile = new File(imageFile.getAbsolutePath()); // path of your file FileInputStream fis = null; try { fis = new FileInputStream(imgFile); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 8; options.inPurgeable = true; options.inScaled = true; Bitmap bm = BitmapFactory.decodeStream(fis, null,options); profileIV.setImageBitmap(bm); } 
  • из памяти Image.FromFile
  • C #: исключение из памяти
  • Interesting Posts

    Как я могу сделать графики согласованной ширины в ggplot (с легендами)?

    Использование LINQ для конкатенации строк

    Как изменить цвет фона вкладки при использовании TabLayout?

    Рост объема Amazon EBS Объем

    Android с длинным касанием

    Выполнение не выполнено для задачи ‘: app: dexDebug’. com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException

    MongoDB – обновление объектов в массиве документа (nested обновление)

    Схема раздела и размер для двойной загрузки Windows 7 и Ubuntu 9.10 с отдельными разделами для данных и / home

    Поднять событие в интервале высокого разрешения / таймера

    Обновление с помощью соединения в SQLite

    Как использовать InputFilter для ограничения символов в EditText в Android?

    Почему исключение операции перекрестного streamа не выбрасывается при запуске exe в bin \ Debug

    Xpath «заканчивается-с» не работает

    Безопасно ли удалять из C: \ Windows \ Installer?

    Как установить выходной stream в TextArea

    Давайте будем гением компьютера.