Android: BitmapFactory.decodeStream OutOfMemoryException - is SoftReference the solution? - android

I'm getting an OutOfMemoryException:
E/AndroidRuntime( 3013): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
E/AndroidRuntime( 3013): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
E/AndroidRuntime( 3013): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:375)
E/AndroidRuntime( 3013): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:394)
In the following function:
static Bitmap downloadBitmap(String url)
{
final HttpClient client = new DefaultHttpClient();
final HttpGet getRequest = new HttpGet(url);
try
{
HttpResponse response = client.execute(getRequest);
final int statusCode = response.getStatusLine().getStatusCode();
// Check HTTP Status code
if (statusCode != HttpStatus.SC_OK)
{
debugPrint("ImageDownloader StatusCode Error " + statusCode + " while retrieving bitmap from " + url);
return null;
}
else;
final HttpEntity entity = response.getEntity();
if (entity != null)
{
InputStream inputStream = null;
try
{
inputStream = entity.getContent();
if( inputStream == null)
{
debugPrint("ImageDownloader::downloadBitmap() - INPUTSTREAM = NULL!!!!!!");
}
else;
// THIS LINE IS GIVING THE ERROR
final Bitmap bitmap = BitmapFactory.decodeStream( new FlushedInputStream(inputStream));
if( bitmap == null)
{
debugPrint("LocrPhoto::downloadBitmap() - about to return BITMAP =NULL!!!!!!");
}
else;
return bitmap;
}
catch (Exception e)
{
// Could provide a more explicit error message for IOException or IllegalStateException
getRequest.abort();
debugPrint("LocrPhoto::downloadBitmap() Error while decoding bitmap from " + url + "\n"+ e.toString());
}
finally
{
if (inputStream != null)
{
inputStream.close();
}
entity.consumeContent();
}
}
else
{
debugPrint("LocrPhoto::downloadBitmap("+url+") - entity = NULL!!!!!");
}
}
catch (Exception e)
{
// Could provide a more explicit error message for IOException or IllegalStateException
//getRequest.abort();
debugPrint("LocrPhoto::downloadBitmap() Error while retrieving bitmap from " + url + "\n"+ e.toString());
}
finally
{
if (client != null)
{
// CLOSE CONNECTION
}
}
debugPrint("LocrPhoto::downloadBitmap("+url+") - returning NULL at end of function");
return null;
}
FlushedInputStream (though I got the error before adding this code):
// A Class to hopefully avoid the BitmapFactory.decodeStream() returning null bug
// http://code.google.com/p/android/issues/detail?id=6066
static class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {
super(inputStream);
}
#Override
public long skip(long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int byt = read();
if (byt < 0) {
break; // we reached EOF
} else {
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
Basically, I have an activity that downloads images, places them in a framelayout and fades frames in and out to give a slideshow. The framelayout has two imageview children.
I've seen people speak of SoftReference being used to prevent OOMExceptions, but I can't understand how this would be applied to my code to (hopefully) prevent this error.
Could anyone explain how this might be achieved?

I was dealing with the same problem as you and I found that the problem is the memory consumption of the BitmapFactory.decodeStream (as the log says). What you have to do is scale your bitmap just before the call of BitmapFactory.decodeStream, you can find a very nice article that will resolve your problem!
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Hope it helps!

Related

android - GCM Notification Dynamic Large Icon not working

I am trying to load a dynamic large icon for my GCM push notification. But it always returns null. Here is my code. The BitmapFactory.decode() always returns null. I checked this code to fetch some icon for image view and it works fine in that case, but not in my case.
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setLargeIcon(downloadBitmap(url))
.setContentTitle(context.getString(R.string.app_name))
.setContentText(message);
static public Bitmap downloadBitmap(String url) {
final AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
final HttpGet getRequest = new HttpGet("http://files.softicons.com/download/system-icons/crystal-project-icons-by-everaldo-coelho/png/22x22/apps/skype.png");
Bitmap bitmap = null;
try {
HttpResponse response = client.execute(getRequest);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.w("ImageDownloader", "Error " + statusCode + " while retrieving bitmap from " + url);
return null;
}
final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
try {
inputStream = entity.getContent();
bitmap = BitmapFactory.decodeStream(new FlushedInputStream(inputStream));
return bitmap;
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (Exception e) {
// Could provide a more explicit error message for IOException or IllegalStateException
getRequest.abort();
Log.w("ImageDownloader", "Error while retrieving bitmap from " + url + e.toString());
} finally {
if (client != null) {
client.close();
}
}
return bitmap;
}
static class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {
super(inputStream);
}
#Override
public long skip(long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int num_byte = read();
if (num_byte < 0) {
break; // we reached EOF
} else {
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
I got it working... It was an issue with android Emulator. I was using Emulator 2.2. As sson as I switched it to a newer version it started to work

Achieving Concurrency using AsyncTask?

I am developing an Android application to download images from my web server. All the Code is running fine. I am using Asynctask to download the images to my sdcard.
I am on a 4mbps connection but my application is taking about 8 mins to download 3 images (2.5 MB). I have read else where that Asynctask automatically manages Thread creation, so now what I can do to achieve concurrency ?
I am posting my code below. The code Below is for my Asynctask activity that downloads the image from server to sdcard.
public class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
private String url;
Bitmap bitmap1;
String sdCard;
private final WeakReference<ImageView> imageViewReference;
public BitmapDownloaderTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
#Override
// Actual download method, run in the task thread
protected Bitmap doInBackground(String... params) {
// params comes from the execute() call: params[0] is the url.
bitmap1 = downloadBitmap(params[0]);
boolean avail = isMemorySizeAvailableAndroid(bitmap1.getRowBytes(),
Environment.isExternalStorageEmulated());
if (avail) {
try {
sdCard = Environment.getExternalStorageDirectory().toString()
+ "/MyCatalogue";
File f1 = new File(sdCard);
if (!f1.exists()) {
f1.mkdirs();
}
String filename1 = params[0].substring(params[0]
.lastIndexOf("/") + 1);
File file1 = new File(f1.toString(), filename1);
OutputStream stream1 = new FileOutputStream(file1);
bitmap1.compress(CompressFormat.JPEG, 100, stream1);
Log.w("Abhishek", "card is " + sdCard);
} catch (Exception e) {
e.printStackTrace();
}
}
Log.w("ImageDownloader", "Success bitmap is" + bitmap1);
return downloadBitmap(params[0]);
}
protected static boolean isMemorySizeAvailableAndroid(long download_bytes,
boolean isExternalMemory) {
boolean isMemoryAvailable = false;
long freeSpace = 0;
// if isExternalMemory get true to calculate external SD card available
// size
if (isExternalMemory) {
try {
StatFs stat = new StatFs(Environment
.getExternalStorageDirectory().getPath());
freeSpace = (long) stat.getAvailableBlocks()
* (long) stat.getBlockSize();
if (freeSpace > download_bytes) {
isMemoryAvailable = true;
} else {
isMemoryAvailable = false;
}
} catch (Exception e) {
e.printStackTrace();
isMemoryAvailable = false;
}
} else {
// find phone available size
try {
StatFs stat = new StatFs(Environment.getDataDirectory()
.getPath());
freeSpace = (long) stat.getAvailableBlocks()
* (long) stat.getBlockSize();
if (freeSpace > download_bytes) {
isMemoryAvailable = true;
} else {
isMemoryAvailable = false;
}
} catch (Exception e) {
e.printStackTrace();
isMemoryAvailable = false;
}
}
return isMemoryAvailable;
}
#Override
// Once the image is downloaded, associates it to the imageView
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
static Bitmap downloadBitmap(String url) {
final AndroidHttpClient client = AndroidHttpClient
.newInstance("Android");
final HttpGet getRequest = new HttpGet(url);
try {
HttpResponse response = client.execute(getRequest);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.w("ImageDownloader", "Error " + statusCode
+ " while retrieving bitmap from " + url);
return null;
} else {
Log.w("ImageDownloader", "Success " + statusCode
+ " while retrieving bitmap from " + url);
}
final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
try {
inputStream = entity.getContent();
final Bitmap bitmap = BitmapFactory
.decodeStream(inputStream);
return bitmap;
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (Exception e) {
// Could provide a more explicit error message for IOException or
// IllegalStateException
getRequest.abort();
Log.w("ImageDownloader", "Error while retrieving bitmap from "
+ url);
} finally {
if (client != null) {
client.close();
}
}
return null;
}
}
Why you download image twice in doInBackground() at the start and the end? You can return the bitmap just downloaded directly.
if your min sdk level >= 11, you can call executeOnExecutor of AsyncTask with param " THREAD_POOL_EXECUTOR" for concurrency.
if your min sdk level < 11, you can implements AsyncTask new API by reference the source code of AsyncTask.
Use a executeOnExecutor
http://developer.android.com/reference/java/util/concurrent/Executor.html
new BitmapDownloaderTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your urls");
Quoting from docs
http://developer.android.com/reference/android/os/AsyncTask.html
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.
If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.

Getting Bitmap null while loading it from Url in the for loop

I am using following code snippet to load url to bitmap in for loop. It gives null value to bitmap sometimes.So what could be the proper solution to get bitmap value for each loop??
for (int i = 0; i < len; i++) {
String url = "image url"+i;
try {
URL url2 = new URL(url);
InputStream inputStream = url2.openConnection()
.getInputStream();
bitmap = BitmapFactory.decodeStream(inputStream);
Log.i("########## bitmap", "" + bitmap);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
InputStream may have problems when the network connection is not stable,which leads to null return value for BitmapFactory.decodeStream().I had also encountered this problem.
Algo has created a FlushedInputStream class, which solves my problem, it may help you
static class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {
super(inputStream);
}
#Override
public long skip(long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int b = read();
if (b < 0) {
break; // we reached EOF
} else {
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
original post : Android decoder->decode returned false for Bitmap download
Friends...I have got the solution for this...I have just added few lines as following...it works nice....
for (int i = 0; i < len; i++) {
String url = "image url";
try {
URL url2 = new URL(url);
InputStream inputStream = url2.openConnection()
.getInputStream();
bitmap = BitmapFactory.decodeStream(inputStream);
Log.i("########## bitmap", "" + bitmap);
if(bitmap== null) {
i--;
continue;
}
bitmap_array.add(bitmap);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
EDIT:
1 more thing you need to add is to check Net Connection in loop.
and Also check for inputStream is null or not, there you can break the loop.

Android: Image Reading from URL

After seeing all the examples from net, i have written the follwoing code to read an image from the URL, show it to the Image view and save it to the path specified.
public class Downloader {
public void startDownload(ImageView i, String path,String url){
BitmapDownloaderTask task = new BitmapDownloaderTask(i,path);
task.execute(url,null,null);
}
static class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {
super(inputStream);
}
#Override
public long skip(long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int b = read();
if (b < 0) {
break; // we reached EOF
} else {
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
static Bitmap downloadBitmap(String url) {
final AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
final HttpGet getRequest = new HttpGet(url);
try {
HttpResponse response = client.execute(getRequest);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.w("ImageDownloader", "Error " + statusCode + " while retrieving bitmap from " + url);
return null;
}
final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
try {
inputStream = entity.getContent();
final Bitmap bitmap = BitmapFactory.decodeStream(new FlushedInputStream(inputStream));
return bitmap;
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (Exception e) {
// Could provide a more explicit error message for IOException or IllegalStateException
getRequest.abort();
System.err.println("Error while retrieving bitmap from " + url +":"+ e.toString());
} finally {
if (client != null) {
client.close();
}
}
return null;
}
private class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
private String url;
private final WeakReference<ImageView> imageViewReference;
private String path;
public BitmapDownloaderTask(ImageView imageView, String FilePath) {
imageViewReference = new WeakReference<ImageView>(imageView);
path = FilePath;
}
#Override
// Actual download method, run in the task thread
protected Bitmap doInBackground(String... params) {
// params comes from the execute() call: params[0] is the url.
return downloadBitmap(params[0]);
}
#Override
// Once the image is downloaded, associates it to the imageView
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
OutputStream outStream = null;
File file = new File(path);
try {
outStream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
outStream.flush();
outStream.close();
}
catch(Exception e)
{}
}
}
Now the problem is this code works sometimes and fails sometimes... the error is
06-30 12:34:23.363: WARN/System.err(16360): Error while retrieving bitmap from https://URL IS HERE---REMOVE FOR PRIVACY:java.net.SocketTimeoutException: Read timed out
and some time a get
SkImageDecoder::Factory returned null.
Help me with the possible reason
consider this example its show how to create image from url
URL url = new URL(Your Image URL In String);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
Bitmap myBitmap = BitmapFactory.decodeStream(input);
yourImageView.setImageBitmap(myBitmap);
you should implement a retry when the connection was failed.

How do I transfer an image from its URL to the SD card?

How can I save an images to the SD card that I retrieve from the image's URL?
First you must make sure your application has permission to write to the sdcard. To do this you need to add the uses permission write external storage in your applications manifest file. See Setting Android Permissions
Then you can you can download the URL to a file on the sdcard. A simple way is:
URL url = new URL ("file://some/path/anImage.png");
InputStream input = url.openStream();
try {
//The sdcard directory e.g. '/sdcard' can be used directly, or
//more safely abstracted with getExternalStorageDirectory()
File storagePath = Environment.getExternalStorageDirectory();
OutputStream output = new FileOutputStream (new File(storagePath,"myImage.png"));
try {
byte[] buffer = new byte[aReasonableSize];
int bytesRead = 0;
while ((bytesRead = input.read(buffer, 0, buffer.length)) >= 0) {
output.write(buffer, 0, bytesRead);
}
} finally {
output.close();
}
} finally {
input.close();
}
EDIT :
Put permission in manifest
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
An excellent example can be found in the latest post on Android developer's blog:
static Bitmap downloadBitmap(String url) {
final AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
final HttpGet getRequest = new HttpGet(url);
try {
HttpResponse response = client.execute(getRequest);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
Log.w("ImageDownloader", "Error " + statusCode +
" while retrieving bitmap from " + url);
return null;
}
final HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream inputStream = null;
try {
inputStream = entity.getContent();
final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
return bitmap;
} finally {
if (inputStream != null) {
inputStream.close();
}
entity.consumeContent();
}
}
} catch (Exception e) {
// Could provide a more explicit error message for IOException or
// IllegalStateException
getRequest.abort();
Log.w("ImageDownloader", "Error while retrieving bitmap from " + url,
e.toString());
} finally {
if (client != null) {
client.close();
}
}
return null;
}

Categories

Resources