跪拜 Guibai
← Back to the summary

Google Play Console Flagged Your Bitmaps — Here Are the Configs That Actually Fix It

Background

This guy at Google really knows how to stir things up. I had just barely managed to bring down the crash and ANR data a bit, and recently I found he gave me another technical quality rectification suggestion. The gist of it is:

screenshot-20260717-111003.png

He really doesn't let me rest, always concerned about whether my KPIs are met, always giving me technical improvements. But then again, this is a bitmap optimization suggestion. It says my project needs to use an image loading library to let them automatically handle downsampling, caching, and memory management. Isn't Glide in my project already doing these things? Or should I be configuring something? A bit confused... It seems necessary to first understand how to truly optimize bitmaps in a project.

What Problems Cause Google Play Console to Give This Rectification Suggestion?

First, understand how this suggestion comes about. The "Bitmap Optimization" detection item in Google Play Console scans bitmap resources in the APK/AAB to see if the following situations exist:

If your project has the above situations, your application might also receive the same rectification suggestion in the Google backend as I did.

Image Loading Libraries

This is the most basic point: do not manage bitmap loading manually. Use mature image loading libraries; they automatically handle caching, downsampling, reuse, and recycling. Currently, there are two main image loading libraries for Android projects: one is Glide, the other is Coil.

Coil Configuration (Compose Project Example)

Global ImageLoader Configuration

image.png

Using AsyncImage in Compose

image.png

SubcomposeAsyncImage in Lists (Asynchronous Loading to Avoid Jank)

image.png

Custom ImageLoader Interceptor (Cooperating with Server-Side Cropping)

Automatically append target size parameters after the URL.

image.png

Register in ImageLoader.

image.png

Glide Configuration (Traditional Android Project Example)

Global RequestOptions (Limiting Image Quality and Downsampling)

image.png

Limiting Memory Cache and BitmapPool

image.png

Glide Optimization for List Pages

Explicitly specify the target size in list items to let Glide automatically downsample.

image.png

Using Glide's RequestManager Lifecycle Management

image.png

Painter Parameter Trap: Do Not Pass Painter to Composable

Why can't you pass Painter? Because the Painter interface does not have the @Stable annotation. The Compose compiler cannot stably infer whether it has changed, easily leading to unnecessary recompositions. The root cause is that Bitmap's equals() calculation cost is very high, so the Compose compiler will not mark Painter as a stable type.

Avoiding Unnecessary Recompositions Triggered by Painter

image.png

Passing Resource ID or URL

image.png

Handling in ViewModel

Avoid Holding Bitmap or Painter in ViewModel

image.png

If You Must Hold a Bitmap, Use ImageBitmap and Pay Attention to Lifecycle

image.png

Image Downsampling

When loading images, always load an image size that exactly meets the display requirements, avoiding stuffing ultra-high-resolution images into small containers.

Using Image Libraries for Automatic Downsampling

Coil and Glide automatically downsample after determining the target size.

Coil's Size Resolution Priority

  1. ImageRequest.Builder.size(width, height) : Explicitly specified
  2. AsyncImage's Modifier.size() : Inferred from layout constraints
  3. When unconstrained : Loads the original size; this behavior should be avoided as much as possible.

Glide's Size Resolution

Using override() to Explicitly Specify

image.png

Manual Downsampling (BitmapFactory)

If manual bitmap handling is necessary in the project, inSampleSize can be used for safe downsampling.

image.png

image.png

PS: inSampleSize must be a power of 2 (1, 2, 4, 8, 16...). The system will round down to the nearest power of 2.

Prioritize Server-Side Cropping

Best Practice: Request Precisely Sized Images

When requesting images from a backend API, try to include target width and height parameters to let the server return a cropped image. The benefits of doing this are:

Implementation Method If using Coil, you can use the interceptor mentioned above. If using Glide, you can use the following method:

image.png

Backend coordination ensures the CDN or image service supports the following parameters:

Parameter Description Example
w / width Target width ?w=200
h / height Target height ?h=200
m / mode Crop mode (fill / fit / crop) &m=crop
q / quality Compression quality &q=80

Avoid Unconstrained Layout Sizes

