androidout-of-memoryviewanimator

Android ViewAnimator and OutOfMemoryError


this is the situation: I have a ViewAnimator with three different layouts. Each layout have a different backgroundimage. For xhdpi devices, the images are 1280x800 px large and have a size of ~150KB. Now the problem: when I start the emulator with a size of 1280x800 and a density of 320dp, I always receive this exception:

09-02 08:22:46.408: D/dalvikvm(1291): GC_EXTERNAL_ALLOC freed 428K, 51% free 2874K/5831K, external 18484K/20407K, paused 83ms
09-02 08:22:46.419: E/dalvikvm-heap(1291): 1366400-byte external allocation too large for this process.
09-02 08:22:46.498: I/dalvikvm-heap(1291): Clamp target GC heap from 25.089MB to 24.000MB
09-02 08:22:46.498: E/GraphicsJNI(1291): VM won't let us allocate 1366400 bytes
09-02 08:22:49.208: E/AndroidRuntime(1291): java.lang.OutOfMemoryError: bitmap size exceeds VM budget

Have anybody a solution or a workaround for my problem? I read something about the BitmapFactory. Does it help? Best Regards!

Edit: Here is my code: The layout file main_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" >

<ViewAnimator
    android:id="@+id/viewAnimator"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

   <LinearLayout
        android:id="@+id/LayoutMain"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"
        android:background="@drawable/background"
        ...some Buttons etc. ...
   </LinearLayout>

   <LinearLayout
        android:id="@+id/layoutGameMenu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"
        android:background="@drawable/gameMenu"
        ...some Buttons etc. ...
   </LinearLayout>

   <LinearLayout
        android:id="@+id/layoutHighscores"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="@drawable/highscores"
        ...some ListViews etc. ...
   </LinearLayout>

</ViewAnimator>
</LinearLayout>

And the MainActivity:

private ViewAnimator va;   

@Override
public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main_layout);
     va = (ViewAnimator) findViewById(R.id.viewAnimator);
     ....
}

....
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if(event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
        if (va.getCurrentView() == findViewById(R.id.layoutHighscores)) {
            va.setInAnimation(inFromRight);
            va.setOutAnimation(outToLeft);
            va.setDisplayedChild(0);
            return true;
        }
        if (va.getCurrentView() == findViewById(R.id.layoutGameMenu)) {
            va.setInAnimation(inFromLeft);
            va.setOutAnimation(outToRight);
            va.setDisplayedChild(0);
            return true;
        }
    } 
    return super.onKeyDown(keyCode, event);
}

With this, I can switch between the mainmenu, the highscores and the gamemenu with buttons to start a gamemode. I choose this because I want the "cool" view animations.

I have tried to solve it with the BitmapFactory methods, but there are no changes.

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 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) {
    if (width > height) {
        inSampleSize = Math.round((float)height / (float)reqHeight);
    } else {
        inSampleSize = Math.round((float)width / (float)reqWidth);
    }
}
return inSampleSize;
}

Solution

  • There is a lesson that covers this situation in the Android Training section of the main Android site. The lesson, Displaying Bitmaps Efficiently, is split into four sections, includes working examples, and supports API level 7 and up (I assume this isn't a problem.)

    If that doesn't help I'd suggest posting some code so more specific help can be given. ;-)