How to work around Android Bitmap memory inefficiencies? - android

We are working on an native Android app that handles large images (5MP+) from the phone's gallery, eventually encoded base 64, JSONized and sent to an upstream server. We've used some of the guidance given in other StackOverflow questions. We've tried hard to keep memory usage to a minimum, following logcat closely, debugging, etc. And while we've gotten under control the "80% use case" we still are getting FATAL errors in certain cases (when the user picks a photo, then cancels, chooses a different one, for instance).
We've found that when importing a 5MP Bitmap, its size quadruples in the Heap (i.e. a 7Mb jpeg becomes 28Mb in memory). We've used tricks to convert to Base64 as efficiently as possible. We've made sure the JSON parser is not leaking and so forth.
So to get back to my original question, is there a way to circumvent Android's Bitmap handling?

eventually encoded base 64, JSONized and sent to an upstream server
Rewrite your Web app to support binary payloads, so that you do not have to do ridiculous stuff like this. Upload JSON-encoded metadata in one request, then upload the image in its original format in a separate request, if need be. Or, use multipart upload to do both in one shot, leaving the image in its original format.
We've found that when importing a 5MP Bitmap, its size quadruples in the Heap (i.e. a 7Mb jpeg becomes 28Mb in memory)
That is because PNGs, JPEGs, and the like are compressed, and the image needs to be uncompressed to be displayed.
is there a way to circumvent Android's Bitmap handling?
You do not say if you are trying to display the image or not. If you are, use BitmapFactory with an appropriate BitmapFactory.Options to scale the image.
If you are not trying to display the image, once you rewrite the Web app to accept a binary payload, you should not need to load the entire image into memory. Just upload it in the format that it already is in, reading in chunks at a time (e.g., 8KB) to write to the OutputStrem of your HTTP PUT (or whatever) for the upload.

Related

Image compression before uploading in android

I have a image uploading module in my app where user can select an image from the gallery. The problem is the size of the image can be upto 10MB. Which is very large, I want to apply some compression technique to these images before uploading them.
I did some research on the internet and found some libraries like ImageMagick, ImgMin which allows easy optimization of the images. Is there any way I can use them in my android project without the involvement of any backend server.
References:
ImgMin
https://github.com/rflynn/imgmin
ImageMagick
http://www.imagemagick.org/script/index.php
An easy option you can try is this method from the Bitmap class.
You can select the compression format of a bitmap and to optimise either the quality, or the file size. A downside is that the you need to get a Bitmap instance to start the compression, which may be something you don't want to do.

Preferred way to fetch/show images on Android?

