Android setBackgroundResource cause out of memory

2019-01-13 14:11发布

问题:

I'm working on a gamebook application that shows 12 views in a ViewPager. This is my custom PagerAdapter :

private class ImagePagerAdapter extends PagerAdapter {

    private int[] mImages = new int[] { R.drawable.copertinai,
            R.drawable.blui, R.drawable.azzurroi, R.drawable.rossoi,
            R.drawable.gialloi, R.drawable.verdei, R.drawable.rosai,
            R.drawable.grigioi, R.drawable.neroi, R.drawable.arancionei,
            R.drawable.marronei, R.drawable.violai, R.drawable.ulm };

    @Override
    public int getCount() {
        return mImages.length;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == ((RelativeLayout) object);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Context context = MainActivity.this;
        RelativeLayout relLayImageView = new RelativeLayout(context);
        relLayImageView.setBackgroundResource(mImages[position]);

        ((ViewPager) container).addView(relLayImageView, new LayoutParams(
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
        return relLayImageView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        ((ViewPager) container).removeView((RelativeLayout) object);
        object=null; 
        System.gc();
    }
}

In some devices it cause an out of memory exceptionr randomly when this line of code is called

relLayImageView.setBackgroundResource(mImages[position]);

But in all devices I see something like this in the logcat when i turn pages:

12-31 00:25:31.655: I/dalvikvm-heap(9767): Grow heap (frag case) to 50.875MB for 10384016-byte allocation

The app also crashes in some devices for the same problem when in another Activity I set different background resource to the main layout based on user action. Here the code:

            btn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {


                colorButtons.get(indiceColoreAttuale).setBackgroundResource(
                                unSelectedColorsRes[indiceColoreAttuale]);

                switch (index) {
                case 0:
                    mainLayout.setBackgroundResource(R.drawable.blus);
                    break;
                case 1:
                    mainLayout
                            .setBackgroundResource(R.drawable.azzurros); 
                    break;
                case 2:
                    mainLayout
                            .setBackgroundResource(R.drawable.rossos);
                    break;
                case 3:
                    mainLayout
                            .setBackgroundResource(R.drawable.giallos);
                    break;
                case 4:
                    mainLayout
                            .setBackgroundResource(R.drawable.verdes);
                    break;
                case 5:
                    mainLayout
                            .setBackgroundResource(R.drawable.rosas);
                    break;
                case 6:                     
                    mainLayout
                            .setBackgroundResource(R.drawable.grigios);
                    break;
                case 7:
                    mainLayout
                            .setBackgroundResource(R.drawable.neros);
                    break;
                case 8:
                    mainLayout
                            .setBackgroundResource(R.drawable.arancios);
                    break;
                case 9:
                    mainLayout
                            .setBackgroundResource(R.drawable.marrones);
                    break;
                case 10:
                    mainLayout
                            .setBackgroundResource(R.drawable.violas);
                    break;
                }

                mainLayout.startAnimation(animationShowTextColor);
                mainLayout.setGravity(Gravity.CENTER_HORIZONTAL);
                indiceColoreAttuale = index;
                colorButtons.get(index).setBackgroundResource(
                        selectedColorsRes[index]);

            }
        });

It runs excepiton again when I call setBackgroundResource() on mainLayout.

I hope you can help me to solve this, thanks in advance!

回答1:

I solved! All your hints were good but the real problem was the "/drawable" folder! I had all the pictures in "/drawable" generic folder that is considered by the system like "/drawable/mdpi", so when I were running in devices with hdpi or more the images were resized, and became too big which cause OutOfMemoryException!

Now i'm using "/drawable-nodpi" to store my images. This folder works like "/drawable" but the images are never resized!



回答2:

Each Android application has limited memory (heap) which can be used by Dalvik VM. It is 32 MB on some devices it is 64 MB. When you set background resource you load that resource on heap. That resource is loaded as Bitmap - it's size is width * height * pixel size. Usualy Bitmaps are loaded as ARGB images which has 4 bytes per pixel. It means that loading 1024x768 image takes 1024*768*4 = 3145728 B = 3072 kB = 3 MB on heap. When you load lot of large images it consumes all free heap memory and causes out of memory exception.

To fix this it is better to load images as small as you can - when you are displaying thumbnail of image it is sufficient to load it in resolution which is not much larger than is resolution of concrete part of display. It means that when you display your image on 800x600 display it is not sufficient to load 1024x768 image. You can use BitmapFactory to load image in smaller resolution.

Use method decodeResource(activity.getResources(), R.drawable.imageId, opts). BitmapFactory.Options opts has parameter inSampleSize where you can set subsampling of image. Also parameter inPreferredConfig can be used to set RGB_565 instead of ARGB_8888 in case when you don't need transparency.