2

I made a ViewPager to display images. When I advance some pages I get an java.lang.OutOfMemoryError: bitmap size exceeds VM budget error.

There are more questions about this issue but I did not find the solution (BitMap.Recycle, System.gc() etc). If you have a suggestion or solution please let me know!

The PNG's are 628KB, 478KB, 587KB, 132KB, 139KB, 149KB, 585KB (crash).

If you have an other solution (scroll images like pages) for me let met know!

My code:

package nl.ipear.vngmagazine;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Environment;
import android.os.Parcelable;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;

public class ShowMagazine2 extends FragmentActivity {
    private ViewPager myPager;
    private MyPagerAdapter myPagerAdapter;

    private static int NUM_PAGES = 15;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.showmagazine);

        myPager = (ViewPager)findViewById(R.id.viewpager1);
        myPagerAdapter = new MyPagerAdapter();
        myPager.setAdapter(myPagerAdapter);

        return;
    }

    private class MyPagerAdapter extends PagerAdapter{
        @Override
        public int getCount() {
            return NUM_PAGES;
        }

        @Override
        public Object instantiateItem(View collection, int position) {      
            String location = Environment.getExternalStorageDirectory() + "/MYData/" + "2012-02/";

            // Inflate and create the view
            LayoutInflater layoutInflater = (LayoutInflater) collection.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View view = layoutInflater.inflate(R.layout.magazinepageview, null);

            ImageView imageView = (ImageView) view.findViewById(R.id.magazinePageImage);
            String fileName = String.format("%s%s%02d%s", location, "2012-02_Page_", position + 1, ".png");
            Log.v("PNG", fileName);
            imageView.setImageBitmap(BitmapFactory.decodeFile(fileName));

            ((ViewPager) collection).addView(view,0);

            return view;
        }

        @Override
        public void destroyItem(View collection, int position, Object view) {
            ((ViewPager) collection).removeView((View) view);           
        }

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

        @Override
        public void finishUpdate(View arg0) {}

        @Override
        public void restoreState(Parcelable arg0, ClassLoader arg1) {}

        @Override
        public Parcelable saveState() {
            return null;
        }

        @Override
        public void startUpdate(View arg0) {}   
    }
}
Neysor
  • 3,893
  • 11
  • 34
  • 66
Hans
  • 43
  • 1
  • 8
  • What is the size (pixels, not KB) of the bitmap? and does it crash at every size? (had this problem too, but I need a little more info) – Bigflow Mar 19 '12 at 15:16
  • Thanks for your reply, the images are aprox 1240x1584. These images are generated from a PDF and are for Phone and Tablet! – Hans Mar 19 '12 at 15:33
  • Did you try smaller images? like 300 x 300. Because it is possible to "cut" images in smaller chunks, and decode those chunks, then later you can show it as a full image. – Bigflow Mar 20 '12 at 07:20
  • @Hans were you able to solve the issue, please accept the answer which helped to solve the issue or if you solved the prob by yourselves, please do share the solution. – suresh cheemalamudi Feb 25 '13 at 10:12
  • @suresh cheemalamudi I did not solve this issue. The only 'solution' i found was to change the image to a lower resolution. – Hans Mar 10 '13 at 15:50

5 Answers5

1

Found the answer in here: Android:java.lang.OutOfMemoryError: Failed to allocate a 23970828 byte allocation with 2097152 free bytes and 2MB until OOM

Just add android:hardwareAccelerated="false" and android:largeHeap="true" in your Application tag inside AndroidManifest:

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:hardwareAccelerated="false"
        android:largeHeap="true">
Community
  • 1
  • 1
dianakarenms
  • 2,609
  • 1
  • 22
  • 22
1

It would appear that your ViewPager is loading in the Bitmaps, but not releasing any when you are scrolling through.

I would advise you limit the number of Pages that are available either side of the current viewable Page, which will allow the system to clean up the Bitmaps in the other Pages.

Make sure you are recycling the Bitmaps when your Activity/Fragment is destroyed to help with OOM issues.

Mimminito
  • 2,803
  • 3
  • 21
  • 27
  • How can I limit the pages? Currently there are three images loaded. – Hans Mar 19 '12 at 18:32
  • There must be a memory leak or a bug in Android because I release memory in destroyItem. I really think I need to use an other approach because I can not fix the error. – Hans Mar 19 '12 at 18:47
  • @Hans Hi, even I am in the same problem.. How did you finally resolve it ? – Y M Feb 25 '15 at 09:07
1

Yes follow Mimminito. I advice 2 pages since rendering is equally fast for back and forth for 3 pages.

Now if you need the images without lag and the images are on the internet. Make sure you download it and put inside an internalStorageDevice then reuse it if it exist.

my top answer is maybe incorrect for the issue of a memory leak. This new answer is a probable reason for the memory leak is because of the height and width of the image.

private byte[] resizeImage( byte[] input ) {

    if ( input == null ) {
        return null;
    }

    Bitmap bitmapOrg = BitmapFactory.decodeByteArray(input, 0, input.length);

    if ( bitmapOrg == null ) {
        return null;
    }

    int height = bitmapOrg.getHeight();
    int width = bitmapOrg.getWidth();
    int newHeight = 250;

    float scaleHeight = ((float) newHeight) / height;

    // creates matrix for the manipulation
    Matrix matrix = new Matrix();
    // resize the bit map
    matrix.postScale(scaleHeight, scaleHeight);

    // recreate the new Bitmap
    Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0,
            width, height, matrix, true);

    bitmapOrg.recycle();

    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    resizedBitmap.compress(CompressFormat.PNG, 0 /*ignored for PNG*/, bos);            

    resizedBitmap.recycle();

    return bos.toByteArray();            
}       

This top code is for rescalling a bitmap "newHeight" is the new height given to the bitmap.

Now if this doesn't work I am now 100% certain that OP must be overloading the ViewPager with a more views that the android memory can handle. The solution for this is to use a ViewFlipper and use the answer I stated above.

sdfwer
  • 1,042
  • 3
  • 10
  • 23
  • The images are stored on external storage, even with setOffscreenPageLimit set to 1 it gives a memory error!? – Hans Mar 19 '12 at 18:44
  • Alright I think the scaling might be wrong if it is too big for its size. I will repost a code on my answer test this only on one page. Then afterwards set it to onflicklistener so that whenever it is flicked it renders a new image on the second page. – sdfwer Mar 19 '12 at 19:26
1

Try and scale down your Bitmap, it really solves the issue, here is how you do it..

BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile( filename, options );
        options.inJustDecodeBounds = false;
        options.inSampleSize = 2; 

        bitmap = BitmapFactory.decodeFile( filename, options );
        if ( bitmap != null && exact ) {
            bitmap = Bitmap.createScaledBitmap( bitmap, width, height, false );
        }

Adjust sample size to whatever you want it to be e.g. 4, 8 etc

Arif Nadeem
  • 8,524
  • 7
  • 47
  • 78
  • When I scale down the images it will work but users must be able to zoom in. Will this work when I scale the image down? – Hans Mar 19 '12 at 15:32
  • try it out yourself by varying sample size, it will be fine as long as you are happy with image quality .. – Arif Nadeem Mar 19 '12 at 15:45
  • I hope it is possible to "re-load" the images when a user zooms-in. – Hans Mar 19 '12 at 18:30
0

call System.gc() in oncreate()

ro1
  • 1
  • 1
    Do not do this. Please, never try to control GC manually. Most likely you'll get worse results than if you don't. – Alexey Malev May 05 '14 at 08:15