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.
Related
Here is my code:
File file = new File(Path to Jpeg File size is 700kb);
InputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(file));
}
catch (Exception e) {
// TODO: handle exception
}
bitmap =BitmapFactory.decodeStream(in);
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
Please help i get error in this copy line i want to make its ARGB_8888 image.Need Help :(
You need to reduce the memory usage.
From you code, you first decode stream to one bitmap, and then copy it, which means you create two large bitmap objects.
You don't need to decode and then copy it, you can try
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888
// You can try value larger than 1
options.inSampleSize = 2 // If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
// Decode bitmap
bitmap = BitmapFactory.decodeStream(in, null, options)
In this case, there's only one bitmap created. And you set inSampleSize to large values to reduce the loaded bitmap size.
I have read 100s of article about the OOM problem. Most are in regard to large bitmaps. I am doing a mapping application where we download 256x256 weather overlay tiles. Most are totally transparent and very small. I just got a crash on a bitmap stream that was 442 Bytes long while calling BitmapFactory.decodeByteArray(....).
The Exception states:
java.lang.OutOfMemoryError: bitmap size exceeds VM budget(Heap Size=9415KB, Allocated=5192KB, Bitmap Size=23671KB)
The code is:
protected Bitmap retrieveImageData() throws IOException {
URL url = new URL(imageUrl);
InputStream in = null;
OutputStream out = null;
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// determine the image size and allocate a buffer
int fileSize = connection.getContentLength();
if (fileSize < 0) {
return null;
}
byte[] imageData = new byte[fileSize];
// download the file
//Log.d(LOG_TAG, "fetching image " + imageUrl + " (" + fileSize + ")");
BufferedInputStream istream = new BufferedInputStream(connection.getInputStream());
int bytesRead = 0;
int offset = 0;
while (bytesRead != -1 && offset < fileSize) {
bytesRead = istream.read(imageData, offset, fileSize - offset);
offset += bytesRead;
}
// clean up
istream.close();
connection.disconnect();
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeByteArray(imageData, 0, bytesRead);
} catch (OutOfMemoryError e) {
Log.e("Map", "Tile Loader (241) Out Of Memory Error " + e.getLocalizedMessage());
System.gc();
}
return bitmap;
}
Here is what I see in the debugger:
bytesRead = 442
So the Bitmap data is 442 Bytes. Why would it be trying to create a 23671KB Bitmap and running out of memory?
I have run into problems like this in the past. Android uses Bitmap VM and it is very small. Make sure you dispose your bitmap via bmp.recycle. Later versions of Android have more Bitmap VM but the version that I've been dealing with has a 20MB limit.
This may work. Shrinking the bitmaps to lesser quality. I am not sure, but this may duplicate the image in memory, but could easily be worth a shot.
Bitmap image;
image = BitmapFactory.decodeByteArray(data, 0, data.length);
Bitmap mutableBitmap = image.copy(Bitmap.Config.ARGB_4444, true);
Canvas canvas = new Canvas(mutableBitmap);
My old answer below I don't think will work streaming.
Options options = new BitmapFactory.Options();
Options options2 = new BitmapFactory.Options();
Options options3 = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;///////includes alpha
options2.inPreferredConfig = Bitmap.Config.RGB_565 ;///////no alpha
options3.inPreferredConfig = Bitmap.Config.ARGB_4444 ;/////alpha lesser quality
image=BitmapFactory.decodeResource(getResources(),R.drawable.imagename,options);
image=Bitmap.createScaledBitmap(image, widthx,height, true);
It sounds like you've already done some reading on this subject, so I'll spare you the standard 'this is how you decode a bitmap' comments..
It jumps out at me that you're perhaps holding on to a reference of old bitmaps (perhaps the tile has moved off screen, but you still have a reference in an array somewhere and as a result it isn't being garbage collected?). This has bitten me really badly in the past - memory leaks are hard to debug.
There's a great Google I/O video over here that really helped me when I was having similar problems. It's around an hour, but will hopefully save you days later on.
It covers things like:
Creating heap dumps
Heap usage in DDMS
Using MAT to compare/analyze heap dumps
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
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 working on an Android app that displays photos which are downloaded from Flickr. I obtain a bitmap object from a byte array, which in turn is read from the relevant Flickr URL, as follows:
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inDither = true;
opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opt);
I then draw the bitmap onto a canvas in the onDraw method of a View object:
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
canvas.drawBitmap(bitmap, 0, 0, paint);
The problem is that the resulting picture is pixelated and I can't figure out why; I have tried a number of variations of the opt and paint objects with no luck. The difference between the picture displayed in my app and the picture at the original URL is roughly demonstrated by the following:
Bad image, see pixelation in top left corner http://homepages.inf.ed.ac.uk/s0677975/bad.jpg
Good picture, this is the expected result http://homepages.inf.ed.ac.uk/s0677975/good.jpg
Look e.g. at the clouds in the top-left corner to see the difference.
Note that JPEG pictures which are loaded from the project resources and drawn in a similar way display just fine, i.e. have no pixelation.
Can anybody give me a hint as to why this is happening?
To elaborate a little, the byte array is obtained from Flickr as follows; this is based on code from the Photostream app by Romain Guy:
InputStream in = new BufferedInputStream(url.openStream(), IO_BUFFER_SIZE);
final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
out = new BufferedOutputStream(dataStream, IO_BUFFER_SIZE);
copy(in, out);
out.flush();
final byte[] data = dataStream.toByteArray();
PS: I also posted a variant of this question on the android.developer Google group.
Thanks a lot for your suggestion -- now I am really puzzled! I did as you suggested and found that the image resulting directly from the downloaded byte array is indeed pixelated. However, this is downloaded from exactly the same URL which, when accessed on my computer, is NOT pixelated. Here is the corresponding Flickr URL:
http://farm3.static.flickr.com/2678/4315351421_54e8cdb8e5.jpg
Even stranger, when I run the same app in the simulator rather than on my phone (a HTC Hero), there is no pixelation.
How on earth is this possible?
Below is the code I use for loading a bitmap from a URL -- it is based on the Photostream app by Romain Guy, and it incorporates Will's suggestion to write the raw byte array to file:
Bitmap loadPhotoBitmap(URL url) {
Bitmap bitmap = null;
InputStream in = null;
BufferedOutputStream out = null;
try {
FileOutputStream fos = new FileOutputStream("/sdcard/photo-tmp.jpg");
BufferedOutputStream bfs = new BufferedOutputStream(fos);
in = new BufferedInputStream(url.openStream(),
IO_BUFFER_SIZE);
final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
out = new BufferedOutputStream(dataStream, IO_BUFFER_SIZE);
copy(in, out);
out.flush();
final byte[] data = dataStream.toByteArray();
bfs.write(data, 0, data.length);
bfs.flush();
BitmapFactory.Options opt = new BitmapFactory.Options();
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opt);
} catch (IOException e) {
android.util.Log.e(LOG_TAG, "Could not load photo: " + this, e);
} finally {
closeStream(in);
closeStream(out)
closeStream(bfs);
}
return bitmap;
}
private static void copy(InputStream in, OutputStream out) throws IOException {
byte[] b = new byte[IO_BUFFER_SIZE];
int read;
while ((read = in.read(b)) != -1) {
out.write(b, 0, read);
}
}
private static void closeStream(Closeable stream) {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
android.util.Log.e(LOG_TAG, "Could not close stream", e);
}
}
}
Am I going crazy here?
Best,
Michael.
Ok, so I finally get it: it appears that my mobile network does image compression to save bandwidth.
Hence a picture downloaded from my phone is of lower quality than the same picture downloaded from my computer.
That's a bummer for my app, but I don't suppose there is anything I can do about it. Sigh.
Thanks again for your input though!
Best,
Michael.
Write the raw bytes fetched from the URL to /sdcard/tmp.jpg, and view on your PC.
JPEG images are compressed in 8x8 (or 16x16) tiles. The 'pixelation' as you describe it is actually in these tiles, suggesting that the 'bad' image is a JPEG that is more aggressively compressed than the other.
So I'd anticipate that the actual issue is that the image being downloaded is a very low-quality version, e.g. one intended for thumbnailing/preview use-cases.
Some version of Android have a bug in Bitmap class and convert the Bitmap to RGB_565 upon some operations. This would manifest itself in artifacts similar to those on your picture. This would also explain the banding of the blue sky.
Also, have in mind that android attempts to "optimize" image by converting them to rgb_565 upon loading and even compiling in resource files. Take a look at:
http://android.nakatome.net/2010/04/bitmap-basics.html