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.
Related
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
Below is the code for imagedownloader which I am using. When a particular image is downloaded first time imagedownloader will download it and store it. For future references again I will pass the url. If image exists then it will be retrieved locally and if it does not exist it will be downloaded. This code functions properly in android 2.2 and 2.3.3. However when i ran it in emulator: android 4.0 and my note2 the image is always downloaded from the internet. It is not retrieved from local storage.
ImageDownloader.java
public class ImageDownloader {
Map<String,Bitmap> imageCache;
public ImageDownloader(){
imageCache = new HashMap<String, Bitmap>();
}
//download function
public void download(String url, ImageView imageView) {
if (cancelPotentialDownload(url, imageView)) {
//Caching code right here
String filename = String.valueOf(url.hashCode());
File f = new File(getCacheDirectory(imageView.getContext()), filename);
// Is the bitmap in our memory cache?
Bitmap bitmap = null;
bitmap = (Bitmap)imageCache.get(f.getPath());
if(bitmap == null){
bitmap = BitmapFactory.decodeFile(f.getPath());
if(bitmap != null){
imageCache.put(f.getPath(), bitmap);
}
}
//No? download it
if(bitmap == null){
BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
imageView.setImageDrawable(downloadedDrawable);
task.execute(url);
}else{
//Yes? set the image
imageView.setImageBitmap(bitmap);
}
}
}
//cancel a download (internal only)
private static boolean cancelPotentialDownload(String url, ImageView imageView) {
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
if (bitmapDownloaderTask != null) {
String bitmapUrl = bitmapDownloaderTask.url;
if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
bitmapDownloaderTask.cancel(true);
} else {
// The same URL is already being downloaded.
return false;
}
}
return true;
}
//gets an existing download if one exists for the imageview
private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
if (imageView != null) {
Drawable drawable = imageView.getDrawable();
if (drawable instanceof DownloadedDrawable) {
DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;
return downloadedDrawable.getBitmapDownloaderTask();
}
}
return null;
}
//our caching functions
// Find the dir to save cached images
private static File getCacheDirectory(Context context){
String sdState = android.os.Environment.getExternalStorageState();
File cacheDir;
if (sdState.equals(android.os.Environment.MEDIA_MOUNTED)) {
File sdDir = android.os.Environment.getExternalStorageDirectory();
//TODO : Change your diretcory here
cacheDir = new File(sdDir,"data/tac/images");
Log.w("ImageDownloader", "Error while retrieving bitmap from ");
}
else
cacheDir = context.getCacheDir();
if(!cacheDir.exists())
cacheDir.mkdirs();
return cacheDir;
}
private void writeFile(Bitmap bmp, File f) {
FileOutputStream out = null;
try {
out = new FileOutputStream(f);
bmp.compress(Bitmap.CompressFormat.PNG, 80, out);
} catch (Exception e) {
e.printStackTrace();
}
finally {
try { if (out != null ) out.close(); }
catch(Exception ex) {}
}
}
///////////////////////
//download asynctask
public class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
private String url;
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.
url = (String)params[0];
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();
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
// Change bitmap only if this process is still associated with it
if (this == bitmapDownloaderTask) {
imageView.setImageBitmap(bitmap);
//cache the image
String filename = String.valueOf(url.hashCode());
File f = new File(getCacheDirectory(imageView.getContext()), filename);
imageCache.put(f.getPath(), bitmap);
writeFile(bitmap, f);
}
}
}
}
static class DownloadedDrawable extends ColorDrawable {
private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;
public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
super(Color.BLACK);
bitmapDownloaderTaskReference =
new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
}
public BitmapDownloaderTask getBitmapDownloaderTask() {
return bitmapDownloaderTaskReference.get();
}
}
//the actual download code
static Bitmap downloadBitmap(String url) {
HttpParams params = new BasicHttpParams();
params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
HttpClient client = new DefaultHttpClient(params);
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;
}
}
Use ShutterBug Library to download pic from HTTP.
https://github.com/applidium/Shutterbug
It just a one line code.
Thanks
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.
hi to all
how can i make this code to wait until it finishes downloading the image
with what can i replace doInBackground(URL... paths) to make it wait to download then continue with the rest of the code
private class DownloadImageTask extends AsyncTask<URL, Integer, Bitmap> {
// This class definition states that DownloadImageTask will take String
// parameters, publish Integer progress updates, and return a Bitmap
protected Bitmap doInBackground(URL... paths) {
URL url;
try {
url = paths[0];
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
int length = connection.getContentLength();
InputStream is = (InputStream) url.getContent();
byte[] imageData = new byte[length];
int buffersize = (int) Math.ceil(length / (double) 100);
int downloaded = 0;
int read;
while (downloaded < length) {
if (length < buffersize) {
read = is.read(imageData, downloaded, length);
} else if ((length - downloaded) <= buffersize) {
read = is.read(imageData, downloaded, length
- downloaded);
} else {
read = is.read(imageData, downloaded, buffersize);
}
downloaded += read;
publishProgress((downloaded * 100) / length);
}
Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0,
length);
if (bitmap != null) {
Log.i(TAG, "Bitmap created");
} else {
Log.i(TAG, "Bitmap not created");
}
is.close();
return bitmap;
} catch (MalformedURLException e) {
Log.e(TAG, "Malformed exception: " + e.toString());
} catch (IOException e) {
Log.e(TAG, "IOException: " + e.toString());
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.toString());
}
return null;
}
protected void onPostExecute(Bitmap result) {
String name = ImageLink.substring(ImageLink
.lastIndexOf("/") + 1);
if (result != null) {
hasExternalStoragePublicPicture(name);
saveToSDCard(result, name);
isImage = true;
} else {
isImage = false;
}
}
}
doInBackground()) executes in the background. The part that waits for the download and continues with the rest of the code is onPostExecute(). This is the function you're probably asking for.
The point about AsyncTask is the main code in your Activity (which creates the AsyncTask) doesn't wait. Async is short for asynchronous - that means something which happens without a pre-determined time-frame.
If you want to have a download or multiple downloads complete before other code can execute then you either need to perform things in a synchronous manner (not good in Android Activities) or you need to write your code to wait on a callback.
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!