Glide: Getting Bitmap Reconfigure warning on Android M

Created on 13 Nov 2015  路  24Comments  路  Source: bumptech/glide

Glide Version/Integration library (if any):3.6.1 with okhttp-integration:1.3.1@aar
Device/Android Version: Nexus 5X, v.6.0.
Issue details/Repro steps/Use case background:

I'm using Glide for displaying images in RecyclerView and in FragmentStatePagerAdapter. On pre M versions of Android everything is going as expected. I did not faced any issues mentioned in https://github.com/bumptech/glide/wiki/Resource-re-use-in-Glide. But on the Android 6 i'm getting this warning when scrolling the RecyclerView or FragmentPages.

W/Bitmap: Called reconfigure on a bitmap that is in use! This may cause graphical corruption!

I put a method onViewRecycled to the RecyclerView adapter, and increased RV pool size. It seems to be helping, but not completely. I'm still have this problem in the FragmentPager, and when I return to the RecyclerView by pressing back button, the warnings are back (I guess that can probably be fixed by increasing RV pool size in onResume).

I'm loading the images from device as bitmaps. Can this be the source of the issue?

Glide load line:

Glide.with(context)
                    .load(Uri.parse(filepath))
                    .asBitmap()
                    .centerCrop()
                    .diskCacheStrategy(DiskCacheStrategy.ALL)
                    .into(holder.guideCover);

I love to use Glide because is super fast. The application is running as expected, hoverer I'm a bit nervous of those warning messages.

What I'm doing wrong?

bug enhancement non-library wontfix

Most helpful comment

I hate spam log too: http://stackoverflow.com/q/19610151/253468

Lack of better solution for now, here's how to hide the irrelevant spam. In IDEA/AS's Android Monitor go to the filters in upper right corner and choose _Edit Filter Configuration_ and

