Tek Eye Logo

Tek Eye

Android Bitmap Loading for Efficient Memory Usage

When handling bitmaps Android developers can come across the error java.lang.OutOfMemoryError: bitmap size exceeds VM budget. This usually occurs when the bitmaps are large (several megabytes uncompressed), also when running the code on older devices, or trying to load lots of bitmaps. This occurs because Android was design for mobile devices, which may have limited hardware resources, including memory, and bitmaps can require large amounts of memory.

Load a Bitmap from Resources Correctly

Despite Android not dealing with multi-megabyte bitmap loading automatically, it can still be done. Instead of loading the whole bitmap a reduced resolution version is loaded (via a technique called subsampling). Android bitmap loading is achieved via the BitmapFactory class. The Options settings passed to the class can reduce the size of the bitmap, saving on memory usage.

Bitmaps in Android

(For this Android tutorial try the code in a simple app. This article assumes that the latest Android Studio is installed, a basic app can be created and run, here called Bitmap Loading, and the code in this article can be correctly copied into Android Studio. The example code can be changed to meet your own requirements. When entering code in Studio add import statements when prompted by pressing Alt-Enter. The code for the Bitmap Loading demo project used in this article is available for importing into Android Studio.)

Bitmaps can be BIG but Android Screens can be Small

A digital image from a camera on an Android voice can vary in size, between 5 and 18 megapixels (MP) is possible (and larger). This is the uncompressed size. The file storing the image, usually a JPEG file, is smaller than the displayed image because of compression. High end cameras can produce 20 MP or higher images. A 5 MP image can have a resolution of 2560×1920, that is greater than high definition (HD) television at 1920×1080, and bigger than the resolution of the Goggle Nexus 10 tablet device at 2560×1600. What about an 18 MP image? A massive 5184×3456. The Nexus 5 phone has a HD screen (1920×1080), so why load more than 2 MP of data if the screen can only display a maximum of 2 MP.

Bitmap Subsampling to the Rescue

Using a technique called subsampling the huge photo image being viewed on the Android device only consumes as much memory as required to display it, 2 MP for a HD resolution screen. This saves precious app memory and reduces the possibility of out of memory errors. Here’s an example of loading large bitmaps on different Android devices. A 1000×1000 placeholder image was loaded into an ImageView and the app run on two devices. One with 480x800 screen, and another with a 320×480 screen.

Different size Android screens

The one million pixel image is only using 230,400 pixels on the larger screen, and 102,400 pixels on the smaller screen. Therefore loading all one million pixels is not required. Loading only the required size for display makes sense.

Android Bitmap Loading Code from the Developer Site

There is an article on the Android Developers site called Loading Large Bitmaps Efficiently that discusses bitmap loading. When processing multiple large bitmaps it needs to be done away from the User Interface (UI) thread, for example using AsyncTask, and use caching when appropriate. The article links to a sample app, DisplayingBitmaps.zip. This same sample is available via Android Studio using the Import Sample option (via the File then New menu option. (In early releases of the Android SDK this example app was previously called Bitmap Fun from the BitmapFun.zip file.)

Android Displaying Bitmaps

The basic steps in loading a large bitmap are:

  1. Determine the required size (from a known size or determining the size of the target View).
  2. Use the BitmapFactory class to get the bitmap’s size (set inJustDecodeBounds in BitmapFactory.Options to true).
  3. Using 1. and 2. calculate the subsampling value (a multiple of 2) and pass it to the BitmapFactory.Options setting inSampleSize.
  4. Use the BitmapFactory to load a reduced size bitmap.

The following example app code performs the steps listed above.

Code to Load an Android Bitmap from a Large Resource Image

A large bitmap is needed. You may be reading this because you have a very large bitmap causing out of memory errors. If not this example code is using a large image of a Turner painting. The image size is 5684×4223, a 24 MP image! You can grab the image from Wikimedia Commons, it is available in several sizes.

Turner Image

The large Turner image was added to the project in a folder called drawable-nodpi under the res folder. A large placeholder image (large enough to match the screen width) was also added to the folder. It can be generated from Placeholder Graphic at openclipart.org. The layout is just an ImageView with a Button below it:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.bitmaploading.MainActivity">
    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@drawable/placeholder_large"
        android:adjustViewBounds="true" />
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/imageView1"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"
        android:text="Load" />
</RelativeLayout>

Here’s the code for the bitmap loading app:

package com.example.bitmaploading;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {
    ImageView mImageView;   //reference to the ImageView
    int xDim, yDim;     //stores ImageView dimensions

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //attach an instance of HandleClick to the Button
        findViewById(R.id.button1).setOnClickListener(new HandleClick());
        //reference the ImageView
        mImageView=(ImageView)findViewById(R.id.imageView1);
    }
    //Button click loads the image
    private class HandleClick implements View.OnClickListener {
        public void onClick(View arg0) {
            //Decode the image from resources
            mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),
                R.drawable.turner_large, xDim, yDim));
        }
    }
    @Override
    //Get the size of the Image view after the
    //Activity has completely loaded
    public void onWindowFocusChanged(boolean hasFocus){
        super.onWindowFocusChanged(hasFocus);
        xDim=mImageView.getWidth();
        yDim=mImageView.getHeight();
    }
    //Load a bitmap from a resource with a target size
    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);
    }
    //Given the bitmap size and View size calculate a subsampling size (powers of 2)
    static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) {
        int inSampleSize = 1;   //Default subsampling size
        // See if image raw height and width is bigger than that of required view
        if (options.outHeight > reqHeight || options.outWidth > reqWidth) {
            //bigger
            final int halfHeight = options.outHeight / 2;
            final int halfWidth = options.outWidth / 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;
    }
}

And here is the app code in action, loading a 24 MP image into a much smaller ImageView:

Large Bitmap Loading in Android

The code used in this tutorial is available in bitmap-loading.zip, ready for importing into Android Studio (select New from the File menu and then Import project). It is also available from the Android Studio example projects page, which has some additional details on importing projects into Android Studio.

Using a Open Source Library to Manage Large Bitmap Loading

This code is good for a single bitmap, however, caching and background loading is important for professional apps. One way of achieving this is to use an existing implementation, such as that provided in the Display Bitmaps sample, or existing libraries that support large bitmap loading with caching, including:

See Also

Archived Comments

Girish on November 5, 2014 at 11:11 am said:

This code also throws OUTOFMEMORYEXCEPTION.

Tek Eye on November 5, 2014 at 12.45 pm said:

Double check that the code has been copied correctly.

Erro on August 3, 2015 at 12:08 am said:

Exeption problem error too, this code don't work any more.

Tek Eye on August 4, 2015 at 10.32 am said:

As per previous comment.

Author:  Published:  Updated:  

ShareSubmit to TwitterSubmit to FacebookSubmit to Google+Submit to LinkedInSubmit to redditPrint Page

markdown CMS Small Logo Icon ↓markdown↓ CMS is fast and simple. Build websites quickly and publish easily. For beginner to expert.



Articles on:

Android Programming and Android Practice Projects, HTML, VPS, Computing, IT, Computer History, ↓markdown↓ CMS, C# Programming, Using Windows for Programming



Free Android Projects and Samples:

Android Examples, Android List Examples, Android UI Examples