I load image from url. it' ok, but when long time it error outofmemory: bitmap size exceeds vm budget. here my code
BitmapFactory.Options bmOptions;
bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
//from web
try {
Bitmap bitmap=null;
InputStream is=new URL(url).openStream();
BitmapFactory.decodeStream(is, null, bmOptions);
is.close();
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = 1;
is = new URL(url).openStream();
bitmap = BitmapFactory.decodeStream(is, null, o2);
is.close();
return bitmap;
} catch (Exception ex){
ex.printStackTrace();
return null;
}
help me please!!
Try using the sampleSize of BitmapFactory.Options, this will reduce the size in memory of your image (and the quality)
But if your image is really too big, I think that's there no miracle solution, the image is simply too big ...
Your problem is exactly what stated in error message: your image is too big to be loaded into memory.
Try with any other image. As this image is exceding the size of ur folder.
R u using Emulator to test it?
Just a idea, it could be this piece of code throws sometime a exception. This will causes that the InputStream will not release rthe asigned resources (memory leak).
if you close the Input stream in a finaly block this issue should be solved
Related
I'm trying to compress images selected by user from gallery for uploading. I saw that my camera pictures are over 5MB and I would like to compress them(same as facebook if possible). What I've been trying:
I let the user select the photo from gallery,get the uri and use this:
File file = new File(getRealPathFromURI(getActivity(), selectedImageUri));
long length = file.length();
Log.e("Filesize:", "Before: " + length);
if (file.getName().toLowerCase().endsWith("jpg")||file.getName().toLowerCase().endsWith("jpeg")){
Bitmap original;
try {
original = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), selectedImageUri);
length = sizeOf(original);
Log.e("Filesize:", "BeforeCompression: " + length);
ByteArrayOutputStream out = new ByteArrayOutputStream();
original.compress(Bitmap.CompressFormat.JPEG, 50, out);
Bitmap decoded = BitmapFactory.decodeStream(new ByteArrayInputStream(out.toByteArray()));
length = sizeOf(decoded);
Log.e("Filesize:", "AfterCompression: " + length);
} catch (IOException e) {
e.printStackTrace();
Log.e("Filesize:", "Error: " + e);
}
I did this to test if it was working first, but what I get in the console is:
/name.company.newapp E/Filesize:: Before: 4970874
/name.company.newapp E/Filesize:: BeforeConversion: 63489024
/name.company.newapp E/Filesize:: AfterConversion: 63489024
The size doesn't change at all. Is this the right approach ?
This happens because you're actually getting the size of memory used by the Bitmap object by calling sizeOf(bitmap) and not the actual file size.
As you should know, a bitmap operates with the number of pixels in an image. Even though you compress the image using a JPEG compression, the image's width and height do not change. Thus the number of pixels do not change and thus the Bitmap's size (in memory) would not change too.
However, if you save the compressed bitmap to a location in your hard disk and use File.length() to calculate the size of the compressed image, then you will notice the difference.
Please check the size before decoding and after compression:
length = sizeOf(original);
Also i would recommend you to flush and close the outputstream:
out.flush();
out.close();
Hope i could help!
Edit:
Please try the following method to decode your bitmap:
public static Bitmap decodeFile(File f,int WIDTH,int HEIGHT){
try {
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
final int REQUIRED_WIDTH=WIDTH;
final int REQUIRED_HEIGHT=HEIGHT;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HEIGHT)
scale*=2;
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}
You can change the width and height of your picture to make it smaller.
I have primarily a question,Can I set a byte[] as image in ImageView without converting back to Bitmap?
The OOM error is thrown when I decode the byte[] to bitmap and I am seeing this happening for images of 300 KB even. I have opted to use BitmapFactory.options (inSampleSize) to scale the image to avoid the exception.
But this alters the dimension (width especially) of my image which looks so bad in my application. Is there anyway to fetch the original image from DB without scaling or altering the image (of course without the risk of OOM error)??
Any help is appreciated..!!
Thanks in Advance.
Code that troubles:
ByteArrayInputStream imageStream = new ByteArrayInputStream(imageByteArray);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap ImageBmp = BitmapFactory.decodeStream(imageStream, null, options);
options.inJustDecodeBounds = false;
//checking if the image is too large we can resize it small in order to avoid Out of Memory error from the decodeStream method.
if(showfullImage(options.outWidth) && !isExternal)
{
options.inSampleSize = 2;
}
imageStream.close();
imageStream = new ByteArrayInputStream(imageByteArray);
ImageBmp = BitmapFactory.decodeStream(imageStream, null, options);
return ImageBmp;
I have a large number of resources in my drawable folder.All are having big size more than 500KB. I have to load all these 25 images all at once in a srollView. As usual I ran out of memory. Is there any way to reduce the size of the image programmatically.
I got this function but it's parameter is a File and I don't know how to create a File from a drawable.
private Bitmap decodeFile(File f){
Bitmap b = null;
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream fis = new FileInputStream(f);
BitmapFactory.decodeStream(fis, null, o);
fis.close();
int scale = 1;
if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
scale = Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
}
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
fis = new FileInputStream(f);
b = BitmapFactory.decodeStream(fis, null, o2);
fis.close();
} catch (FileNotFoundException e) {
}
return b;
}
I have to go thorugh this screen cyclically many times after a number of traversal the system is showing Low memory and it is removing the other views in the back from the stack , but I actually need it.
Please help me.
You can open an InputStream from your drawable resource using following code:
InputStream is = getResources().openRawResource(id);
here id is the identifier of your drawable resource. for eg: R.drawable.abc
Now using this input stream you can create a file. If you also need help on how create a file using this input stream then tell me.
Update: to write data in a file:
try
{
File f=new File("your file name");
InputStream inputStream = getResources().openRawResource(id);
OutputStream out=new FileOutputStream(f);
byte buf[]=new byte[1024];
int len;
while((len=inputStream.read(buf))>0)
out.write(buf,0,len);
out.close();
inputStream.close();
}
catch (IOException e){}
}
I like shortcuts so I prefer using this.
For creatting file from drawable see this.
Put this in your build.gradle
compile 'id.zelory:compressor:1.0.4'
And wherever you want to compress the image put
Bitmap compressedImageFile = Compressor.getDefault(context).compressToBitmap(your_file);
README.md provides much more information. Sorry for bringing the answer after 6 years.
bro there are two methods
Easiest one Use some 3rd party library
Or use Bitmap class to scale drawable down
just use Bitmap.createScaledBitmap method to compress drawables
steps :
// Step 1 Load drawable and convert it to bitmap
Bitmap b = BitmapFactory.decodeResource( context , resId )
// Step 2 reScale your Bitmap
Bitmap nBitmap = b.createScaledBitmap( getResources() , newHieght , newWidth , true );
// Step 3 Create a drawable from bitmap
BitmapDrawable drawable = new BitmapDrawable(nBitmap);
I highly recommend you to use 3rd part library because this method is very expensive
like https://github.com/Tourenathan-G5organisation/SiliCompressor
I took cue from #mudit's answer and created the drawable from Input Stream. And later loaded the drawable to the ImageView in the Adapter.
InputStream inputStream = mContext.getResources().openRawResource(R.drawable.your_id);
Bitmap b = BitmapFactory.decodeStream(inputStream);
b.setDensity(Bitmap.DENSITY_NONE);
Drawable d = new BitmapDrawable(b);
mImageView.setImageDrawable(d);
Here is the detailed version of solution
I am trying to display a jpg file from a server into an imageView. When I try to load a smaller image (300x400), there are no problems. But when I try to load a full size picture (2336x3504), the image will not load. The file size of the image is only 2mb. I do not get any errors in logcat and there are no exceptions thrown. It simply won't load the image. I also tried using this:
BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap=BitmapFactory.decodeStream(is,null,options);
This doesn't do anything to help load the large files, but it does resize the smaller image (like it is suppose to). I did add the large picture to my resources and tested it as if it was embedded in the app and it worked fine, just won't work on the server. I have been working all day on this and can't seem to figure out how to load these large pictures. Can anyone help me out with this? Thanks for any info.
Here is the link where I found the above code and have been playing with the other examples but still not getting it to work.
EDIT:
Here is the code I'm using, to load the image:
public static Bitmap getBitmapFromURL(String src) {
Bitmap bmImg;
URL myFileUrl = null;
try {
myFileUrl = new URL(src);
HttpURLConnection conn= (HttpURLConnection)myFileUrl.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize = 16;
bmImg = BitmapFactory.decodeStream(is, null, options);
return bmImg;
} catch (Exception e) {
// TODO Auto-generated catch block
Log.d("Error", e.toString());
return null;
}
}
Here is the logcat screenshot (couldn't figure out how to copy the text appropriately in eclipse) I cleared the log right before I hit the button to load the image. So all you see is what happens when I hit that button. I erased the company and app names (where you see "com.", assume its "com.mycompany.myapp".
It is not uncommon for BitmapFactory.decodeFromStream() to give up and just return null when you connect it directly to the InputStream of a remote connection. Internally, if you did not provide a BufferedInputStream to the method, it will wrap the supplied stream in one with a buffer size of 16384. One option that sometimes works is to pass a BufferedInputStream with a larger buffer size like:
BufferedInputStream bis = new BufferedInputStream(is, 32 * 1024);
A more universally effective method is to download the file completely first, and then decode the data like this:
InputStream is = connection.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is, 8190);
ByteArrayBuffer baf = new ByteArrayBuffer(50);
int current = 0;
while ((current = bis.read()) != -1) {
baf.append((byte)current);
}
byte[] imageData = baf.toByteArray();
BitmapFactory.decodeByteArray(imageData, 0, imageData.length);
FYI, the buffer sizes in this example are somewhat arbitrary. As has been said in other answers, it's a fantastic idea not to keep an image that size in memory longer than you have to. You might consider writing it directly to a file and displaying a downsampled version.
Hope that helps!
Devunwired's answer is right but out of memory error can occur if image size is too large, in that case we will have to scale down image, here is the code to scale down image after DevunWired's download image code
final BitmapFactory.Options options = new BitmapFactory.Options();
BufferedInputStream bis = new BufferedInputStream(is, 4*1024);
ByteArrayBuffer baf = new ByteArrayBuffer(50);
int current = 0;
while ((current = bis.read()) != -1) {
baf.append((byte)current);
}
byte[] imageData = baf.toByteArray();
BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options);
options.inJustDecodeBounds = true;
options.inSampleSize = 2; //calculateInSampleSize(options, 128, 128);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options);
Does it silently fail, or does it throw an exception or OutOfMemory error? Btw, if a jpeg is 2MB that doesn't mean it'll take up 2MB of memory. 2MB is the compressed size, and since Android is working with a Bitmap, the 2336 x 3504 will take up approximately 2336 x 3504 x 4 bytes in memory. (2336 x 3504 x 4 = 32,741,376). Downsampling 8 times still might not be enough, especially if you have other bitmaps in memory at the time.
In my application I load a couple of images from JPEG and PNG files. When I place all those files into assets directory and load it in this way, everything is ok:
InputStream stream = getAssets().open(path);
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null);
stream.close();
return new BitmapDrawable(bitmap);
But when I try to load the exact same images from sd card, I get an OutOfMemory exception!
InputStream stream = new FileInputStream("/mnt/sdcard/mydata/" + path);
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null);
stream.close();
return new BitmapDrawable(bitmap);
This is what I get in the log:
11-05 00:53:31.003: ERROR/dalvikvm-heap(13183): 827200-byte external allocation too large for this process.
11-05 00:53:31.003: ERROR/GraphicsJNI(13183): VM won't let us allocate 827200 bytes
...
11-05 00:53:31.053: ERROR/AndroidRuntime(13183): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
11-05 00:53:31.053: ERROR/AndroidRuntime(13183): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
...
Why can this happen?
UPDATE: Tried both of these on real device - it seems that I can't load more than 12MB of bitmaps into whatever is called "external memory" (this is not an sd card).
I tried all the approaches mentioned here & at other resources but I came to the inference that setting ImageView's reference to null will solve the issue:
public Bitmap getimage(String path ,ImageView iv)
{
//iv is passed to set it null to remove it from external memory
iv=null;
InputStream stream = new FileInputStream("/mnt/sdcard/mydata/" + path);
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null);
stream.close();
stream=null;
return bitmap;
}
& you are done!
Note:Though it may solve above problem but I would suggest you to check Tom van Zummeren 's optimized image loading.
And also check SoftReference: All SoftReferences pointing to softly reachable objects are guaranteed to be cleared before the VM will throw an OutOfMemoryError.
When doing a lot with bitmaps, don't debug the app - just run it. The debugger will leave memory leaks.
Bitmaps are very expensive. If possible, scale them down on load by creating BitmapFactory.Options and setting inSampleSize to >1.
EDIT: Also, be sure to check your app for memory leaks. Leaking a Bitmap (having static Bitmaps is an excellent way to do that) will quickly exhaust your available memory.
Probably nothing wrong with your API usage, I guess all we can do is infer that using the AssetManager involves less behind-the-scenes heap allocation than opening a random file from the SD card.
800KB is a serious allocation in anybody's book... this will doubtless be for the decompressed image pixels. Given that you know the size of the image, what depth is it? If it's 32bpp then try overriding that using inPreferredConfig.
This is a fairly common issue which all of us face while loading images from the sdcard.
The solution as I found was to use inJustDecodeBounds first while loading the image using decodeFileDescriptor . That would not actually decode the image, but give the image size. Now I can scale it appropriately(using the options) so as to resize the image for the display area. Its needed because low memory on the phone can be easily taken over by your 5MP image. This I believe is the most elegant solution.
There are two issues here....
Bitmap memory isn't in the VM heap but rather in the native heap - see BitmapFactory OOM driving me nuts
Garbage collection for the native heap is lazier than the VM heap - so you need to be quite aggressive about doing bitmap.recycle and bitmap =null every time you go through an Activity's onPause or onDestroy
Instead of loading it from the SD Card directly, why not move the image to the cache in the phone's internal storage using getCacheDir() or use a temp directory to store the images in?
See this, this on external memory usage. Also, this article may be of relevance to you.
Use the below code and you will never get the following error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
BitmapFactory.Options bounds = new BitmapFactory.Options();
bounds.inSampleSize = 4;
myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bounds);
picturesView.setImageBitmap(myBitmap);
The best solution i found and edited according to my need
public static Bitmap getImageBitmap(String path) throws IOException{
// Allocate files and objects outside of timingoops
File file = new File(thumbpath);
RandomAccessFile in = new RandomAccessFile(file, "rws");
final FileChannel channel = in.getChannel();
final int fileSize = (int)channel.size();
final byte[] testBytes = new byte[fileSize];
final ByteBuffer buff = ByteBuffer.allocate(fileSize);
final byte[] buffArray = buff.array();
#SuppressWarnings("unused")
final int buffBase = buff.arrayOffset();
// Read from channel into buffer, and batch read from buffer to byte array;
long time1 = System.currentTimeMillis();
channel.position(0);
channel.read(buff);
buff.flip();
buff.get(testBytes);
long time1 = System.currentTimeMillis();
Bitmap bmp = Bitmap_process(buffArray);
long time2 = System.currentTimeMillis();
System.out.println("Time taken to load: " + (time2 - time1) + "ms");
return bmp;
}
public static Bitmap Bitmap_process(byte[] buffArray){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inDither=false; //Disable Dithering mode
options.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared
options.inInputShareable=true; //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
options.inTempStorage=new byte[32 * 1024]; //Allocate some temporal memory for decoding
options.inSampleSize=1;
Bitmap imageBitmap = BitmapFactory.decodeByteArray(buffArray, 0, buffArray.length, options);
return imageBitmap;
}
Thanks to all the threads, I've found a solution that works for me on a real device.
The tricks are all about using
BitmapFactory.Options opts=new BitmapFactory.Options();
opts.inSampleSize=(int)(target_size/bitmap_size); //if original bitmap is bigger
But for me this was not enough. My original image (taken from the Camera app) was 3264x2448. The correct ratio for me was 3, since i wanted a plain VGA image of 1024x768.
But setting inSampleSize to 3 was not enough: still out of memory exception.
So in the end I opted for a iterative approach: I start from the computed correct size, and increase it until I stop having a OOM exception.
For me it was at sample of 4.
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
// o2.inSampleSize = scale;
float trueScale = o.outWidth / 1024;
o2.inPurgeable = true;
o2.inDither = false;
Bitmap b = null;
do {
o2.inSampleSize = (int) trueScale;
Log.d(TAG, "Scale is " + trueScale);
try {
b = BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (OutOfMemoryError e) {
Log.e(TAG,"Error decoding image at sampling "+trueScale+", resampling.."+e);
System.gc();
try {
Thread.sleep(50);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
trueScale += 1;
} while (b==null && trueScale < 10);
return b;
You must not depends on the GC to recycle your bitmap memory.
You must clearly recycle the bitmap when it is not needed.
See the Bitmap method:
void recycle()
Free up the memory associated with this bitmap's pixels, and mark the bitmap as "dead", meaning it will throw an exception if getPixels() or setPixels() is called, and will draw nothing.
Try this another way...
Bitmap bmpOrignal = BitmapFactory.decodeFile("/sdcard/mydata/" + path");
Allows inSampleSize resize the final read image.
getLength() of AssetFileDescriptor allows get size of file.
You can vary inSampleSize according to getLength() to prevent OutOfMemory like this :
private final int MAX_SIZE = 500000;
public Bitmap readBitmap(Uri selectedImage)
{
Bitmap bm = null;
AssetFileDescriptor fileDescriptor = null;
try
{
fileDescriptor = this.getContentResolver().openAssetFileDescriptor(selectedImage,"r");
long size = fileDescriptor.getLength();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = (int) (size / MAX_SIZE);
bm = BitmapFactory.decodeFileDescriptor(fileDescriptor.getFileDescriptor(), null, options);
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try {
if(fileDescriptor != null) fileDescriptor.close();
} catch (IOException e) {}
}
return bm;
}