I am using UIL to load bitmaps. It works fine. But I have an issue when using it with widgets.
I am loading it with custom NonViewAware
:
private static class WidgetImageAware extends NonViewAware
{
protected final int mId;
public WidgetImageAware(int imageSize, int id)
{
super(new ImageSize(imageSize, imageSize), ViewScaleType.CROP);
mId = id;
}
@Override
public int getId()
{
return mId;
}
}
To set scaling I use imageScaleType(ImageScaleType.EXACTLY)
in DisplayImageOptions
.
After calling:
imageLoader.displayImage(someUri, new WidgetImageAware(480, id), displayImageOptions, new SimpleImageLoadingListener()
{
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage)
{
height = loadedImage.getHeight() // height == 950 !!
width = loadedImage.getWidth() // width == 950 !!
}
});
So the size of loaded Bitmap is much bigger (950x950) than requested in Aware (480x480). I need to load Bitmap with size EXACTLY 480x480. Otherwise update of widget will throw:
java.lang.IllegalArgumentException: RemoteViews for widget update exceeds maximum bitmap memory usage (used: 3632836, max: 2304000) The total memory cannot exceed that required to fill the device's screen once.
So there is a bug when using two instances of DisplayImageOptions with two different ImageScaleTypes.
It is cached according firstly used ImageScaleType and all ongoing ImageScaleTypes are ignored. This is caused by process of generation of key in cache that ignores ImageScaleType.
I have simplified the issue to following test example:
public void testMultipleImageScaleTypes() throws InterruptedException
{
final CountDownLatch lock = new CountDownLatch(1);
final List<Bitmap> bitmaps = new ArrayList<>();
// this image is 950x950
final String uri = "http://spaceplace.nasa.gov/review/3d-gallery/sun-sdo-disk.en.jpg";
final DisplayImageOptions first = new DisplayImageOptions.Builder().imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2).cacheInMemory(true).build();
final DisplayImageOptions second = new DisplayImageOptions.Builder().imageScaleType(ImageScaleType.EXACTLY).cacheInMemory(true).build();
ImageLoader.getInstance().init(new ImageLoaderConfiguration.Builder(getContext()).memoryCache(new LRULimitedMemoryCache(2048)).build());
ImageLoader.getInstance().displayImage(uri, new NonViewAware(new ImageSize(480,480), ViewScaleType.CROP), first, new SimpleImageLoadingListener()
{
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage)
{
bitmaps.add(loadedImage);
ImageLoader.getInstance().displayImage(uri, new NonViewAware(new ImageSize(480,480), ViewScaleType.CROP), second, new SimpleImageLoadingListener()
{
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage)
{
bitmaps.add(loadedImage);
lock.countDown();
}
});
}
});
lock.await(1, TimeUnit.HOURS);
assertEquals(950, bitmaps.get(0).getWidth());
assertEquals(950, bitmaps.get(0).getHeight());
assertEquals(480, bitmaps.get(1).getWidth()); // it fails here - bitmap size is 950x950
assertEquals(480, bitmaps.get(1).getHeight());
}
I have submitted bug report. The workaround might be to use different ImageLoader instances for different ImageScaleTypes. But it is quite wasting of memory.