0

I need to set a background image in my app. I have used an imageview and am trying to set its background programatically. I am getting the 'out of memory error'. I read other posts on SO and have changed my code to get image only as per the screen height and width. I have tried a few other things but still keep getting the same error. Kindly help.

Thanks.

    @Override
protected void onResume() {
    super.onResume();

    ivBackground.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.drawable.themes, getDisplayWidth(), getDisplayHeight()));
    ivBackground.setScaleType(ScaleType.CENTER_CROP);
}

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



public int getDisplayHeight(){
    DisplayMetrics dm = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(dm);
    return dm.heightPixels;
}

public int getDisplayWidth(){
    DisplayMetrics dm = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(dm);
    return dm.widthPixels;
}



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

xml layout

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
    <ImageView android:id="@+id/ivBackground"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"/>

    <android.support.v4.view.ViewPager

        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainPage"
         />
ambit
  • 1,099
  • 2
  • 28
  • 43
  • Do you trying to download the image in parallel in Asynctask or you are trying to store in a local variable..? – Naveen Kumar Kuppan Mar 29 '14 at 08:29
  • 1
    why setting image in onResume() ?? it called many times as per activity lifecycle – Manmohan Badaya Mar 29 '14 at 08:34
  • I am doing it in onresume() because the background image is set as per the theme of the app. The user can select a theme from anywhere in the app. Whenever the user comes back to the page, we need to check the theme and set the background accordingly. I have not pasted all the details in the code above. If I set the background image in oncreate, the background does not change everytime the user comes back to the activity – ambit Mar 29 '14 at 08:52
  • u should do it with check instead of setting directly. – Manmohan Badaya Mar 29 '14 at 10:12
  • @Manmohan. sorry did not get as to how to do it with check.. – ambit Mar 29 '14 at 10:44
  • any boolean to check the need to set bitmap or not. can be done using SharedPrefrence (one among alternative). – Manmohan Badaya Mar 29 '14 at 13:04

5 Answers5

2

i don't have "reputation" to post a comment so posting an answer. here is what i think is happening. activity is starting first time so imageview don't have anything to display.

  • onResume a bitmap is binded to imageview
  • onPause activity pauses
  • OnResume again now here's tricky part. as activity is not getting destroyed(i'm assuming, you have to check) that bitmap is still bound to imageView. and when this line is called "ivBackground.setImageBitmap" it will call "decodeSampledBitmapFromResource" method to get a bitmap according to screen size. which eventually create a bitmap that is reduced to screen dimension , ergo there will be 2 bitmaps loaded for brief amount of time in memory. and as you are loading bitmap for full screen size that can take 20-25 mb per bitmap in memory . hence this may create "out of memory" situation.

solution : setting setImageBitmap null in onPause method worked for me .

Ankit
  • 270
  • 1
  • 4
  • 12
2

I solved this error by doing this - in my case, the image dimension was way too big for my phone so I had to resize it dynamically before I could load it into the imageview.

This is what I did:

STEP 1:

I found out what my screen resolution is by:How do I get the ScreenSize programmatically in android

    WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();
    DisplayMetrics metrics = new DisplayMetrics();
    display.getMetrics(metrics);
    int width = metrics.widthPixels;
    int height = metrics.heightPixels;

STEP 2:

I then dynamically resize the bitmap:

    Bitmap bm = drawableToBitmap(ContextCompat.getDrawable(this, R.drawable.cloud_plane));
    Bitmap bitmapsimplesize = Bitmap.createScaledBitmap(bm, width, height, true);
    bm.recycle();
    background.setImageBitmap(bitmapsimplesize);

The drawableToBitmap method can be found here:How to convert a Drawable to a Bitmap?

public static Bitmap drawableToBitmap (Drawable drawable) {
    Bitmap bitmap = null;

    if (drawable instanceof BitmapDrawable) {
        BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
        if(bitmapDrawable.getBitmap() != null) {
            return bitmapDrawable.getBitmap();
        }
    }

    if(drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
        bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel
    } else {
        bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    }

    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);
    return bitmap;
}
Community
  • 1
  • 1
Simon
  • 19,658
  • 27
  • 149
  • 217
1

Just put largeHeap="true" in application tag (AndroidManifest.xml)

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:largeHeap="true"
    android:theme="@android:style/Theme.Black.NoTitleBar" >

I think it will help you.
Thank you.

Darshak
  • 2,298
  • 4
  • 23
  • 45
  • 1
    large the heap more frequent garbage collection. So `android:largeHeap="true"` should be used carefully – Raghunandan Mar 29 '14 at 08:33
  • @Raghunandan Then what is the alternative ? Because it needs more heap memory. – Darshak Mar 29 '14 at 08:37
  • 6
    and do read http://developer.android.com/training/articles/memory.html . Quoting the docs However, the ability to request a large heap is intended only for a small set of apps that can justify the need to consume more RAM (such as a large photo editing app). **Never request a large heap simply because you've run out of memory and you need a quick fix—you should use it only when you know exactly where all your memory is being allocated and why it must be retained**. – Raghunandan Mar 29 '14 at 08:45
1

Add below code on Activity :

  @Override
        public void onDestroy(){

                bitmap.recycle();

            System.gc();
            super.onDestroy();

        }
BradleyDotNET
  • 60,462
  • 10
  • 96
  • 117
  • I am not sure how do i use bitmap.recycle(). I am not using bitmap directly. Kindly help – ambit Mar 29 '14 at 09:53
  • ivBackground.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.drawable.themes, getDisplayWidth(), getDisplayHeight())); ivBackground.setScaleType(ScaleType.CENTER_CROP); – Neha - Systematix Mar 29 '14 at 10:00
  • Bitmap bitmap=decodeSampledBitmapFromResource(getResources(), R.drawable.themes, getDisplayWidth(), getDisplayHeight());ivBackground.setImageBitmap(bitmap); ivBackground.setScaleType(ScaleType.CENTER_CROP); – Neha - Systematix Mar 29 '14 at 10:02
  • am still not sure about where is the recycle part in the code you typed.. thanks for your suggestion though. I have given an upvote to your answer.. – ambit Mar 29 '14 at 10:46
  • Thanks.. recycle() is the inbuild function of Bitmap class.It is use to refresh bitmap you need to refresh bitmap when activity destroy and in onResume() – Neha - Systematix Mar 29 '14 at 11:03
0

I added the following to my above code and got it to work. Its more or less related to what other individuals have mentioned here. So, thanks everyone for the help.

@Override
protected void onDestroy() {
    System.gc();
    super.onDestroy();
    ivBackground.setImageBitmap(null);
}
ambit
  • 1,099
  • 2
  • 28
  • 43