I am facing an issue where Picasso is unable to rapidly load images.
There are 3 ImageViews which display left, middle and the right images.
On fast forward/backward, for a given time point, the left, middle and the right are chosen and displayed in the three box.
Here is the initialization. OkHttp cache is 100MB along with 99MB of LruCache
new Picasso.Builder(getApplicationContext())
.downloader(new OkHttp3Downloader(picassoOkHttpClient))
.memoryCache(new LruCache(99999999))
.build();
The number of images over which the window moves is 3000+. So I preload the images as
picasso.load(imageUrl).fetch();
Average image size is 10KB - 320x180 so it amounts to 35MB which is less than 99MB LruCache.
When the rapidly cycling through works, many of the images are GREEN (from MEMORY) and BLUE (from DISK) but then after few rapid cycles, the 3 images freeze or become very slow. At this time most of the images are BLUE. App is still responding. No OutOfMemory or any other exception.
android:largeHeap option is also enabled.
The hit ratio in the Picasso stats is very low. 150/3000.
The images are loaded as
picasso.load(leftUrl)
.noFade()
.noPlaceholder()
.into(leftImageView);
picasso.load(middleUrl)
.noFade()
.noPlaceholder()
.into(middleImageView);
picasso.load(rightUrl)
.noFade()
.noPlaceholder()
.into(rightImageView);
Issue 1 -> Picasso takes several minutes to download 3000+ images. Images load fast enough on other platforms and there is plenty of bandwidth (high enough to stream 4K)
Issue 2 -> Even after letting Picasso take its own time and finish loading all the images, it still freezes. Green ones are fast, Blue slower and then Blue ones freeze.
PS: I understand that "inflated" images will be much larger in size and will easily cross the 99MB bounds. That is why only some are green while most of them are blue.
The only reason I can understand fro your problem is that you are using images of very high resolution with huge size.
My suggestion for you is to resize the images while you are loading those images.
Something like that:
Picasso.with(getApplicationContext()).load(filePath).resize(400,400).centerCrop().into(imageView);
Hope this will help!
Try using another image loading library? Maybe the flaw is simply with Picasso. Personally i've never been fond of Picasso as it always had some issues. I've started using Glide, and can't find a single bad thing about it. It supports all kinds of caches and transformations, and it does it extremely well.
Examples:
- Glide
- UniversalImageLoader
- Fresco
Related
I am trying to load images (around 5mb each) into ImageView's of size 45x45 dp in a RecyclerView. Even though original image is large, doesn't Glide load a smaller version of it because target ImageView is small? So, I expect Glide to load images in just a few seconds with an average internet speed. But, it takes like 20 seconds. What is the problem?
Images are stored in firebase storage.
Glide code :
Glide.with(context).load(firebaseStorageUrl).into(imageView);
Glide needs to load the full image from the internet before it can resize it. So the download takes a long time the first time. Afterwards it can use the small image from the cahe it created if you have caching activated.
In case you don't want to load smaller files, I recommend using a Gif-drawable library for loading large images from Glide into GifDrawable(works as an ImageView).
It can load files more than 100MB very fast. It works with plain JPG, PNG and BMP too. If these are GIFs, it plays without any delay or freeze.
If drawables declared by android:src and/or android:background are GIF files then they will be automatically recognized as GifDrawables and animated. If given drawable is not a GIF then mentioned Views work like plain ImageView and ImageButton.
Glide downloads the image first, and then you have methods to retrieve the image as bitmap to resize and compress the image. However, that won't solve your problem . Possibly there are two solutions for this:
Keep a low sized image in the server.
Use a File downloader library, which helps to download images serially and asynchronously as a queue, which would help to download the file quick. FileDownloader library is a good option for this.
Use Override in Glide for load image faster.
I have a Scrollview that contains 4 big size images 750x1023, 750x2265, 750x1898, and 750x1112
I load image with Picasso
Picasso.with(getContext()).load(resIds[i]).fit().centerCrop().into(mainImageview.get(i));
But it brings to OutOfMemory error.
It makes to problem:
not all images load
app closing
Its important not to make image smaller - it can make UI uncorrect.
What the way to solve this problem? id need, to post code of program - just say what piece.
Thanks!
After all manipulation, problem is still unsolved.. If i scale images with BitmapFactory, it gives "Bitmap too large to bu uploaded into a texture", what ever size i write. If a scale with picasso, it crops my image What ever i do, app memmory enought for two, three switching pages... I'm exhausted because of this problem ...Help
The issue I believe is with gl max size which is something like 2048x2048 and the error will be cannot apply texture or something to that effect Picasso has a lot of ways to scale images but if you must use the full size image then its possible to cut the image into 4 or more pieces and assemble it like Google maps check this "Bitmap too large to be uploaded into a texture"
I'm using picasso to load images in my recycler view adaper but it is taking to much time to load image. Here is my call to load image with picasso.
Picasso.with(hostActivity).load("ImageUrl").fit().centerCrop().into(holder.ImageView);
If I do same thing with asynctask task, image loaded instantly.
Am I doing any thing wrong?
Thanks.
fit() needs to wait for the size of the ImageView to be determined before it can size the image to match, and the size can't be calculated until the end of the layout pass. You might get quicker results by using resize() if you are able to predict reasonable width and height values.
You might also want to look at the Glide library as it takes a different approach to caching that can be quicker than Picasso in some cases, for example instead of caching full size images it caches the resized ones. However, there are many pros and cons to both libraries; although the syntaxes are very similar, some things that work in Picasso will not work in Glide, and vice versa.
So, I have been using this amazing library Glide for showing native images in my gallery app. I am using ViewPager with FragmentStatePagerAdapter to show full size images. The pager's off screen limit is 1 (default to save memory). I am using this code to load images into ViewPager in my fragment:
Glide.with(getActivity())
.loadFromMediaStore(uri)
.asBitmap()
.signature(new MediaStoreSignature(mimeType, dateModified,
.into(mImageView);
Now, I am facing some issues here like:
Images take quite some amount of time to load (if not cached). So, while user is scrolling through the viewpager blank screen is shown while image is loading which is what I want to avoid. Is there any way I can do this? Maybe by precaching images?
Sometimes, while scrolling through large size images (mainly Camera photos) OOM Exception is thrown and user is left with blank screen as no image is loaded. This also happens when I am shifting from potrait to landscape mode. So, I tried to use methods like atMost() -- which degrade the quality of image further as images are already loaded in RGB_565 and approximate() which is also causing OOM. How can I achieve maximum image quality without getting OOM exceptions?
For the second issue, I was thinking to load lesser quality images for the off screen items and then enhance quality when they come on-screen. Is it possible?
I have also tried to use ARGB_8888 but the result was same: OOM exception.
TL;DR
Make sure the ImageView has match_parent or fixed dp as dimensions
wrap_content makes Glide load full resolution Bitmaps.
.placeholder() shows an image instead of empty space while loading large bitmap
.thumbnail(float) loads a downsampled version fast while the bigger image is loading in the background
Also look around the Glide issues, maybe you find something helpful.
Details
I would be curious what the xml is for the ImageView, because my guess is it's wrap_content which results in loading images at full resolution into Bitmaps (using a lot of memory). If that's the case I would suggest using match_parent or fixed dp to lower the resolution. Note: you won't use detail, because currently the image is downsampled at render time anyway, just pulling that forward to decoding phase.
You also have to make sure that your app doesn't have constraints for memory usage. Can you load 3 (off screen limit = 1 means 1+current+1 pages) camera photos into Bitmaps without Glide? Again, assuming this is full resolution, it should be possible to store 3 screen size amount of bytes in memory with or without Glide, but you have to guide Glide to not load at full resolution.
You can load smaller sized image via .thumbnail(), it accepts a full Glide.with... not including .into() OR there's a shorthand parameter which is just a percentage (in 0.0 ... 1.0), try the latter first. It should decode the image much faster, especially with a really small number like 0.1 and then when higher quality one finishes it's replaced.
So the simpler option is to add .thumbnail() to your current load. A more convoluted one involves to start loading a lower resolution image with .sizeMultiplier() at the same time the Fragment's view is created and then start loading the high resolution one when the ViewPager has changed page. This helps with peeking the pages.
Alternatively you can just use a .placeholder() while the image is loading so it's not empty space, but "something" there.
Regarding using ARGB_8888 (32 bit per pixel): if you increase the memory consumed by Bitmaps (compared to RGB_565 (16 bit per pixel) don't expect to get run out of memory later. Once you get it working with 565 you can try increasing, but until then it's futile trying.
Also look around the Glide issues, maybe you find something helpful.
For last 10+ hours I try to get a large (40+) amount of images (in ImageButton format) on a single Android screen without out of memory errors. The activity I work on is an image picker for a coloring book app. Images are of various sizes in the range of (500 to 1200)x(500 to 1200), PNGs (if that matters).
I have tried:
Horizontal Scroll View with images added from the code. The result is slow (I do it on UI thread) and consumes large memory space.
Horizontal Scroll View with images added from the code via AsyncThread. The result is fast but still consumes large memory space. I like the user experience of this one the most!
Grid View and List View - both are very choppy (testing on first Nexus 7). Memory usage is better.
What I am considering
View Pager - first results look better than Grid View from performance perspective (I have not completed it to the moment to assess the memory usage but it should be better from what I understand). Yet, I dislike the user experience and prefer a scrollable list of all images.
Conversion of my resources to JPG (will that get rid of Transparency byte?)
Downsizing the images to max 500x500px
None of the solutions seems as good as the Android Photo Gallery app available on all devices. This experience is what I would love to mirror. No idea how this is done though :(
Two questions.
What is the best way to get such thing (40+ Images scrollable on single screen) done? Is it really ViewPager? ScrollView with AsyncTask and well thought images resolution? Something I have not tried yet?
What is the memory limit I should try to keep below? Looking at Android heap size on different phones/devices and OS versions it seems to be 256 MB, is that fair assumption?
Thanks. Have a great weekend!
PS. On iOS all works like charm when I add all the buttons into a scroll view :(
Some basic calculations reveals your problem:
40+ images at 1200x1200 = approx 57MB, the comments are correct you need to subsample (i.e. thumbnail) the images or use a library such as the Universal Image Loader. Converting to JPG doesn't matter. That is a compressed storage format, the memory required to display the pixels remains the same.
There is a Android Developers article on Loading Large Bitmaps Efficiently with sample code. The following steps are covered in more detail in the article Android Bitmap Loading for Efficient Memory Usage.
The basic steps in loading a large bitmap are:
Determine the required size (from a known size or determining the
size of the target View).
Use the BitmapFactory class to get the bitmap’s size (set inJustDecodeBounds in BitmapFactory.Options to true).
Calculate the subsampling value and pass it to the BitmapFactory.Options setting inSampleSize.
Use the BitmapFactory to load a reduced size bitmap.