Lets consider the following, I have an app with several dozen photos I want to show the user at any given time.
Right now I'm creating multiple files for each image, sizing them for different screen sizes and storing them in their respective drawable folders.
It's increasing the size of my app dramatically.
So here is my question: Is it possible to store the images on a server and use an image library like Picasso, Fresco or something else (open to anything) to fetch that image and scale it down for the device it's running on without risking running out of memory?
I'm worried that fetching a large image, loading it into memory and then resizing it will cause the same problem as trying to display it on older devices with little memory available to them.
You can write methods to request different images sizes from your server based on client info. Just write a method to measure the screen size and then request the appropriate image based on a URL endpoint (like http://myimageserver.com/images/ldpi/image1.png).
You can do optimization post-download, such as scaling, before saving the image to a local file store.
Using a reputable image loading library is a valid method (my own favourite is Glide).
The answer to your question really depends on the number of images you want to show! If there are lots, then yes storing them on a server is probably best, but also the most time-consuming and expensive (both in time and money).
Your other (easier) option is to keep the originals in the assets folder, and use your image loader to scale and load them for you. The correct path for an image in your assets folder is file:///android_asset/your_image_here.jpg. This way, you're only keeping one version of each photo in your apk and they'll load much faster.

removing image data from memory

I have to believe there's a way to clear image data from memory once it's no longer required, but despite exhaustive searching I can't find a solution. Both stack and the google android dev list are full of questions regarding OOM errors, specifically "bitmap size exceeds VM budget", but I still don't see a clear answer.
I understand there are hard memory limits on devices, and I understand it's not realistic to load up and display or cache large amounts of image data, but there should be away to discard data that's no longer required.
For example, imagine this very basic hypothetical app, that emulates a lot of the behavior of the native gallery app:
An image gallery that allows the user to peruse images from a remote server.
There might be any number of images on that server.
The app displays 1 image at a time, and allows a user to go back or forward 1 image at a time through button presses or swiping.
There'd be a maximum of 3 images rendered at any one time (so the user can see the one immediately to the left or right of the current image when swiping). All other image data should be discarded.
Images are loaded using URL.openStream and Drawable.createFromStream or BitmapFactory.decodeStream. Streams are closed appropriately.
Images are sized appropriately on the server before being fetched.
Loading happens in AsyncTasks. Tasks that are no longer needed (due to moving away from an image with an incomplete task) are cancelled. Any references in the AyncTask are WeaklyReferenced.
When any image is no longer required, it's "cleared" via:
getBackground().setCallback(null)
Listeners are set to null
setImageDrawable/Bitmap(null)
removeView
This simple construct, that takes into account all the suggest practices I'm aware of, will inevitably crash with an OOM error at some point. Using BitmapFactory.Options inSampleSize and inPreferredConfig will delay the inevitable, but not forever, and at the cost of image quality. In this example, I've used remote images, but the issue exists with images stored in /assets/ or in internal memory, etc.
My feeling is that if we can display X amount of image data at one point, and we take all steps to remove that image data from memory, we should be able to display that same amount of data later, without having to compensate for what has happened before.
With the sheer quantity of questions about this very issue, I'd hope to have a standard solution documented, but if there is one, I can't find it. I've seen answers posted by Romain Guy, who otherwise seems very generous with his knowledge and active in the community, that say something like "Simple. Don't use so much memory". OK. Tell me how.
I should also mention that System.gc does nothing to help this. I'm also aware of bitmap.recycle, but unless I'm mistaken this can't be used in this fashion.
Am I missing something fundamental? Is there a way to discard image data once it's no longer being used? What is missing from the above to create a simple photo gallery? Assuming the built-in gallery app uses the framework and not the NDK, I imagine there has to be a way...
TYIA.
/this question has also been posted on the android developer google group list.
Through my work with Prime I found a few tips, one of which you have not mentioned. When you decode your Bitmaps make sure to use the inPurgeable and inInputShareable flags in your BitmapFactory.Options. That will help a little bit but I would recommend you look at my implementation of image loading in Prime. I use it in all of my products without any memory issues. I have found that 95% of memory problems are from the incorrect usage of the Bitmap class.
There is a very detailed article about the use of bitmaps on the android developer website.
Did you look at it ?
It explains how to load, cache and display bitmaps efficently and how to get rid of this famous OutofMemoryError.
There is also a sample application from an image gallery.
I think that's what you're looking for.

Fastest way to read/write a Bitmap from/to file?

I'm currently writing Bitmaps to a png file and also reading them back to a Bitmap. I'm looking for ways to improve the speed at which writing and reading happens. The images need to be lossless since I'm reading them back to edit them.
The place where I see the worst performance is the actual BitmapFactory.decode(...).
Few questions:
1. Is there a faster solution to read/write from file to a Bitmap using NDK?
2. Is there a better library to decode a Bitmap faster?
3. What is the best way to store and read a Bitmap?
Trying to resolve the best/fastest possible way to read/write image to file came down to using plain old BitmapFactory. I have tried using NDK to do the encoding/decoding but that really didn't make a difference.
Essentially the format to use was lossless PNG since I didn't want to loose any quality after editing an image.
The main concept from all this was that I needed to understand was how long encoding took versus decoding. The encoding numbers where in the upper 300-600ms, depending on image size, and decoding was just fast, around 10-23ms.
After understanding all that I just created a worker thread that I passed images needing encoding and let it do the work without affecting the user experience. The image was kept cached in memory just in case it was needed right away before it was completely encoded and saved to file.

Android uploading pictures to server in most efficient way

I need to get images along with other data (very similar to email with attachements) to the server. I also need to do it in reliable manner so I can retry, etc on failure.
Server is WCF REST server and I do lot of other communications with it(JSON) but just got this new requirement to upload images.
Since I use JSON to post data to my server - I use GSON on Android side to serialize data.
Here is how I got it implemented so far (everything else works this way but I just started with images)
User filling activity fields (text data)
User takes some picture(s) via camera intents. Currently I just use 1 file for pictures
I take picture from SDCard, load/resize it - dispaly on ImageView and store in byte[]
User submits - I take all data along with images from byte[] and put it into Java object
Call GSON converter and serialize object
Save object into SQLite
AsyncTask looks in SQLite for records, opens cursor and get's text
AsyncTask creates HttpConnection and posts text data to my server.
THE END
Now to my problems..
Obviously on #3 - I "explode" ram with my byte arrays. Sometime I even feel my Nexus S becomes sluggish. But by doing that - I avoid filling SD card or app folder with many files. I take picture and than grab it. Next picture will overwrite previous one.
Step #5 IS slow. I didn't try custom serializer on GSON and instead of serializing byte array into something like [1,-100,123,-12] I can get much smaller size with Base64 but still. It will be slow. And I can have up to 20 images...
Step #6 is no problem. But with certain size (I tried 300px image) I started to get error in step 7 on OpenCursor
07-06 20:28:47.113: ERROR/CursorWindow(16292): need to grow: mSize = 1048576, size = 925630, freeSpace() = 402958, numRows = 2
07-06 20:28:47.113: ERROR/CursorWindow(16292): not growing since there are already 2 row(s), max size 1048576
07-06 20:28:47.113: ERROR/Cursor(16292): Failed allocating 925630 bytes for text/blob at 1,1
So, this whole thing is not something I like. Ideally I want all data to be uploaded in single piece to server.
I was thinking maybe storing images timestamped on SD card and store only their name in DB. Than I would process them right before sending to server. And on success I would delete those images. This kind of logic will make SQLite schema much more complex but maybe there is no better way?!
I guess I'm looking for best practice to deal with images. How to do followin with minimal memory/CPU usage:
Take picture
Display thumbnail
Resize
Send to server
EDIT 1:
Currently I'm researching possibility of uploading whole shizang as a multi-part MIME message. That would require adding some JAR's to my Android package. Also I'm not sure how effective will be Apache code to load images and sending them(I guess better than my code)
http://okandroidletsgo.wordpress.com/2011/05/30/android-to-wcf-streaming-multi-part-binary-images/
And that I would have to deal with parsing all this on WCF side since there is no way to do it with built-on .NET framework.
http://antscode.blogspot.com/2009/11/parsing-multipart-form-data-in-wcf.html
PLEASE TELL ME IF YOU TRIED THIS!
EDIT 2:
MIME is no good. There is no point since it serializes binary using Base64 which is same thing..
Nobody answered but here is what I figured hard way:
Rule #1: When dealing with images - avoid using objects/memory. Sounds obvious but it's not. I figured that resizing image to 800x600 is OK. Anything bigger - you may consider just leaving it as is because it is possible to do http stream on bigger file but it's hard to work with OOM exceptions when you load images into memory for processing
Rule #2: When use GSON - use JsonWriter to populate stream. Otherwise memory will explode. Than pass that stream into HttpClient. JsonWriter will write in chunks and data will be sent as it process.
Rule #3: See rule #2. It will work OK for multiple small images. This way GSON will serialize them 1 by one and feed into stream. Each image WILL be loaded int memory anyway.
Rule #4: This is probably the best solution but requires more coordination with server. Images sent 1 by 1 before message sent to server. They sent as stream without any encoding. This way they don't have to be base64 encoded and they don't have to be loaded in memory on device. Size of transmission will be smaller as well. When all images sent - post main informational object and collect all package together on server.
Rule #5: Forget about storing BLOB in SQLite
Bottom line:
It is much cheaper in term of resources to send images WITHOUT any resizing. Resizing makes sense only when Image get's to about 800x600-ish
Sending multiple images in a single package makes sense when image get's small like 600x400-ish
As soon as you need to upload files - start thinking streams everywhere. DO NOT load stuff into memory.

Categories

Resources