0

I've read so many threads in StackOverflow, but couldn't find the solution for my problem. I've been trying to fix the problem for the past 1 week, but couldn't figure it out. Would be great if someone could help me out,

Here is the problem,

I have nearly 50 images and I make use of ViewPager and Fragments to make the user swipe through the images. All the images are 250*350 dimensions which is not more than 20KB per image. On Swyping 30th-35th image I get OutOfMemory exception.. Here is the stack trace,

09-07 16:30:09.126: E/MessageQueue-JNI(1023): java.lang.OutOfMemoryError
09-07 16:30:09.126: E/MessageQueue-JNI(1023):   at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
09-07 16:30:09.126: E/MessageQueue-JNI(1023):   at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:683)
09-07 16:30:09.126: E/MessageQueue-JNI(1023):   at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:513)
09-07 16:30:09.126: E/MessageQueue-JNI(1023):   at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:541)
09-07 16:30:09.126: E/MessageQueue-JNI(1023):   at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:571)

Here is the code MyGallery.java,

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        List<Fragment> fragments = getFragments();

        pageAdapter = new MyPageAdapter(getSupportFragmentManager(), fragments);
        ViewPager pager = (ViewPager) findViewById(R.id.viewpager);
        pager.setOffscreenPageLimit(2);
        pager.setAdapter(pageAdapter);
     }



private List<Fragment> getFragments() {
    List<Fragment> fList = new ArrayList<Fragment>();
    String picture_book = "";
    for (int i = 1; i <= 50; i++) {
        picture_book = "picture" + i;
        fList.add(MyGallery_Fragment.newInstance(picture_book));
    }
    return fList;
}

private class MyPageAdapter extends FragmentStatePagerAdapter {
    private List<Fragment> fragments;

    public MyPageAdapter(FragmentManager fm, List<Fragment> fragments) {
        super(fm);
        this.fragments = fragments;
    }

    @Override
    public Fragment getItem(int position) {
        return this.fragments.get(position);
    }

    @Override
    public int getCount() {
        return this.fragments.size();
    }
}

And here is the MyGaller_Fragment class,

public static final MyGallery_Fragment newInstance(String picture) {

        MyGallery_Fragment f = new MyGallery_Fragment();
        contextForDialog = f.getActivity();
        Bundle bdl = new Bundle();
        bdl.putString("picture", picture);

        f.setArguments(bdl);
        return f;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        context = activity.getApplicationContext();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

        picture = getArguments().getString("picture");

        View v = inflater
                .inflate(R.layout.mygallery_fragment, container, false);


        picture_img = (ImageView) v.findViewById(R.id.picture_iv);
        picture_resID = getResources().getIdentifier(picture, "drawable",
                context.getPackageName());


        picture_biticon = BitmapFactory.decodeResource(getResources(),
                picture_resID);
        picture_img.setImageBitmap(picture_biticon);

        return v;
    }
 }

This is what I tried,

  1. I was making use of FragmentPagerAdapter and changed to FragmentStatePagerAdapter as mentioned here.. I thought that FragmentStatePagerAdapter just keeps the fragments' views directly left and right of the currently shown item and destroys the other items automatically.. but that didn't solve the problem,

  2. I even set setOffScreenPageLimit to 2, so that the old fragments will be destroyed.. but that didn't solve either.

  3. I tried destroying Bitmap after assigning the value, but got the same error.

  4. I also tried this code in MyGallery_Fragment class,

    @Override public void onDestroy() { super.onDestroy(); Drawable drawable = picture_img.getDrawable(); if (drawable instanceof BitmapDrawable) { BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; picture_biticon = bitmapDrawable.getBitmap(); picture_biticon.recycle(); } }

  5. I tested the app in Note II and it shows outofmemory between any of the 30th-35th image swipe, but the same code is working fine in my newly bought Moto E.

Would be great if someone could help me out to fix this problem.

Community
  • 1
  • 1
Naveen
  • 1,040
  • 4
  • 15
  • 38

2 Answers2

1

Storing this amount of fragment is bad idea - you shouldn't do that. View pager caches neighbors for animation purposes and it's enough. Instead of frag. list you should consider storing list of models that are needed for building particular views and create Fragments on the fly in your adapter.

Other advices:

Samsung devices handle memory differently than other devices, I had similar problems on S3 and S4. Also you should know that view pager loads by default neighbors of current fragments so if you set off screen limit to 2 that means that viewpager will load also two fragments on the left from current one and two on the right, so you should consider leaving default value (1).

About file size - if it's 30KB in file it doesn't mean that it will use same amount in graphic card memory - it's usually way more. You should consider rescaling those in runtime before pushing it to view as Vino mentioned. Also you can try set android:largeHeap="true" in your manifest application node.

3mpty
  • 1,354
  • 8
  • 16
  • Thanks for your advice. As I'm making use of [FragmentStatePagerAdapter](http://developer.android.com/reference/android/support/v13/app/FragmentStatePagerAdapter.html), I thought that the fragments would be reused. Please let me know what should I change to avoid this? Any tutorials would be really helpful. – Naveen Sep 07 '14 at 12:06
  • Google example: http://developer.android.com/training/animation/screen-slide.html – 3mpty Sep 07 '14 at 12:45
  • Yes I had a look at the example given by you.. Also I [checked this](http://developer.android.com/reference/android/support/v4/app/FragmentStatePagerAdapter.html) out as well - Even in this example, they create new instances of Fragments. I'm in the view that Android will take care of destroying the fragments that is not in the view. Please correct me if my understanding is not correct. – Naveen Sep 07 '14 at 15:21
  • 1
    Yes, unused fragments should be destroyed by GC. If you're storing references to them situation is opposite - GC can't touch them. – 3mpty Sep 08 '14 at 10:57
0

There is an article on loading bitmaps on the developer.android.com site. You can think about it as best practices. You can find it at http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

It talks about different mechanisms for loading bitmaps. Have you considered those?

Looking at your code, you set the off screen limit to 2. But at the same time, you create 50 instances of the fragment and add it to the list. I reckon your code should look like (untested code)

public MyPageAdapter(FragmentManager fm) {
    super(fm);
}

@Override
    public Fragment getItem(int position) {
        String picture_book = "picture" + i;
        return MyGallery_Fragment.newInstance(picture_book);
    }

    @Override
    public int getCount() {
        return 50;
    }
Vino
  • 1,544
  • 1
  • 18
  • 26
  • Thanks.. I already had a look at Android developer site that you had mentioned.. It is about handling large bitmaps, but in my case I have all the images with 250*350 dimensions and not more than 20KB in size. yes I'm creating multiple instances of Fragments, but I don't see any problem if I load just TextView.. The problem is with Imageview only. – Naveen Sep 07 '14 at 11:35