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

Проблема с отсутствием памяти Ошибка 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)
4b9b3361

Ответ 1

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

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

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

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

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

@Override
public void onDestroy() {
    super.onDestroy();

    imageView.setImageDrawable(null);
}

Ответ 2

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

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

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

Ответ 3

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

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

    <ImageView android:id="@android:id/icon"
           android:layout_width="@dimen/width"
           android:layout_height="@dimen/height"
           android:adjustViewBounds="true" 
           android:scaleType="fitXY" 
          <!-- DON'T USE "fitXY"! -->
    />
    

    Измените 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 на некоторых этапах вашего приложения, используя: 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 предоставляет инструменты для определения проблем с потреблением памяти в вашем приложении.
  • Наконец, проверьте страницу проблемы на Glide GitHub, по тем же вопросам, которые могут помочь вам в определении вашей проблемы. Репо управляется очень хорошо, и они очень полезны.

Ответ 4

Изображения бывают всех форм и размеров. Во многих случаях они больше чем требуется для типичного пользовательского интерфейса приложения (UI). Для Например, приложение системы Галерея отображает фотографии, сделанные с использованием ваша камера 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;
    }

Ответ 5

Вы можете оставить его в сторонних библиотеках, таких как 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'

Квадрат Пикассо тоже делает это Пикассо загружает ресурсы из своего URI

Ответ 6

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

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

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

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 в свой класс "Методы" (любым другим полезным методам):

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

}

Ответ 7

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

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

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

Ответ 8

У меня была такая же проблема, когда я показывал большое изображение в режиме просмотра изображений в режиме 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);
         }