Get Bitmap using a Target in Picasso

Tags: , , ,



I’m working on movies Android app which get list of movies from an API which provides a poster path for all movies.

I want to get the image as Bitmap from the image’s URL to save it as a Bitmap variable in the model class. I want to save the image as blob in the DB to retrieve it directly without redownloading it each time the user opens the app. Is that possible?

I want to do something like this, but it always returns null.

 private Bitmap posterBitmap;

 public void setPosterBitmap () {
    Picasso.get().load(POSTERS_URL).into(new Target() {
        @Override
        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
            posterBitmap = bitmap; // I don't want to set it to an Image view here
        }

        @Override
        public void onBitmapFailed(Exception e, Drawable errorDrawable) {}

        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {}
    });   
}

Thanks in advance.

Answer

This code is working for me:

...
private static final String TAG = MainActivity.class.getName();
private Target mTarget;
...
        
mTarget = new Target() {
    @Override
    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
        if (bitmap == null) {
            Log.w(TAG, "Null");
        } else {
            Log.i(TAG, "Worked");
        }
    }
        
    @Override
    public void onBitmapFailed(Exception e, Drawable errorDrawable) {
        Log.w(TAG, "failed");
    }
        
    @Override
    public void onPrepareLoad(Drawable placeHolderDrawable) {
        Log.i(TAG, "Prepare");
    }
};
        
// Small image loads without resize
// Picasso.get().load("http://www.theretirementmanifesto.com/wp-content/uploads/2016/08/Yoda-free-clip-art-680x410.jpg").into(mTarget);
// Mega high res out of memory image 
Picasso.get().load("https://upload.wikimedia.org/wikipedia/commons" + 
    "/5/5e/M104_ngc4594_sombrero_galaxy_hi-res.jpg"). 
        resize(100, 100).into(mTarget);

Also I’m assuming that the following line is in the manifest:

<uses-permission android:name="android.permission.INTERNET" />

Using this version of Picasso:

implementation 'com.squareup.picasso:picasso:2.71828'

Also it may be worth declaring the Target as a member variable due to issues Picaso has arising from ‘weak references’. You will have to research this, but I believe it may be unsafe to declare the Target as an anonymous inner class.

Also it may be necessary to call resize(x, y) to prevent an out of memory situation depending on the image sizes and whether you control their source.

UPDATE:

The project won’t work as written because it is using an synchronous solution, but you’re making an asynchronous call:

holder.moviePosterIV.setImageBitmap(movie.getPosterBitmap());

The code:

public Bitmap getPosterBitmap() {
    target = new Target() {
        @Override
        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
           posterBitmap = bitmap;
        }

        @Override
        public void onBitmapFailed(Exception e, Drawable errorDrawable) {}

        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {}
    };
    return posterBitmap;
}

It cannot be done like this. In a nut shell, the Target is going to be called in the future when the image has been downloaded. But the code is being written as if the image is ready immediately.

It is a synchronous solution to an asynchronous problem. In synchronous programming we write 1 line after the other then return the result when the data is ready.

If we wrote it synchronously:

  f() {
    image = getImage(url) // the program could be blocked here for minutes
    return image
  }

So instead we do it asynchronously:

  f() {
    getImageInTheFuture(url, imageReadyFunc); // call imageReadyFunc when it is finally downloaded
  }

  imageReadyFunc(image) {
    setTheImage();
  }

The asynchronous solution prevents the app from blocking, but it is also a real pain because we can no longer use a ‘return’ statement. Instead we have to break the code up into 2 sections. Code that we can run before the image is available. Code that we can run after the image is available.

But under the hood Picasso is doing all of this work for you. I’d really advise against trying to manage the bitmaps directly. In the bad old days of Android before Picasso, Glide, etc. apps used to routinely crash and run out of memory trying to manage their own bitmaps. It is technically difficult to do without causing memory leaks and running out of memory.

Hope that makes sense…



Source: stackoverflow