Jacob Tabak
Jacob Tabak

Reputation: 8094

Synchronous image loading on a background thread with Picasso - without .get()

I have a custom viewgroup (that contains Picasso-loaded images) that can be reused in two places:

  1. Displayed to the user in the application (on the UI thread)
  2. Drawn to a canvas and saved as a .jpeg (on the background thread)

My code for drawing to the canvas looks like this:

int measureSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
view.measure(measureSpec, measureSpec);
Bitmap bitmap =
      Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
view.draw(canvas);

The problem is that there is no time for the image to load before I draw the view to the canvas. I'm trying to avoid coupling as much as possible here so I'd prefer not to add a Picasso callback because the class that's doing the drawing doesn't know anything about the view that it's drawing.

I'm currently working around the issue by changing the image loading code to .get() rather than .load() and then using imageView.setImageBitmap(). Unfortunately, this adds a ton of complexity to the view and I really don't like it.

What I'd like to do is pass an option to Picasso's RequestCreator that the request should be executed synchronously on the current thread (and throw an exception if it's the main thread). I wonder if this is too much of an edge case for support to be built directly into Picasso? Or is it already in the API and I'm oblivious to it?

Upvotes: 2

Views: 9569

Answers (2)

Nikola Despotoski
Nikola Despotoski

Reputation: 50538

Perfect answer by Jacob Tabak

Here is small addition that handles the case if you load image into Target. I haven't found a way to get the origin of image to pass appropriate LoadedFrom argument.

 public static void into(RequestCreator requestCreator, Drawable placeHolder, Drawable errorDrawable, Target target) {
        boolean mainThread = Looper.myLooper() == Looper.getMainLooper();
        if (mainThread) {
            requestCreator.into(target);
        } else {
            try {
                target.onBitmapFailed(placeHolder);
                Bitmap bitmap = requestCreator.get();
                target.onBitmapLoaded(bitmap, Picasso.LoadedFrom.MEMORY);
            } catch (IOException e) {
                target.onBitmapFailed(errorDrawable);
            }
        }
    }

Upvotes: 2

Jacob Tabak
Jacob Tabak

Reputation: 8094

Here is my solution:

/**
 * Loads the request into an imageview.
 * If called from a background thread, the request will be performed synchronously.
 * @param requestCreator A request creator
 * @param imageView The target imageview
 * @param callback a Picasso callback
 */
public static void into(RequestCreator requestCreator, ImageView imageView, Callback callback) {
  boolean mainThread = Looper.myLooper() == Looper.getMainLooper();
  if (mainThread) {
    requestCreator.into(imageView, callback);
  } else {
    try {
      Bitmap bitmap = requestCreator.get();
      imageView.setImageBitmap(bitmap);
      if (callback != null) {
        callback.onSuccess();
      }
    } catch (IOException e) {
      if (callback != null) {
        callback.onError();
      }
    }
  }
}

Upvotes: 12

Related Questions