I am coding an app where I am going to acquire bitmaps from URLs using inputstreams. Before they have been loaded, however, I require a placeholder. My current plan is to first get the bounds of the bitmap (using Options and inJustDecodeBounds) and use this to set the placeholder aspect ratio and size.
However, if it takes the same amount of time to do this kind of request as it takes to simply request the actual bitmap, then I may as well simply request the actual bitmap.
So does setting inJustDecodeBounds to true in an instance of BitmapFactory.Options reduce the time taken for the request?
The using of inJustDecodeBounds reduce the required amount of memory to get only the bitmap information.
It's not a question about speed.
Related
I have read that HttpUrlConnection sends GET request when connection is made. Also then I can retrieve an instance of InputStream to read that resource. Does this mean that whole resource file is downloaded as the connection is made?
What I want to achieve is to set an ImageView image to remote image from the web. However my idea is to do this in memory-friendly way and calculate inSampleSize for BitmapFactory. In order to calculate that size - I need view dimensions and remote image dimensions. Remote image dimensions may be retrieved this vay (basically it should not load an image into memory):
BitmapFactory.Options options = new BitmapFactory.Options ();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream (inputStream, null, options);
However once read inputStream can not be reused in this case (or can it?). Also HttpUrlConnection returns the same instance of InputStream which means that if I want to read an image again (and load only the size I need using inSampleSize option) - I have to re-connect.
I want to be able to load large images, however as I have heard - HTTP requests are slow so is it worth it to send a second request? Also I don't know if the whole image is downloaded anyway even if I am reading only image info for the first time and not loading the whole thing.
If it is not worth it I think the only way will be to copy whole image into memory, get it's dimensions, read only the size I need and finally clean up the memory. Witch would be pretty memory expensive for the short period of time.
For loading images from remote web use Android Smart Image View..
it Load Images from URLs in memory friendly way
http://loopj.com/android-smart-image-view/
By following this link, I have written the following code to show a large image bitmap from sdcard.
try {
InputStream lStreamToImage = context.getContentResolver().openInputStream(Uri.parse(imagePath));
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(lStreamToImage, null, options);
options.inSampleSize = 8; //Decrease the size of decoded image
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeStream(lStreamToImage, null, options);
} catch(Exception e){}
image.setImageBitmap(bitmap);
But it is not returning the bitmap(I mean it returns null). In logcat it is showing the below message repeatedly
08-02 17:21:04.389: D/skia(19359): --- SkImageDecoder::Factory returned null
If I will comment the options.inJustDecodeBounds line and rerun it, it works fine but slowly. The developer guide link I provided above says to use inJustDecodeBounds to load bitmaps efficiently.
Please tell me where I am doing wrong.
inJustDecodeBounds does not load bitmaps. That's the point of it. It loads the dimensions of the bitmap without loading the actual bitmap so you can do any pre-processing or checking on the bitmap before you actually load it. This is helpful is you, say, were having memory issues and you needed to check if loading a bitmap would crash you program.
The reason your bitmap might be loading slowly is because it's probably very large and SD cards are very slow.
EDIT:
From the documentation:
If set to true, the decoder will return null (no bitmap), but the out... fields will still be set, allowing the caller to query the bitmap without having to allocate the memory for its pixels.
Edit 2:
Looking at your code with the example provided by Google, it looks like you are doing relatively the same thing. The reason it's returning null is possibly your InputStream has been modified in the first decoding and thus not starting at the beginning of the bitmap's memory address (they use a resource ID rather than InputStream.
From the code you supplied here, here's what I've figured. You are ALWAYS setting a sample size to 8 regardless of what the first decoding gives you. The reason Google decodes the first time is to figure out what the actual size of the bitmap is versus what they want. They determine that the bitmap is ZxZ dimensions and they want YxY dimensions, so they calculate the samplesize that they should use from the second decoding. You are not doing this. You are simply retrieving the dimensions of the bitmap and not using them. THEN, you set the sample size to a hard-coded 8, swapping it to a hard-coded ARGB_4444 bitmap, then decoding the full bitmap in to memory. In other words, these three lines are not being used:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(lStreamToImage, null, options);
Setting inJustDecodeBounds merely gives you the bitmap's dimensions without putting the bitmap in to memory. It doesn't make it more efficient. It's meant to allow you to load bitmaps in a smaller memory space if they are too big because you can pre-decide what size it should be without decoding the whole thing).
The reason decoding the bitmap is slow might merely be a CPU thing. Depending on the size of your bitmap, you're loading the bitmap from an InputStream from the SDcard which is a slow operation in itself.
This might sound like a strange/silly question. But hear me out.
Android applications are, at least on the T-Mobile G1, limited to 16
MB of heap.
And it takes 4 bytes per pixel to store an image (in Bitmap form):
public void onPictureTaken(byte[] _data, Camera _camera) {
Bitmap temp = BitmapFactory.decodeByteArray(_data, 0, _data.length);
}
So 1 image, at 6 Megapixels takes up 24MB of heap. (Cue Memory overflow).
Now I am very much aware of the ability to decode with parameters, to effectively reduce the size of the image. I even have a method which will scale it down to a desired size.
But what about in the scenario when I want to use the camera as a quality camera!
I have no idea how to get this image into the database. As soon as I decode, it errors.
Note: I need(?) to convert it to Bitmap so that I can rotate it before storing it.
So to sum it up:
Limited to 16MB of heap
Image takes up 24MB of heap
Not enough space to take and manipulate an image
This doesnt address the problem, but I Recommend it as a starting point for others who are just loading images from a location:
Displaying Bitmaps on android
I can only think of a couple of things that might help, none of them are optimal
Do your rotations server side
Store the data from the capture directly to the SDCARD without decoding it, then rotate it chunk by chunk using the file system, then send that to your DB. Lots of examples on the web (if your angles are simple, 90 180 etc) though this would be time consuming since IO operations against SDCARD's are not exactly fast.
When you decode drop the alpha channel, this may not solve you issue though and if you are using a matrix to rotate the image then you would need a target/source anyway
Options opt = new Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
// Decode the raw camera a bitmap with no alpha channel
bmp = BitmapFactory.decodeByteArray(raw, 0,raw.length, opt);
There may be a better way to do this, but since your device is so limited in heap etc. I can't think of any.
It would be nice if there was an optional file based matrix method (which in general is what I am suggesting as option 2) or some kind of "paging" system for Android but that's the best I can come up.
First save it to the filesystem, do your operations with the file from the filesystem...
I have a photo on disk with dimensions 2560 x 1920. This is often too large to load into memory, so I'm trying to use BitmapFactory.Options.inSampleSize to conserve memory. From the docs:
inSampleSize: If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
This is how I use it:
BitmapFactory.Options optsDownSample = new BitmapFactory.Options();
optsDownSample.inSampleSize = 3;
Bitmap bmp = BitmapFactory.decodeFile(path, optsDownSample);
but the app still sometimes crashes on the last line there, and from logcat I can see it's trying to allocate ~5mb, and I suspect this is because the downsampling is not really being honored.
Anyone else know what could be going on here, am I using inSampleSize incorrectly?
Thanks
I'm also struggling understanding how to use the BitmapFactory.Options, based on all the documentation I've read I believe you are just missing the optsDownSample.inJustDecodeBounds = false; as indicated on the Android Developers site.
Best of lucks!
I have an Activity which takes photos (with full possible resolution, so quite large), the application have then the chance to analyze them. Only one photo is handled at a time. The problem is that I run in to a "Out of memory" after 4 - 5 photos. I see
dalvikvm-heap Out of memory on a 5070745-byte allocation
(the byte size varies) followed by
Camera-JNI Couldn't allocate byte array for JPEG data
My application does not crash but as it appears to me the Camera simply becomes unable to deliver the images from this point on. I pay attention to memory usage in my application but here it seems that there is a memory leak somewhere outside and I'm asking me how can I cope with this. Any solution approaches existing for this?
This may not be exactly what you are trying to do, but in order to display multiple large pictures (4-6 MB) on a grid view, I found this code to create a smaller bitmap with lower quality, to avoid out-of-memory situations:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 5;
options.inPurgeable = true;
options.inInputShareable = true;
Bitmap bm = BitmapFactory.decodeFile("/sdcard/myapp/" + filesFound.get(position), options);
The options are the important part, and by varying the settings, I managed to take memory down from where it would crash, to around 28MB when I started using the inSampleSize. It further went down with the inPurgeable and inInputShareable settings set to true. My activity is still sluggish, but it's much better.
For your application, if it can analyze a bitmap, the above code may shrink down the memory usage enough to make it work. I'm still new to Android, so it's possible this may not work at all.. ;-).
Regards,
Kevin
Since you run out of memory after 4-5 pictures you probably arent calling yourBitmap.recycle(); after it has been saved to the SD-card?
Also in the onPictureTaken() method you could save the tempData from the picture into a bitmap using the Bitmap.Config.RGB_565 instead of ARGB(default) if you don't need the alpha channel.
// Create options to help use less memory
Options opt = new Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
// Decode the tempdata into a bitmap, with the options as the last argument
bitmapFromRawCameraTempData = BitmapFactory.decodeByteArray(rawCameraTempData, 0, rawCameraTempData.length, opt);