I have ViewPager which I use to show zoomable images (using ImageViewTouch). I need to load large bitmaps from Internet (http). By large I mean 2000x1000. The images need to be so large, because they are zoomable and need to show details. Images on server are .jpg format, but it's not problem - I can change it.
How can I manage to load so large images to ImageViewTouch (ImageView) without getting probles with memory?
By now I'm using simply this (AsyncTask):
ImageView currentView; //ImageView where to place image loaded from Internet
String ImageUrl; //URL of image to load
protected Bitmap doInBackground(ArrayList... params) {
Bitmap bitmap;
InputStream in = null;
imageUrl = (String)params[0].get(1);
try{
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response = httpclient.execute(new HttpGet(imageUrl));
in = response.getEntity().getContent();
} catch(Exception e){
e.printStackTrace();
}
try {
bitmapa = BitmapFactory.decodeStream(in);
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
currentView.setImageBitmap(bitmap);
}
And it cause many problems with memory:
E/dalvikvm-heap(369): 69560740-byte external allocation too large for this process.
E/GraphicsJNI(369): VM won't let us allocate 69560740 bytes
or
E/AndroidRuntime(369): java.lang.RuntimeException: An error occured while executing doInBackground()
E/AndroidRuntime(369): at android.os.AsyncTask$3.done(AsyncTask.java:200)
E/AndroidRuntime(369): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274)
E/AndroidRuntime(369): at java.util.concurrent.FutureTask.setException(FutureTask.java:125)
E/AndroidRuntime(369): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308)
E/AndroidRuntime(369): at java.util.concurrent.FutureTask.run(FutureTask.java:138)
E/AndroidRuntime(369): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
E/AndroidRuntime(369): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
E/AndroidRuntime(369): at java.lang.Thread.run(Thread.java:1019)
E/AndroidRuntime(369): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
E/AndroidRuntime(369): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
E/AndroidRuntime(369): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:470)
E/AndroidRuntime(369): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:525)
E/AndroidRuntime(369): at com.package.app.ImageDownloader.doInBackground(ImageDownloader.java:78)
E/AndroidRuntime(369): at com.package.app.ImageDownloader.doInBackground(ImageDownloader.java:1)
E/AndroidRuntime(369): at android.os.AsyncTask$2.call(AsyncTask.java:185)
E/AndroidRuntime(369): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
E/AndroidRuntime(369): ... 4 more
What version of the OS do you test this on? On earlier versions the heap space available is much lower than on later versions.
I would seriously consider downsizing your bitmaps to avoid this. If you are downloading a 2000x1000 Bitmap
for an mdpi phone then this is probably a bad idea.
I would also recommend reading this article for more information on how to load the appropriate image exactly for a specific ImageView
, based on its dimensions.
Finally, you should always close an InputStream
in a finally
block:
try {
bitmap = BitmapFactory.decodeStream(in);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) { in.close(); }
}
There is also android:largeHeap="true"
which you can consider, specifically made for apps that deal with large bitmaps, such as photo editing apps.
A 2000x1000 pixel image is large, but not THAT large.
I see, though that the process is trying to allocate 69560740 bytes before it dies, that is about 66MByte!
Your 2000x1000, in worst case, is about 2000x1000x4 = 8MBytes, way less than 66MBytes. Something else is going on.
Also, I found an issue with using Bitmaps as a return/result value in AsyncTasks. AsyncTasks that have finished still hold on to their result until they are garbage collected. Since you don't control the garbarge collection of AsyncTask instances, don't use Bitmap as a return/result value. Instead put the Bitmap in a field and assign it when the doInBackground is about to end, and use this field to get the Bitmap in onPostExecute and(!) set this field to null:
Bitmap bitmap;
protected Void doInBackground(ArrayList... params) {
InputStream in = null;
imageUrl = (String)params[0].get(1);
try{
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response = httpclient.execute(new HttpGet(imageUrl));
in = response.getEntity().getContent();
} catch(Exception e){
e.printStackTrace();
}
try {
bitmap = BitmapFactory.decodeStream(in);
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Void unused) {
if (bitmap != null) {
currentView.setImageBitmap(bitmap);
}
// And don't forget to null the bitmap field!
bitmap = null;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With