I have a problem using the Universal Image Loader Library.
Unfortunately, the Library causes huge memory usage of my application.
I have a ScrollView (there are tons of reasons why I am using a ScrollView instead of a ListView, most of them have to do with custom insertion and item selection animations) holding about 20 custom Views.
Each of those custom views consists of some TextViews as well as an ImageView.
And now here is the thing:
The Images dispalyed in the ImageViews are downloaded from the Internet and all have around 100-150kb (dimension 640 x 320) each. If I download the Images using the UniversalImageLoader my app crashes with an OutOfMemoryException because it uses around 80Mb of memory.
If I do not download the Images, and just put in a Hard-Coded Image with the size of 1200kb (dimension 1920 x 1080) per custom view, my app only consumes around 40Mb of memory.
What am I doing wrong using the UniversalImageLoader when downloading ten times smaller Images causes my app to use up twice as much memory?
I disabled all caching mechanisms but the problem still persists.
Here is my UniversalImageLoader setup:
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(c)
.build();
ImageLoader.getInstance().init(config);
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.loading)
.showImageForEmptyUri(R.drawable.loading)
.showImageOnFail(R.drawable.loading)
.cacheInMemory(false)
.cacheOnDisc(false)
.imageScaleType(ImageScaleType.IN_SAMPLE_INT) // default
.build();
And in code, I call it like this:
ImageLoader im = ImageLoader.getInstance();
im.displayImage("myurl", myimageview, options);
What am I doing wrong? Why is the UniversalImageLoader using up so much memory?
For all the other people out there having the same issues:
I managed to minimize my Memory usage and get rid of the OutOfMemoryException by following these guidelines:
https://github.com/nostra13/Android-Universal-Image-Loader#useful-info
The most important part for me was to reduce the ThreadPoolSize down to 2. I could not feel any performance changes after setting it down.
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(c)
.threadPoolSize(2)
.build();
DisplayImageOptions options = new DisplayImageOptions.Builder()
// other options
.bitmapConfig(Bitmap.Config.RGB_565)
.imageScaleType(ImageScaleType.EXACTLY)
// other options
.build();
This reduced my applications total memory consumption by about 35%.
Related
I'm loading about 50 images. In my activity I have this configuration
DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
.cacheOnDisk(true).cacheInMemory(true)
.imageScaleType(ImageScaleType.EXACTLY)
.resetViewBeforeLoading(true)
.displayer(new FadeInBitmapDisplayer(300))
.build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
.defaultDisplayImageOptions(defaultOptions)
.memoryCache(new WeakMemoryCache())
.diskCacheSize(100 * 1024 * 1024)
.build();
ImageLoader.getInstance().init(config);
in binderdata
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.displayImage(uri, holder.iv_img);
Some images are loaded, but some are not and I get OutOfMemory error.
Check this tips from Useful Info
If you often got OutOfMemoryError in your app using Universal Image
Loader then:
Disable caching in memory. If OOM is still occurs then it seems your app has a memory leak. Use MemoryAnalyzer to detect it. Otherwise
try the following steps (all of them or several):
Reduce thread pool size in configuration (.threadPoolSize(...)). 1 - 5 is recommended.
Use .bitmapConfig(Bitmap.Config.RGB_565) in display options. Bitmaps in RGB_565 consume 2 times less memory than in ARGB_8888.
Use .imageScaleType(ImageScaleType.EXACTLY) (Your already use this tips)
Use .diskCacheExtraOptions(480, 320, null) in configuration
Hope this helps!!
1.I would suggest you to checkout Image Management Library by Facebook that is Fresco that is pretty awesome and mature as compared to other Image Loading Library.
2.Fresco handles all the things caching of images with 3 Tier architecture ( BITMAP_MEMORY_CACHE, ENCODED_MEMORY_CACHE and DISK_CACHE). It also reduces OOM(Out Of Memory) issues. When image in a view goes out of screen it automatically recycles the bitmap, hence releasing the memory.
3.Fresco has SimpleDraweeView as custom image view which supports Rounded Corners and Circles link and supports Animated(.gif, .webp) as well as Normal Images(.jpg, .png).
I'm using the "Android universal image loader library (v 1.9.1)" and I'm trying to limit the amount of (disc) cache being used by using this line in a config builder:
.discCache(new TotalSizeLimitedDiscCache(cacheDir, 25 * 1024 * 1024))
I might be doing something really stupid here, but this was supposed to limit my cache to 25MB and when I look at the current App info I'm already at 45+ MB, so it doesn't seem to be working.
Have I missed something?
added suspicion: what if the cache-number also includes the 'memorycache' that is set to true? I working under the assumption this is only a cache as far as the imageLoader is concerned, and from an application/os point of view just another thing using memory. if it isn't, it might explain the numbers?
Complete code for initializing :
private void initImageLoader() {
DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.cacheOnDisc(true)
.build();
File cacheDir = StorageUtils.getCacheDirectory(getApplicationContext());
cacheDir.mkdirs();
ImageLoaderConfiguration config = new
ImageLoaderConfiguration.Builder(getApplicationContext())
.defaultDisplayImageOptions(defaultOptions)
.discCache(new TotalSizeLimitedDiscCache(cacheDir, 25 * 1024 * 1024))
.build();
ImageLoader.getInstance().init(config);
}
And for calling the code (showing imageUrl in imageView)
ImageLoader.getInstance().displayImage(imageUrl, imageView);
When setting the default memory-cache to false (so don't use .cacheInMemory(true)), the cache seems to be keeping itself to the set maximum. The computed cache is probably a combination of the on-disc cache and the memory cache.
This does make the "cache" number from the Android OS somewhat less usable, but on the other hand, it is kinda fair. As it looks like the numbers are lineing up, it's just a matter of setting both maximums, and communicating this to the user.
As we all know, the VM Heap Size for Android Apps is limited.
(mostly to 16,24,32,48 or 64 MB of RAM depending on the hardware)
we can get the actual Heap Size with
ActivityManager am = (ActivityManager)this.getSystemService(ACTIVITY_SERVICE);
Toast.makeText(this, "HEAP SIZE: " + am.getMemoryClass() + "MB", 1).show();
.
But, what is the memory strategy if I open a new Intent???:
Intent intent = new Intent(MainActivity.this, NewActivity.class);
MainActivity.this.startActivity(intent);
Does this new activity get the full heap size and the old activity is holding in the background, and its memory is cached?
Or does the intent get a complete new VM?
I have a very memory intense App with heavily filles Grid- and ListViews.
Since Android 3.0, Bitmaps are allocated inside the Heap, and that causes a lot of headache and trouble with OutOfMemory Errors... I was wondering if I can outsorce the memory hungry Views in their own Intents. <-- does this make any sense?
Every process has one heap, of whatever size the device specifies (minimum 16 MB if Google Play is installed on the device).
All of your app's components must run within this heap, as your app is a single process. So all your Activities, Services, BroadcastReceivers share the same heap.
When you launch a new Activity, your previous Activity is pushed into the background. Due to this, its onPause() is called, and you should use that method to free up memory (remove loaded bitmaps etc) if you need to.
Also, outsorce the memory hungry Views in their own Intents makes no sense because Intents are used to start Application components like Activities and Services. You cannot use a View with an Intent. Instead, you should be scaling your bitmaps and loading only the required size, and make use of technique like lazy loading to make sure you don't have more bitmaps in memory than required.
I agree with Raghav Sood. I have added a bit more of how to display images in listview or gridview.
I have used Universal Image loader to display large number of images in listview.
You should recycle bitmaps when not in use.
http://www.youtube.com/watch?v=_CruQY55HOk. Talk is about memoy management and memory leaks and how to avoid it. If you ever run into memory leaks you can use a MAT Analyzer to find memory leaks. The video also talks about using MAT Analyzer and demonstrates how to get rid of memory leaks.
When you display images in listview you need to recycle the views. Views that are visible are not recycled.
To display images in gridview or listview you can use univarsal image loader. A improved version of lazy loading. Images are cached. You can display images localy or from a server.
https://github.com/nostra13/Android-Universal-Image-Loader
File cacheDir = StorageUtils.getOwnCacheDirectory(context, "your folder");
// Get singletone instance of ImageLoader
imageLoader = ImageLoader.getInstance();
// Create configuration for ImageLoader (all options are optional)
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(a)
// You can pass your own memory cache implementation
.discCache(new UnlimitedDiscCache(cacheDir)) // You can pass your own disc cache implementation
.discCacheFileNameGenerator(new HashCodeFileNameGenerator())
.enableLogging()
.build();
// Initialize ImageLoader with created configuration. Do it once.
imageLoader.init(config);
options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.stub_id)//display stub image
.cacheInMemory()
.cacheOnDisc()
.displayer(new RoundedBitmapDisplayer(20))
.build();
In your getView()
ImageView image=(ImageView)vi.findViewById(R.id.imageview);
imageLoader.displayImage(imageurl, image,options);//provide imageurl, imageview and options
You can configure with other options to suit your needs.
Along with Universal Image Loader you can view holder for smooth scrolling and performance. http://developer.android.com/training/improving-layouts/smooth-scrolling.html.
http://www.youtube.com/watch?v=wDBM6wVEO70. The talk is about viewholder and performance.
I'm working on a music player app for android and am trying to find the best way of dealing with images. I'm loading artist images from the web, and album images either from the MediaStore or the web depending on the circumstances. I will need to add either set of images (artist or album) to listViews at different times.
Storing the images in memory (HashTable) would make loading very quick, but could become impractical if the user has a very large music library.
However I can't load them on an "as needed" basis because I need to bind them to a listView. In addition, I have a notification that displays the artist image and provides navigation controls. If I wait to load the image when the user clicks the next button, it might not load before the notification view updates.
Is the best approach to connect to the Net on first load and then save the files locally, and then load them as needed? If I do it this way, will the image be loaded quickly enough to keep up with a user fast-clicking the next button and refreshing the artist image view?
Thanks,
Use a listview or gridview to display images.
You can use https://github.com/nostra13/Android-Universal-Image-Loader Universal Image Loader to load images either form MediaStore or from the web. Uses caching. Based on Lazy Loading but has more configuration options.
In you adapter contructor
File cacheDir = StorageUtils.getOwnCacheDirectory(a, "UniversalImageLoader/Cache");
// Get singletone instance of ImageLoader
imageLoader = ImageLoader.getInstance();
// Create configuration for ImageLoader (all options are optional)
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(a)
// You can pass your own memory cache implementation
.discCache(new UnlimitedDiscCache(cacheDir)) // You can pass your own disc cache implementation
.discCacheFileNameGenerator(new HashCodeFileNameGenerator())
.enableLogging()
.build();
// Initialize ImageLoader with created configuration. Do it once.
imageLoader.init(config);
options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.stub_id)//dummy image
.cacheInMemory()
.cacheOnDisc()
.displayer(new RoundedBitmapDisplayer(20))
.build();
In your getView()
ImageView image=(ImageView)vi.findViewById(R.id.imageview);
imageLoader.displayImage(imageurl, image,options);
You can also use https://github.com/thest1/LazyList. Also uses caching.
Also use a ViewHolder in listview or grdiview. http://developer.android.com/training/improving-layouts/smooth-scrolling.html.
Load your images from local file storage or MediaStore in the getView() function of your ListView's ListAdapter or SimpleListAdapter. The adapter will take care of only calling getView() when necessary for the item(s) needed by position. Your system will be fast enough to load the images in this manner when they are needed.
You will have to fetch all images from the web at an earlier time and store them in the local filesystem so they are available for display by the ListView.
I'm loading a 95KB image, dimensions 1024x683 using Universal Image Loader. When I'm on wireless, the image loads just fine. However, if I turn off wireless and use the phone's network, it downloads the image at 305x203.
This is my config:
// setup the image async loader
DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
.cacheInMemory()
.cacheOnDisc()
.build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
.defaultDisplayImageOptions(defaultOptions)
.build();
ImageLoader.getInstance().init(config);
This is the download:
ImageLoader.getInstance().displayImage(imageUrl, imageView, new SimpleImageLoadingListener() {
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
android.util.Log.v("image size", String.valueOf(loadedImage.getWidth())+" x "+String.valueOf(loadedImage.getHeight()));
}
});
I've tried removing cacheInMemory. I've tried using loadImage and specifying a target size, but it still downloaded at the smaller size. I tried changing the image scale type, using NONE and EXACTLY, no change.
I feel like I'm missing something obvious, but I can't figure it out.
Dunno exactly what the UniversalImageLoader actually does, but maybe it is responsible. You can find out by downloading the image with a normal AndroidHttpClient connection with both connection types (WiFi and mobile) and then comparing their actual size.
This phenomenon can also happen due to a reformatting proxy of your network operator, that detects your mobile device's user agent and scales down the image without your knowledge or request. I know this sounds odd, but those things happen. You can find out of this is the case, but trying a different provider (change sim cards) or by trying to change the settings of your http user-agent (pretend to be a full blown desktop firefox).
Good luck!