1

i have a simple problem: Although i'm using sampleSize properly, my code doesn't even reach the BitmapFactorycode, since DefaultHttpClient is already throwing the exception.

Here is my code:

                DefaultHttpClient client = new DefaultHttpClient();
                HttpGet request = new HttpGet(mSongInfo.imageLarge);
                HttpResponse response = client.execute(request);
                int sampleSize = 1;
                while (response.getEntity().getContentLength() / sampleSize
                        / sampleSize > 100 * 1024) {
                    sampleSize *= 2;
                }
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize = sampleSize;
                final Bitmap bitmap = BitmapFactory.decodeStream(response
                        .getEntity().getContent(), null, options);

And here is the exception:

0   java.lang.OutOfMemoryError: (Heap Size=11463KB, Allocated=7623KB, Bitmap Size=9382KB)
1       at org.apache.http.util.ByteArrayBuffer.<init>(ByteArrayBuffer.java:53)
2       at org.apache.http.impl.io.AbstractSessionInputBuffer.init(AbstractSessionInputBuffer.java:82)
3       at org.apache.http.impl.io.SocketInputBuffer.<init>(SocketInputBuffer.java:98)
4       at org.apache.http.impl.SocketHttpClientConnection.createSessionInputBuffer(SocketHttpClientConnection.java:83)
5       at org.apache.http.impl.conn.DefaultClientConnection.createSessionInputBuffer(DefaultClientConnection.java:170)
6       at org.apache.http.impl.SocketHttpClientConnection.bind(SocketHttpClientConnection.java:106)
7       at org.apache.http.impl.conn.DefaultClientConnection.openCompleted(DefaultClientConnection.java:129)
8       at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:173)
9       at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
10      at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
11      at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:359)
12      at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
13      at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487)
14      at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465)
15      at de.goddchen.android.easysongfinder.fragments.SongFragment$1.run(SongFragment.java:79)
16      at java.lang.Thread.run(Thread.java:1027)

As you can see, the code doesn't even reach the part where i check the size (Content-Length) of the image and calculate a proper sample size.

I wasn't aware that simply calling DefaultHttpClient.execute(...) will already load the complete content into the memory.

Am i doing something wrong? What is the right way to first retrieve the content length and then start reading the content from an InputStream?

EDIT To avoid common answers that show how to load images from a URL: i already know how to do that, i have also posted the code above, so why do you keep referencing tutorials on that? I explicitly was very clear about the problem: Why is HttpClient.execute(...) already fetching the whole content and storing it in memory instead of providing a proper InputStream to me? Please don't post any beginner tutorials on how to load a Bitmapfrom a URL...

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
Goddchen
  • 4,459
  • 4
  • 33
  • 54
  • Is your HTTP response including the `Content-Length` header? What environment are you testing this on (device vs. emulator, OS version, etc.)? – CommonsWare Nov 02 '12 at 23:11
  • Yes it is providing the proper content length. I am receiving these error from real devices ranging from Gingerbread to Jelly Bean... – Goddchen Nov 02 '12 at 23:14

3 Answers3

1

Just use HttpUrlConnection for that simple request.

Also the way you are determining the sample size is hackish, content size before decoding to Bitmap is not a good way to determine the Bitmap byte allocation, use its dimensions.

Emil Davtyan
  • 13,808
  • 5
  • 44
  • 66
  • Is HttpUrlConnection providing a proper InputStream? And not storing everything in memory immediately? – Goddchen Nov 02 '12 at 23:14
  • Yes. Also take my note about your sample size determination. – Emil Davtyan Nov 02 '12 at 23:15
  • yes i know about the sample size issue, but getting dimensions would require the read the stream twice (once with inJustDecodeBounds and then again with the proper inSampleSize). which is not acceptable. So i am better off with a rough guess on the content length of the image... – Goddchen Nov 02 '12 at 23:21
1

also check the methods setFixedLengthStreamingMode and setChunkedStreamingMode on HttpURLConnection. Considering them you can reduce the memory consumption while downloading the big image.

Just FYI you can take a look libs that might provide the functionally you are trying to implement. Some also support caching which might be very handy.

Here are few I know of

https://github.com/nostra13/Android-Universal-Image-Loader

https://github.com/novoda/ImageLoader

https://github.com/kaeppler/ignition

Vad1mo
  • 5,156
  • 6
  • 36
  • 65
0

I had a similar issue with images loaded from internet and the way I fixed this was to save the image in a temporary location on the phone (you can use internal or external storage, doesn't matter) and load the image from there. Save the image bytes in a file, use a buffer, don't read and write all the bytes on a single operation.

And this way it works fine. I don;t have a sample code now, I don't find the project I used this technique, but you can fins using google ways to save a binary resource from http.

Later edit: Another similar question was asked here: Regarding download image from url in android

Later later edit:

This stream cannot be read multiple times, on first read get the image size and after this go to take another info. This is the short explanation for your error. The compiler optimize your code to be able to re-read from the stream an put it on the memory. When this happens, OOM is thrown.

Community
  • 1
  • 1
Zelter Ady
  • 6,266
  • 12
  • 48
  • 75
  • If the image throws an OOM when loaded from the HTTP response stream it will surely throw one when read as is after being stored on disk. Use BitmapFactory.Options to resize the image when actually reading it into memory like it's said here: http://stackoverflow.com/a/8497703/939023 – Shivan Dragon Nov 02 '12 at 22:49
  • When you read a resource from HTTP it reads all the bytes and after this try to decode the image. Just save the image, using a buffer, let's say 1k buffer. You won;t get any OOM exception. – Zelter Ady Nov 02 '12 at 22:54
  • shivan, i am using the BitmapFactory.Options.inSampleSize as you can see in my code. Although that code is never reached since HttpClient seems to load the complete content into memory upon the call to execute(...). That's the actual problem. – Goddchen Nov 02 '12 at 22:56
  • i don't get it :( Where in the code am reading the stream twice? – Goddchen Nov 02 '12 at 23:22
  • Sorry... Now I see you are reading it only once. I thought you get the image size first from the stream. I didn't pay attention to that part of the code. But... I still think that the stream bytes are stored into local memory and a better way to read that resource is to save it into a local file, free the stream, and use local copy. My opinion. – Zelter Ady Nov 02 '12 at 23:30