Do not use wrapContentSize or unconstrained sizes on Composables that load remote images. Doing so will cause:

Problems

When the image library cannot infer the target bounds, it falls back to loading the full original image, leading to:

Principle

image.png

Correct Approach

Set Explicit Sizes

image.png

Define Aspect Ratio, Let the Layout Engine Calculate Exact Pixel Target

image.png

Use Fixed Sizes in Lists

image.png

Avoid Unconstrained, Leading to Loading Original Size

image.png

Choosing the Correct Pixel Format

The memory usage difference between different pixel formats is significant:

Format Bits Per Pixel Supports Transparency Applicable Scenarios Savings Compared to ARGB_8888
ARGB_8888 32 bit Yes Needs alpha channel (default) /
RGB_565 16 bit No Images not needing transparency Saves half the memory
ARGB_4444 16 bit Yes Not recommended (poor quality) Saves half (not recommended)
ALPHA_8 8 bit Alpha only Masks/Overlays Saves 75%

In non-special scenarios, RGB_565 is sufficient for over 80% of images in an app, which can immediately halve memory usage.

Configuration Method

Coil

image.png

Glide

image.png

Determining if Transparency is Needed

In a project, you can determine whether an image needs transparency based on different scenarios. For example, the following four scenarios do not need it:

image.png

But if you encounter the following scenarios, then the image must support transparency:

image.png

GPU Texture Upload Optimization

Calling ImageBitmap.prepareToDraw() can upload the texture to the GPU in advance before actual drawing, reducing the first frame rendering latency.

image.png

Most image loading libraries have this optimization built-in. Only when not using an image loading library and needing to call it manually should you do the above.

Hardware Bitmap

Android 8.0 (API 26) supports hardware bitmaps, where bitmap data exists only in GPU memory, reducing CPU memory usage. If using the Coil loading library, it automatically uses hardware bitmaps. If using Glide, Glide enables hardware bitmaps by default. If you want to disable it, you can use the following method:

image.png

Prioritize Vector Graphics Over Bitmaps

For scenarios like geometric shapes, icons, etc., always prioritize vector graphics (ShapeDrawable, VectorDrawable). The benefits of doing this are:

image.png

So, why do we still need bitmaps? Or when is it more appropriate to use bitmaps versus vector graphics? Here are a few scenarios:

Memory Management and Release

When Using an Image Loading Library

The image library manages the Bitmap pool, releasing Bitmaps back into the pool for reuse when they are no longer needed, retaining a memory buffer. Manual recycling is not required.

When Managing Manually

image.png

The Correct Way to Add Padding to Images

When adding padding to images, some people are accustomed to using letterboxing, directly modifying the image size to add transparent borders. However, this changes the image size and increases memory usage. The correct approach is to keep the image size unchanged and use InsetDrawable or add padding on the parent container.

image.png

Avoid Packing Large Images into APK/AAB

In some projects, a static resource image might be several hundred KB or even several MB. If loaded directly into memory, memory usage will definitely spike. Therefore, avoid introducing large images into the project. If a large image must be used, do the following things first:

Multi-Density Adaptation

Try to place images of different resolutions into corresponding directories to avoid loading high-resolution images on low-density devices.

image.png

Performance Monitoring and Analysis Methods

adb Command Monitoring

image.png

Android Studio Memory Profiler

  1. Open ProfilerMemory panel.
  2. Record the scenario: Enter page → Load images → Scroll → Leave page.
  3. Pay attention to the following metrics:
    • Java Heap: Heap memory occupied by Bitmap objects.
    • Native Heap: Native layer Bitmap pixel data.
    • Graphics: GPU texture memory.
  4. Check if memory drops back after leaving the page (memory leak detection).

Coil Memory Monitoring

image.png

Finally

This article mainly talks about how to correctly handle bitmaps in a project to prevent warnings from Google. However, sometimes you might find that your project handles bitmaps well in every aspect, but you still receive rectification suggestions from Google. In this case, it's possible that a certain SDK you are using has problems in this area. Therefore, regularly check the official website updates of the third-party libraries you use. It's possible that the new version has already solved the problem, and you are still using the old version, analyzing the cause vigorously, which is really a waste of time and energy.