enter this into the _Log Tag_ field (you can remove the other tags if you're not bothered by those):

^(?!AbsListView|IInputConnectionWrapper|ApplicationPackageManager|Bitmap)

or you can explicitly just filter that single message if you enter the following into _Log Message_:

^(?!Called reconfigure on a bitmap that is in use)

^(?!...) is called negative lookahead assertion in regex. ^ mean beginning of input string, so it translates to !input.startsWith(regex)

All 24 comments

Thanks for the report! It looks like it was also found by someone (and you too :) on StackOverflow. The message is coming from Bitmap.cpp and was added in https://github.com/android/platform_frameworks_base/commit/ae2e8b4891491e8e89bed5f2c9626415adee09cb.

(_The following is my interpretation_) mPinnedRefCount seems to be in correlation with Bitmap objects on Java heap having strong references. Obviously we have strong references to Bitmaps in our BitmapPool otherwise we couldn't call reconfigure. The native layer doesn't really know what we are doing with our Bitmaps, most likely it's expecting us to only have references to Bitmaps in Views for the purposes of drawing.

(_The following is my opinion based on limited information_) It looks like Glide's best feature (bitmap reuse) may actually cause people to go back to other libraries, because the others don't give a warning. I'm not sure it is because they do it right, or they just simply don't re-use bitmaps as excessively as Glide...

I think increasing the RV pool size will just hide the warning. The cost of hiding is keeping more views and Bitmaps in memory (didn't verify, but likely). It's also possible that the reference counting is slower than it needs to be so it doesn't have time to propagate to the native layer before Glide reuses it. I think if you don't ever see any visual glitches/crashes it's fine to ignore it. There are other Android system logs that we (as developers) can't do anything about.

@sjudd what do you think about this issue?

OK, thanks for your advice. I can say, that even with those warnings application works on M like on all other devices. Please let me know, If there is anything how can I help more.

I had same problem too.
It logs warning messages after loading a GIF image.

Yeah @TWiStErRob
Its not causing any bitmap corruption nor crashing the app on M . Just showing log when we scroll fast .

The short answer is that the framework needs us to perform some additional and otherwise unnecessary steps to prove that what we're doing is safe. It's not a great answer, but for now you can largely ignore these warnings inside of Glide. Do let us know if you see any actual corruption.

After upgrading the support library to the new 23.2.1 this message appear all the time, without even scroll which makes unusable and really annoying.

I hate spam log too: http://stackoverflow.com/q/19610151/253468

Lack of better solution for now, here's how to hide the irrelevant spam. In IDEA/AS's Android Monitor go to the filters in upper right corner and choose _Edit Filter Configuration_ and

enter this into the _Log Tag_ field (you can remove the other tags if you're not bothered by those):

^(?!AbsListView|IInputConnectionWrapper|ApplicationPackageManager|Bitmap)

or you can explicitly just filter that single message if you enter the following into _Log Message_:

^(?!Called reconfigure on a bitmap that is in use)

^(?!...) is called negative lookahead assertion in regex. ^ mean beginning of input string, so it translates to !input.startsWith(regex)

I had same problem too.
After upgrading the support library to the new 24.0.2 alpha.
It logs warning messages after loading a GIF image.

@sjudd what would be required to make the system happy? (i.e. what is the "long answer"?)

The longer answer is that you need to make the rendering system aware that you're no longer going to use the Bitmap before reconfiguring it. To do so, you at least need to unset the Bitmap on whatever was rendering it (the last View that drew the Bitmap for example) and then you'd need to post something back to the main thread before reconfiguring so that the whatever was rendering the Bitmap gets to draw one more time.

As long as the View doesn't actually re-draw, there isn't any risk of corruption, but the system can't tell whether or not you're going to force the View to re-draw the reconfigured Bitmap, so it warns.

This warning will be removed in N I believe, so I'm going to close this.

I am seeing this issue on Android M, but only when I enable proguard. If prodguard is disable everything is fine and even the logs won't come. but when I enable proguard the log comes and have graphical issues.

@anoop44 please open a new issue detailing a repro and showing the graphical issue(s).

This error does seem to cause graphical corruption but I haven't had a chance to create a simple repro case. However, I have a question about the SizeConfigStrategy#get code:

result.reconfigure(width, height,
    result.getConfig() != null ? result.getConfig() : Bitmap.Config.ARGB_8888);

Is there any reason I couldn't just skip the reconfigure if the width and height of the bitmap were not being changed?

Are you sure this causes the corruption? What kind of problem is visible? Good point about shortcutting, @sjudd?

I'm working on a way to reproduce the bug in isolation.

I can't say for certain that the reconfigure is the cause, either, but I'm curious whether or not it's even necessary when the size and config is not changing. Is there something non-obvious that happens under the hood, deep in the Android native bitmap code?

I have seen this grahical issue only after using with an custom imageview I wrote. The extra behaviour added to the imageview was to change height according to the bitmap/drawable set to it.

I'm seeing this issue when using glide for populating images in a recyclerview.

On top of that, high cpu utilization when compared to fresco, causing the phone to overheat.. It's a shame cause this library is perfect in every other regard!

@manrock007 what is "this issue" you're referring to? Because the log line is not an issue, only the corruption, for which we have no repro, nor even a screenshot for yet. If you can provide any relevant info, please do.

It would be nice to see a write-up of that performance comparison which shows in which situation Glide causes the phone to overheat. Are you by any chance displaying a grid of animated GIFs?

The issue that I was referring to is the log line. Depending on how fast I scroll, the log frequency changes from maybe once per second to maybe once per 100ms.

No its not animated gifs.. These are images. From sites like imgur.. I don't see any graphical corruption, just the phone overheating.. I'll try to get some stats about the cpu utilization.. Anything I can quickly provide? Logs from glide library?

@manrock007 the log line doesn't do anything as stated above by sjudd. You're essentially seeing a log line whenever Glide saves you GC/CPU time by not allocating a whole new Bitmap. Based on your scrolling speed this will happen more often or not.

Not sure what you could give quickly, loading each image takes network, CPU and I/O time, and it should have the same amount with any library. Let's take this overheating thing out of this conversation, it's likely unrelated, so please open a new issue, once you have more details!

(_Update: he did open a new issue https://github.com/bumptech/glide/issues/1394 and it wasn't related to reconfiguring Bitmaps, just playing GIFs and caching them incorrectly._)

I have the same problem too since I used glide to load gif on Android 6.0.1 device.
My issue link here : #1663

If any update about this problem, please let me know, thanks a lot

Hi, Glide! I experienced this recently in some of my own work. The warning is called whenever you try to use a Bitmap instace for BitmapFactory.Options#inBitmap that is not the same size as the incoming image, and I've seen on-and-off-again image corruption from this, especially when using the emulator.

Here's code that does not trigger the warning:

public class TileBitmapProvider implements BitmapProvider {

    private final TileProvider provider;
    private final Bitmap.Config bitmapConfig;
    private final int backgroundColor;
    private final BitmapPool bitmapPool;
    private final Rect frameRect = new Rect();

    public TileBitmapProvider(final TileProvider provider,
                              final BitmapPool bitmapPool,
                              final Bitmap.Config bitmapConfig,
                              final int backgroundColor) {
        this.provider = provider;
        this.bitmapConfig = bitmapConfig;
        this.backgroundColor = backgroundColor;
        this.bitmapPool = bitmapPool;
    }

    @Override public Bitmap getBitmap(final Tile tile, final Context context) {
        try {
            Bitmap bmp = tile.getBitmap();

            if (bmp == null || bmp.isRecycled()) {
                bmp = bitmapPool.poll();
                bmp = bmp != null ? bmp : Bitmap.createBitmap(tile.getWidth(), tile.getHeight(), bitmapConfig);
                // tiles at edges need to be padded so that they fit correctly
                bmp.eraseColor(backgroundColor);

                final BitmapFactory.Options ops = new BitmapFactory.Options();
                ops.inPreferredConfig = bitmapConfig;
                ops.inBitmap = bmp;
                ops.inMutable = true;

                frameRect.set(0, 0, tile.getWidth(), tile.getHeight());
                bmp = BitmapRegionDecoder.newInstance(provider.readTile(tile), false).decodeRegion(frameRect, ops);
            }

            return bmp;
        } catch (final IOException e) {
            Logger.getInstance().critical(getClass().getSimpleName(), e, "Error loading tile:");
        }
        return null;
    }
}

Here's the code that triggers the warning:

public class TileBitmapProvider implements BitmapProvider {

    private final TileProvider provider;
    private final Bitmap.Config bitmapConfig;
    private final int backgroundColor;
    private final BitmapPool bitmapPool;

    public TileBitmapProvider(final TileProvider provider,
                              final BitmapPool bitmapPool,
                              final Bitmap.Config bitmapConfig,
                              final int backgroundColor) {
        this.provider = provider;
        this.bitmapConfig = bitmapConfig;
        this.backgroundColor = backgroundColor;
        this.bitmapPool = bitmapPool;
    }

    @Override public Bitmap getBitmap(final Tile tile, final Context context) {
        try {
            Bitmap bmp = tile.getBitmap();

            if (bmp == null || bmp.isRecycled()) {
                bmp = bitmapPool.poll();
                bmp = bmp != null ? bmp : Bitmap.createBitmap(tile.getWidth(), tile.getHeight(), bitmapConfig);
                // tiles at edges need to be padded so that they fit correctly
                bmp.eraseColor(backgroundColor);

                final BitmapFactory.Options ops = new BitmapFactory.Options();
                ops.inPreferredConfig = bitmapConfig;
                ops.inBitmap = bmp;
                ops.inMutable = true;

                bmp = BitmapFactory.decodeStream(provider.readTile(tile), null, ops);
            }

            return bmp;
        } catch (final IOException e) {
            Logger.getInstance().critical(getClass().getSimpleName(), e, "Error loading tile:");
        }
        return null;
    }
}

Changing the method to use a BitmapRegionDecoder with a Rect sized to the existing Bitmap instance's size does not trigger the warning because no Bitmap instance is being resized.

this log print frequency is a bit high

Hi, Glide! I experienced this recently in some of my own work. The warning is called whenever you try to use a Bitmap instace for BitmapFactory.Options#inBitmap that is not the same size as the incoming image, and I've seen on-and-off-again image corruption from this, especially when using the emulator.

Here's code that does not trigger the warning:

public class TileBitmapProvider implements BitmapProvider {

    private final TileProvider provider;
    private final Bitmap.Config bitmapConfig;
    private final int backgroundColor;
    private final BitmapPool bitmapPool;
    private final Rect frameRect = new Rect();

    public TileBitmapProvider(final TileProvider provider,
                              final BitmapPool bitmapPool,
                              final Bitmap.Config bitmapConfig,
                              final int backgroundColor) {
        this.provider = provider;
        this.bitmapConfig = bitmapConfig;
        this.backgroundColor = backgroundColor;
        this.bitmapPool = bitmapPool;
    }

    @Override public Bitmap getBitmap(final Tile tile, final Context context) {
        try {
            Bitmap bmp = tile.getBitmap();

            if (bmp == null || bmp.isRecycled()) {
                bmp = bitmapPool.poll();
                bmp = bmp != null ? bmp : Bitmap.createBitmap(tile.getWidth(), tile.getHeight(), bitmapConfig);
                // tiles at edges need to be padded so that they fit correctly
                bmp.eraseColor(backgroundColor);

                final BitmapFactory.Options ops = new BitmapFactory.Options();
                ops.inPreferredConfig = bitmapConfig;
                ops.inBitmap = bmp;
                ops.inMutable = true;

                frameRect.set(0, 0, tile.getWidth(), tile.getHeight());
                bmp = BitmapRegionDecoder.newInstance(provider.readTile(tile), false).decodeRegion(frameRect, ops);
            }

            return bmp;
        } catch (final IOException e) {
            Logger.getInstance().critical(getClass().getSimpleName(), e, "Error loading tile:");
        }
        return null;
    }
}

Here's the code that triggers the warning:

public class TileBitmapProvider implements BitmapProvider {

    private final TileProvider provider;
    private final Bitmap.Config bitmapConfig;
    private final int backgroundColor;
    private final BitmapPool bitmapPool;

    public TileBitmapProvider(final TileProvider provider,
                              final BitmapPool bitmapPool,
                              final Bitmap.Config bitmapConfig,
                              final int backgroundColor) {
        this.provider = provider;
        this.bitmapConfig = bitmapConfig;
        this.backgroundColor = backgroundColor;
        this.bitmapPool = bitmapPool;
    }

    @Override public Bitmap getBitmap(final Tile tile, final Context context) {
        try {
            Bitmap bmp = tile.getBitmap();

            if (bmp == null || bmp.isRecycled()) {
                bmp = bitmapPool.poll();
                bmp = bmp != null ? bmp : Bitmap.createBitmap(tile.getWidth(), tile.getHeight(), bitmapConfig);
                // tiles at edges need to be padded so that they fit correctly
                bmp.eraseColor(backgroundColor);

                final BitmapFactory.Options ops = new BitmapFactory.Options();
                ops.inPreferredConfig = bitmapConfig;
                ops.inBitmap = bmp;
                ops.inMutable = true;

                bmp = BitmapFactory.decodeStream(provider.readTile(tile), null, ops);
            }

            return bmp;
        } catch (final IOException e) {
            Logger.getInstance().critical(getClass().getSimpleName(), e, "Error loading tile:");
        }
        return null;
    }
}

Changing the method to use a BitmapRegionDecoder with a Rect sized to the existing Bitmap instance's size does not trigger the warning because no Bitmap instance is being resized.

@MisterRager please, post this solution as a pull request

Was this page helpful?
0 / 5 - 0 ratings