I am developing an application for the Galaxy S4. One of the requirements of the application is having a SplashScreen containing an Image of 1920x1080 pixels. Its a high quality .jpeg image and the size of the Image is about 2 Megabytes.

The problem is that I am getting an OutOfMemoryError as soon as I start the Application. I am quite stunned that this already happens with an image of just 2 megabyte in size? How can I fix this problem and display the image?

Changing the dimensions or the size of the image is not an option.

SplashScreen.java

public class Splashscreen extends Activity {

private static final int SPLASH_DURATION = 2000;
private boolean isBackPressed = false; 

@Override
protected void onCreate(Bundle savedInstanceState) {

    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(LayoutParams.FLAG_FULLSCREEN, LayoutParams.FLAG_FULLSCREEN);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.splashscreen);

    Handler h = new Handler();

    h.postDelayed(new Runnable() {

        @Override
        public void run() {

            // check if the backbutton has been pressed within the splash_duration
            if(!isBackPressed) { 

                Intent i = new Intent(Splashscreen.this, MainActivity.class);
                i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                Splashscreen.this.startActivity(i);
                overridePendingTransition(R.anim.short_fade_in, R.anim.short_fade_out);
            }       

            finish();
        }
    }, SPLASH_DURATION);
}

@Override
public void onBackPressed() {

    isBackPressed = true;
    super.onBackPressed();
}
}

And the splashscreen.xml

    <ImageView 
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/ivSplashScreenImage"
        android:layout_width="match_parent"
        android:layout_height="match_parent" 
        android:scaleType="fitXY"
        android:src="@drawable/high_res_splashscreen_image"/>

ADDITIONAL INFORMATION:

Sometimes, (when a lot of device memory is available) the app is able to make it past the splash screen, but then, the memory consumption of the app is just insane. (around 100 megabyte). Even though I close the SplashScreen Activity and finish() it, it seems that there is a reference to the ImageView / the Image kept in memory.

How can I reduce the huge memory consumption?

When I do not display the splash screen, my app only consumes around 35MB of memory. With the SplashScreen image, its around 100MB.


Solution 1:

Three hints which should help you:

  1. Use this to load your images, from Loading Large Bitmaps Android documentation:

    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) {
    
            // Calculate ratios of height and width to requested height and width
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
    
            // Choose the smallest ratio as inSampleSize value, this will guarantee
            // a final image with both dimensions larger than or equal to the
            // requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }
    
        return inSampleSize;
    }
    
  2. Make sure you have only one instance of your Bitmap in memory. After displaying it, call recycle() and set your reference to null. You can use Memory Allocation Tracker to see what is allocated. You can also read HPROF files, as suggested in comments.

  3. By default ARGB_8888 pixel format is used, which means 4 bytes per pixel. Very good article: Bitmap quality, banding and dithering. Your image is JPEG, so it doesn't have transparency, so you are wasting 1 byte on every pixel for alpha channel. It's not very probable, but maybe with acceptable quality you can use even more economical format. Take a look at them. Maybe RGB_565 for example. It takes 2 bytes for pixel, so your image would be 50% lighter. You can enable dithering to improve the quality of RGB_565.

Solution 2:

I had a similar problem and the solution is simple. Put your high resolution 1920x1080 px image in the drawable-nodpi directory. The system does not scale resources tagged with this qualifier regardless of the current screen's density, which leads to the OutOfMemoryError, so the problem should disappear.

Please check Android documentation: http://developer.android.com/intl/es/guide/practices/screens_support.html

Hope it helps.