21

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!

A--C
  • 36,351
  • 10
  • 106
  • 92
TheModularMind
  • 2,024
  • 2
  • 22
  • 36
  • Sounds like you are loading some _huge_ backgrounds and the device does not have enough memory for them... – K-ballo Dec 31 '12 at 00:14
  • Yes, but sometimes happens on devices with lots of memory, and don't happens in devices with less memory.. I load images that are not really big (from 50 to 200 kb). – TheModularMind Dec 31 '12 at 00:34
  • They don't sound _huge_. You could try `recycle`-ing the bitmaps yourself before changing the background resource. Get the old background drawable and after changing it see if its a `BitmapDrawable`, if it is then call `recycle()` on its `Bitmap`. – K-ballo Dec 31 '12 at 00:35
  • Thanks, I tried that but unfortunatly i got an "IllegaArgumentException: Cannot draw recycled bitmaps" after changing the background resource for the fourth time – TheModularMind Dec 31 '12 at 01:04
  • Image file size often has nothing to do with the amount of memory required (often more so with jpg, but similar with png). The memory requires is based on width, height, and bytes per pixel (memory required = width x height x bytes/pixel). What are the dimensions of the images you are loading? – bobnoble Dec 31 '12 at 02:18

2 Answers2

65

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!

CinetiK
  • 1,748
  • 2
  • 13
  • 19
TheModularMind
  • 2,024
  • 2
  • 22
  • 36
11

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.

tomasbuzek
  • 526
  • 3
  • 13