I have activity that parses XML feed (with news) and loads parsed data (text and image url) in WebView's, which are inside gallery widget.
Something like this:
mimeType = "text/html";
encoding = "utf-8";
String html = "<img src=\""
+ newsImageUrl.get(position)
+ "\" style=\"max-width:200px; max-height:200px;\" align=\"left\""
+ "/>" + newsDescription.get(position);
data.loadDataWithBaseURL("", html, mimeType, encoding, "");
Everything works fine, but sometimes inside news feed there is this BIG IMAGES. Well, they dont cause any problems unless you start to rotate a phone. And after couple of orientation changes we have happy Out Of Memory exception.
Well, in Android, OOM is always hiding somewhere near Bitmas, that's why most people use BitmapFactory with inSampleSize, or something more exotic. Well, supposedly, I can download and resample images, and then just load them from SD card. But for now I will try to avoid it.
Anyway, the question is - what to do with large images when they are being loaded in WebView? Is there a way to resize them (not only visually)? Is there any way to clear memory occupied by WebView onOrientationChange (webview.freeMemory() doesn't really helps).
Actually, you don't have so many choices. Freeing memory doesn't help as your images are still allocated in memory, and the images are too big to fit.
The only workable way is to reduce their size, either at the source (this way you have nothing to do programmatically) or at download time, before you display them (you save them scaled down to the SD and you display these local copies instead of the source images).
This depends of whether you have to use the full-scale images at some point. If not, there's absolutely no point in keeping them so large.
Anyway, the problem was not in webview large images.
I downscaled app and apparently the problem was in my gallery widget design. To be more specific in graphical interface elements that surrounds it. Even with empty gallery after couple orientation changes I got oom.
And I found the reason to it only today. I used /drawables/ folder which downscales all images to mdpi. And all phones I tested it on was hdpi. Apparently android uses some maybe no so efficient scaling method, and it's leaking somewhere there.
I moved all my drawables to hdpi and mdpi folders and crashes magically stopped.
So, conclusion - avoid using /drawables/ folder.
Related
I'm developing a music Player, and since i have started I've a question: Should i use Drawable or Bitmap?
Considered that:
I have to load many images, one for every song (The image is loaded only when it is the turn of that song).
The image loaded has to be visualized on the Widget and Notification too.
The same image is displayed on more activities.
The image loading has to be as fast as possible.
The memory usage have to be as low as possible, even after have loaded several images.
I was thinking to temporarly save images of actual playlist, so I don't have to reload it every time is played the same song.
I am using Glide library to have better performance in this moment, and I am working with bitmap. Any suggestion?
Both graphics objects works almost everywhere.
If you loading from file system (downloads) you need to load Bitmaps to create BitmapDrawables so you may apply bitmap directly in this case.
If you are using from the res folder you can load as Drawables.
Drawable is just a wrapper for things that can be drown (colors, vector images, layer-lists, selectors and bitmaps).
Whereas bitmaps are actually representation of images in Android.
So, your question is invalid, we can not equate bitmap and Drawable.
I have a huge image to render (1024x25373p) cut into 99 images of 1024x256p.
I have tried to use a ListView, but without success : it crashes when scrolling, whithout any error (exept one line saying the proccess was stopped).
So, my question is, how do I render this huge image ?
Please note that I have tried to use TileView by moagrius, without success (I can't get it to work with navigation drawer)
As a suggestion ,
If this is listview, you may not need such larger size images "1024x256p" .
Actual size of your imageView may much for less than that. So its a wastage of
heap if you try to load those images directly without some processing.
Definitly you need to do some scaling down for your image based on actual size that you need. Nice exapmple and code has been published on the official doc
You need to deallocate memory or clear the all bitmaps which are not visible on the listview for particular moment.
You can use progressing loader in order to load your images in to the listview. Then loading will be happen based on the scrolling.
Also you can define lager heap enable in your manifest though it is not recommended but has to do in highly memory consumable apps.
android:largeHeap="true"
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.
I am looking for a library or some idea on how I can load an image by parts from the disk or a url straight to the disk then in parts again to the ram. So the two ways I see that this can be done is loading the whole image onto the disk by reading and writing it from the url directly using the ram only for the buffer then when the image is on the disk some how creating bitmaps of only parts of the image, that way I DO NOT load it all and putting those in a ListView.
The issue is that I am dealing with extremely long images (10K + pixels long w/ a width of 4-600) and they hog up lots of ram if loaded all in one bitmap. I can not just scale them down like the Google android tutorial does in the handling large bitmaps section as that results in a width too small to deal with. So if I can somehow generate small bitmap slivers on the disk I can use them by loading them in a ListView preventing loading the image as a whole into ram.
Right now I am breaking the long images into slivers from a bitmap and I realized that that isn't really accomplishing what I am trying to do as the whole image is loaded into a bitmap in memory and is then broken up, then GC (So I am using up the full abmount of ram anyways). I am testing on a new top of the line android phone and the app works fine, but the heap size reaches 80mb+ with the larger images temporarily in ram as it breaks down the bitmap and this will be an issue with devices that have lower heap limits
You can try using this class, support from 2.3 http://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html
If you are using Java you can work with InputStreamReader and OutputStreamWriter. Method read() accepts the buffer as one of the parameters, its length can be anything suitable. Of course you can create a new file for each buffer being written.
Is it what you're looking for?
edit
well its not. have you seen this Strange out of memory issue while loading an image to a Bitmap object ?
If you have control over the server from where you are fetching data, throw in another field in your response, such that it returns a thumbnail/smaller image. The server can then generate the required thumbnails for you, without you bothering about it.
Decoding bitmaps on the fly might be expensive, most of the times. If you can't change anything on the server, download and save the images, and after saving, generate their corresponding thumbnails and save them as well. In your list, use the thumbnails. Also, save the information about which images have been cached, and whose thumbnails you already have. This might look like a lot of work, but depending on the use case, this can be a better approach dealing with large images.
Problem with downsizing?
Well, you can come up with some kind of logic as to generate thumbnails, based on the original size of the image. For longer(vertically long images), you could use BitmpapRegionDecoder (http://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html) as #Binh Tran has suggested.
Try maybe encoding the image to make the size small.
For anyone who's wondering, the gallery is http://www.spore.com/sporepedia.
My app uses the Spore API to get the 100 newest creations, then displays them in a GridView. The text data about the creations is easy to store, but the images present a problem.
As far as I know, I can either keep the images in a Hashtable or grab them every time they are viewed. Neither of these will work - the Hashtable quickly presents an OutOfMemoryError, and the constant reloading causes a lot of load on the server and a lot of lag on the client.
Is there a better way to store the images?
First Don't get 100 at a time, there is no way your displaying 100 images in a gridview and having them be a usefully visible size. Retrieve the images asynchronously and get a screen full or a screen and a half at a time. I'm guessing you can display 6 to 9 images of a decent size per screen with supporting text/UI elements, you may even want to choose how many to display based on the handsets screensize/resolution. So you should probably be getting 9 to 12 images at a time.
Second, i don't know what resolution these images are coming in at, but it sounds like 'big'. If the API supports it receive only a thumbnail version for you grid view. If not, what i would probably do is: when you receive an image from the API, first create a new image scaled down to the size needed for your thumbnail that goes into the grid view, keep this in memory. Then create a new image scaled down to the size you would need for your 'detail' screen (if you have one) and cache this to the SD card and free it from ram as well as the original source image, this scaling should probably occur in a separate thread to avoid it impacting the responsiveness of your UI thread. Depending on the usage pattern, I would probably also cache the thumbnails to the SD card so it would be cheap to free the ram they use in onStop/onPause and reload them in onStart/onResume. IT also sounds like you downloading a 'top 100' or something of the sort. Since i wouldn't expect an entires new top 100 on every application use you could save a lot of network traffic by caching the images and information to the SD card and only downloading new entries each time the program runs.
Alternatively a process like:
Receive Image -> Scale in place to detail size -> cache detail size -> scale detail size to thumbnail size in place -> display thumbnail in gridview while caching thumbnail asynchronously
Would use even less memory since there is only ever 1 copy of the image in memory, but it would slow the response of the gridview as the image has to be scaled twice before it gets displayed. It may be fast enough to not matter or you may be able to play tricks with the grid view by having it display the large image (scaled internally) while the thumbnail is generated in the background, switching the gridview to the thumbnail when its ready. You'll just have to try it to determine if its fast enough.