Mobile Development 23 min read

How to Accurately Measure and Reduce Android Bitmap Memory Usage

This article explains Android's DisplayMetrics density values, shows how to calculate a Bitmap's runtime memory footprint using getByteCount, explores the impact of screen density, pixel format, and scaling, and provides practical techniques such as inSampleSize, matrix scaling, and pixel format selection to minimize memory consumption.

Tencent TDS Service
Tencent TDS Service
Tencent TDS Service
How to Accurately Measure and Reduce Android Bitmap Memory Usage

0. Introduction

The article discusses screen density variables in DisplayMetrics: density (the logical density used as a scaling factor for density‑independent pixels) and densityDpi (the screen density expressed in dots‑per‑inch). Their relationship is linear.

density

1

1.5

2

3

3.5

4

densityDpi

160

240

320

480

560

640

Unless otherwise noted, the article refers to densityDpi when discussing "density".

1. How Much Memory Does a Bitmap Occupy?

Android developers often encounter Out‑Of‑Memory (OOM) errors caused by large images. The runtime size can be obtained via:

public final int getByteCount() {
    // int result permits bitmaps up to 46,340 x 46,340
    return getRowBytes() * getHeight();
}

Example: a 522×686 PNG placed in drawable‑xxhdpi on a Samsung S6 occupies 2,547,360 B, which can be retrieved with the method above.

2.1 getByteCount Source

getByteCount

multiplies getHeight() by getRowBytes(). getRowBytes() is implemented in native code:

public final int getrowBytes() {
   if (mRecycled) {
          Log.w(TAG, "Called getRowBytes() on a recycle()'d bitmap! This is undefined behavior!");
   }
   return nativeRowBytes(mFinalizer.mNativeBitmap);
}

The native function ultimately calls SkBitmap::rowBytes(), which returns width * bytesPerPixel. For the common ARGB_8888 format, each pixel occupies 4 bytes, so rowBytes = 4 * width.

Thus the memory formula is:

bitmapInRam = width × height × 4 bytes

2.2 Influence of Density

When loading resources with BitmapFactory.decodeResource, Android performs a density‑to‑target‑density scaling. The original resource density ( inDensity) comes from the resource folder (e.g., xxhdpi = 480), while the target density ( inTargetDensity) is the device's DisplayMetrics.densityDpi (e.g., Samsung S6 = 640).

public static Bitmap decodeResourceStream(Resources res, TypedValue value,
    InputStream is, Rect pad, Options opts) {
    if (opts == null) opts = new Options();
    if (opts.inDensity == 0 && value != null) {
        int density = value.density;
        if (density == TypedValue.DENSITY_DEFAULT) {
            opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
        } else if (density != TypedValue.DENSITY_NONE) {
            opts.inDensity = density; // e.g., 480 for xxhdpi
        }
    }
    if (opts.inTargetDensity == 0 && res != null) {
        opts.inTargetDensity = res.getDisplayMetrics().densityDpi; // e.g., 640
    }
    return decodeStream(is, pad, opts);
}

The scaling factor is scale = targetDensity / density. The decoded bitmap is then resized:

if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
    scaledWidth = int(scaledWidth * scale + 0.5f);
    scaledHeight = int(scaledHeight * scale + 0.5f);
}

For the 522×686 PNG in xxhdpi on a Samsung S6, the calculation becomes:

(522 / 480) × 640 × (686 / 480) × 640 × 4 = 2,546,432 B , which matches the measured 2,547,360 B after rounding.

2.3 Precision Issues

The final bitmap size is derived from the integer‑rounded scaled dimensions, leading to a small discrepancy between the theoretical formula and the actual getByteCount() result.

3. Reducing Bitmap Memory Footprint

3.1 JPEG vs PNG

File size differences between JPEG (lossy) and PNG (lossless) do not affect runtime memory usage; both are decompressed to the chosen pixel format. JPEG lacks an alpha channel, so if decoded as RGB_565 it can use half the memory of ARGB_8888.

3.2 Using inSampleSize

When the original image is larger than needed, set options.inSampleSize = 2 to load an image at one‑quarter the pixel count, reducing memory and processing time.

BitmapFactory.Options options = new Options();
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId, options);

3.3 Scaling with Matrix

For small images that need to be displayed larger, keep the sampled bitmap and apply a Matrix to scale during drawing, preserving the reduced memory footprint.

Matrix matrix = new Matrix();
matrix.preScale(2, 2, 0f, 0f);
canvas.concat(matrix);
canvas.drawBitmap(bitmap, 0, 0, paint);

3.4 Choosing an Appropriate Pixel Format

Common Android bitmap configs:

ALPHA_8

Alpha only

ARGB_4444

Deprecated, low quality

ARGB_8888

4 bytes per pixel (default)

RGB_565

2 bytes per pixel, no alpha

If alpha is not required (e.g., JPEG sources), using RGB_565 cuts memory in half.

3.5 Indexed Bitmaps (Experimental)

Skia supports an indexed format where each pixel occupies 1 byte, but Android's Java API does not expose it. Attempting to decode an indexed PNG with Options.inPreferredConfig = Config.RGB_565 on a Nexus 6 yields a null config and a byte count of 36,864 B (one‑quarter of ARGB_8888), demonstrating the potential memory savings despite lack of official support.

3.6 When Images Are Unnecessary

For simple loading animations consisting of a few frames, consider drawing shapes programmatically in a custom view instead of loading multiple PNG assets.

4. Conclusion

The article covered how to obtain a Bitmap's runtime memory size, how density, pixel format, and scaling affect that size, and offered practical tips—such as using inSampleSize, matrix scaling, and appropriate configs—to reduce memory consumption in Android apps.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AndroidMemory OptimizationBitmapSkiaDisplayMetricsinSampleSize
Tencent TDS Service
Written by

Tencent TDS Service

TDS Service offers client and web front‑end developers and operators an intelligent low‑code platform, cross‑platform development framework, universal release platform, runtime container engine, monitoring and analysis platform, and a security‑privacy compliance suite.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.