-2

I have an activity where I load an image from the given URI. The android training article suggested that it should be done in background so that it does not block the UI. I followed the same Article.

Here is the snippet where I call the AsyncTask

    Uri uri = ....
    ImageLoaderTask task = new ImageLoaderTask(imageView);
    task.execute(uri);

My ImageLoaderTask is given below.

public class ImageLoaderTask extends AsyncTask<Uri, Void, Bitmap> {

private WeakReference<ImageView> imageViewReference;

public ImageLoaderTask(ImageView imageView) {
    // Use a WeakReference to ensure the ImageView can be garbage collected
    imageViewReference = new WeakReference<ImageView>(imageView);
}

@Override
protected Bitmap doInBackground(Uri... params) {
    Bitmap bitmap = ImageUtils
                .decodeSampledBitmapFromFile(params[0], 200,
                        200);
    return bitmap;
}

@Override
protected void onPostExecute(Bitmap bitmap) {
    if (imageViewReference != null && bitmap != null) {
            final ImageView imageView = imageViewReference.get();
            if (imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
}

}

This is almost as described in the article specified above.

The code in ImageUtils class is given below

public static Bitmap decodeSampledBitmapFromFile(Uri uri,
                                                 int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        File imageFile = getImageFile(uri);
        BitmapFactory.decodeFile(imageFile.getPath(), options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(imageFile.getPath(), options);

}

private static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

When I call the task.execute(uri), the app crashes. I tried it in my phone and not on the emulator. I could not get my emulator running on my machine since it takes too much time.

What could be the possible cause of this?

user1223879
  • 123
  • 3
  • 13
  • `ImageLoaderTask task = new ImageLoaderTask(imageView);` what is imageview here??? – Pankaj Jun 06 '15 at 11:33
  • check that you are running the async task from your UI thread, thats where I would start looking without a stack trace, are you able to provide one? – Burrito Jun 06 '15 at 11:35
  • @Clairvoyant : imageView is the ImageView widget object – user1223879 Jun 06 '15 at 11:37
  • @Burrito: I tried getting the stack trace but the app crashes before that. No idea how to get it. By the way how to know if I am calling it from the UI thread or not and how to call it outside the UI thread (Sorry if it is a silly question) – user1223879 Jun 06 '15 at 11:39
  • You are getting crash coz your sending params as imageview object and fetching it in doInBackground as uri – Pankaj Jun 06 '15 at 11:40
  • @user1223879 have a look in logcat mate, thats where the trace will be. You can also log the thread name to logcat, Log.d("sometag", Thread.currentThread().getName()); If it says "main" its running on the UI thread, try calling that before you execute. Also check that you have internet permissions in your manifest :) – Burrito Jun 06 '15 at 11:41
  • @Clairvoyant: I am passing the imageView to the Task's constructor. The doInbackground is called from the execute method where uri is passed. – user1223879 Jun 06 '15 at 11:42
  • First, there are many [image loading libraries](http://android-arsenal.com/tag/46) -- please use one rather than writing your own. Second, please use LogCat to get at the stack trace: https://stackoverflow.com/questions/23353173/unfortunately-myapp-has-stopped-how-can-i-solve-this Third, [a `Uri` is not a file](https://commonsware.com/blog/2014/07/04/uri-not-necessarily-file.html). Your `getImageFile()` method will not work on hundreds of millions of Android devices. Use a `ContentResolver` to get an `InputStream` on the `Uri`, then use `decodeStream()` on `BitmapFactory`. – CommonsWare Jun 06 '15 at 11:46

2 Answers2

1

Instead of passing ImaageView reference to Async class standard practice try to define custom interface for load bitmap

public interface ImageBitampLoadListener {
    public void onBitampLoad(Bitmap bitmap);
}

Pass custom interface reference Async :

private ImageBitmapLoadListener listener;

public ImageLoaderTask(ImageBitmapLoadListener listener) {
    this.listener = listener;
}

Set call back from Async :

@Override
protected void onPostExecute(Bitmap bitmap) {
   super.onPostExecute(bitmap);
   if(listener!=null){
      listener.onBitampLoad(bitmap);
   }
}

Set custom listener to Async and get Bitamp :

ImageLoaderTask task = new ImageLoaderTask(new ImageBitmapLoadListener() {
    @Override
    public void onBitampLoad(Bitmap bitmap) {
         imageView.setImageBitmap(bitmap);
    }
});
task.execute(uri);
Haresh Chhelana
  • 24,720
  • 5
  • 57
  • 67
0

I solved the issue by overriding onProgressUpdate() method of the AsyncTask. Now my ImageLoader code looks like

@Override
protected void onProgressUpdate(Void... values) {
    try {
        if (imageViewReference != null && bitmap != null) {
            final ImageView imageView = imageViewReference.get();
            if (imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    } catch (Throwable t) {
        Utils.showErrorDialog(ChatWindow.getInstance(), t);
    }
}

@Override
protected void onPostExecute(Bitmap bitmap) {
    this.bitmap = bitmap;
    onProgressUpdate();
}
user1223879
  • 123
  • 3
  • 13