Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Target.onBitmapLoaded() method not called the first time after completed (network) request. #352

Closed
lukasz1 opened this issue Jan 6, 2014 · 19 comments

Comments

@lukasz1
Copy link

lukasz1 commented Jan 6, 2014

onBitmapLoaded() is not called immediately after fetching has been completed. But when I load the image second time (which I suppose is already cached) the method is indeed called.

The code I use:

Picasso.with(this)
        .load(pictureUrl)
        .into(new Target() {
            @Override
            public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                // not being called the first time
            }

            @Override
            public void onBitmapFailed(Drawable errorDrawable) {

            }

            @Override
            public void onPrepareLoad(Drawable placeHolderDrawable) {

            }
        });

Picasso version: 2.1.1

@JakeWharton
Copy link
Member

Your Target is being garbage collected. Implement it on an object or store it in a field. If all you want is a callback, there's a two argument version for loading into an ImageView.

@Gryzor
Copy link

Gryzor commented Jan 6, 2014

Given the number of people (myself included the first time I used a Target) who make this mistake, wouldn't it be wiser to add some sort of "bigger" warning?

@rocboronat
Copy link

Same here.

@dnkoutso
Copy link
Collaborator

dnkoutso commented Nov 6, 2014

Same here what? Are you using an anonymous class for target?

@alexxsanchezm
Copy link

I have the same problem, i my case Picasso is fired from an IntentService, now what i did was create a class which extends from Target and add some useful parameters to process the loaded bitmap, but onBitmapLoaded() is not called. hope someone can help. thanks.

`final class ImgTarget implements Target {

    private Uri uri;
    public ImgTarget(Uri uri) {
        this.uri = uri;
    }

    @Override
    public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {
        Log.v("IMG Downloader", "onBitmapLoaded ...");
        try {
            Log.v("IMG Downloader", "Writing image data...");
            OutputStream os = getContentResolver().openOutputStream(uri, "w");
            boolean compressed = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
            Log.v("IMG Downloader", "Compressed? : " + compressed);
            os.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onBitmapFailed(Drawable drawable) {
        Log.v("IMG Downloader", "Bitmap Failed...");
    }

    @Override
    public void onPrepareLoad(Drawable drawable) {
        Log.v("IMG Downloader", "Bitmap Preparing Load...");
    }

}`

@JakeWharton
Copy link
Member

You are not keeping a reference to the created ImgTarget and it is being
garbage collected.
On Jan 31, 2015 8:34 AM, "Alex" notifications@github.com wrote:

I have the same problem, i my case Picasso is fired from an IntentService,
now what i did was create a class which extends from Target and add some
useful parameters to process the loaded bitmap, but onBitmapLoaded() is not
called. hope someone can help. thanks.

`final class ImgTarget implements Target {

private Uri uri;
public ImgTarget(Uri uri) {
    this.uri = uri;
}

@Override
public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {
    Log.v("IMG Downloader", "onBitmapLoaded ...");
    try {
        Log.v("IMG Downloader", "Writing image data...");
        OutputStream os = getContentResolver().openOutputStream(uri, "w");
        boolean compressed = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
        Log.v("IMG Downloader", "Compressed? : " + compressed);
        os.close();

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

@Override
public void onBitmapFailed(Drawable drawable) {
    Log.v("IMG Downloader", "Bitmap Failed...");
}

@Override
public void onPrepareLoad(Drawable drawable) {
    Log.v("IMG Downloader", "Bitmap Preparing Load...");
}

}`


Reply to this email directly or view it on GitHub
#352 (comment).

@alexxsanchezm
Copy link

@JakeWharton Well, probably my approach is wrong but due i need to download an image list, this is how i get images. but target objects are still GC'd. Thanks.

final List<ImgTarget> targets = new ArrayList<ImgTarget>(); 
private void downloadImages(List<CustomUri> uris){
    CustomUri uri;
    String url;
    ImgTarget target;
    for(CustomUri uri : uris){

        url = Constants.BASE_URL + Constants.API_VER + uri.kind + "/" + uri.key + "/img/" + uri.imageKey;
        target = new ImgTarget(uri.uri);
        picasso.load(url).into(target);
        targets.add(target);
    }
}

@AdarshYadav
Copy link

@lukasz1 : Finally found solution here:- http://www.opensourcealternative.org/tutorials/android-tutorials/android-picasso-save-image-tutorial/

@MrThiago
Copy link

I am having the same problem.

   Picasso.with(MapActivity.this)
                        .load(avatar)
                        .into(new Target() {
                            @Override
                            public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                                Log.e(TAG, "Picasso > onBitmapLoaded");


                                mMap.addMarker(new MarkerOptions()
                                        .position(new LatLng(doubleLat, doubleLon))
                                        .title(name)
                                        .snippet(email)
                                        .icon(BitmapDescriptorFactory.fromBitmap(bitmap)));
                            }
                            @Override
                            public void onBitmapFailed(Drawable errorDrawable) {
                                Log.e(TAG, "Picasso > onBitmapFailed");
                                loadDefaultMarker(map, doubleLat, doubleLon, name, email);
                            }
                            @Override
                            public void onPrepareLoad(Drawable placeHolderDrawable) {
                                Log.e(TAG, "Picasso > onPrepareLoad");
                            }
                        });

onBitmapLoaded is only called once in the app life of install of the device. after that, this method is not longer calling.

Can you please advice. thank you

@ghost
Copy link

ghost commented Mar 30, 2016

@maxizrin
Copy link

I'm having the same problem, only I use Picasso from inside a class that extends ImageView.
I'm getting this issue when trying to refresh the InfoWindow for map markers after their image has loaded.
I pass this, from within the class extending ImageView, as the target, I'm certain it is not being GC'd because it's a single layout that gets reused for every marker, and it does work... from the second attempt.

I even save a reference to the callback I'm given, just in case, to no avail.
I don't understand what I'm doing wrong.

public void loadFromUrl(String url, Callback onLoad) {

        if (isLoading)//cancel previous request
            Picasso.with(this.getContext()).cancelRequest(this);
        isLoading = true;
        setScaleType(ScaleType.CENTER_INSIDE);
        url = url.replace(" ", "%20");
        Log.d("WebImage URL", url);
        RequestCreator request = Picasso.with(this.getContext())
                .load(url)
                .placeholder(R.drawable.load_rotate)
                .error(R.mipmap.missing)
                .fit()
                .centerCrop();
        if (!animationEnabled)
            request.noFade();

        this.callback = onLoad;
        this.onLoad = new com.squareup.picasso.Callback() {
            @Override
            public void onSuccess() {
                isLoading = false;
                setScaleType(ScaleType.CENTER_CROP);
                if (callback != null)
                    callback.call(true);
            }

            @Override
            public void onError() {
                isLoading = false;
                if (callback != null)
                    callback.call(false);
            }
        };

        request.into(this, this.onLoad);
    }

@Robbypatel1357
Copy link

@phillwiggins
Copy link

Adding a tag seems to fix this problem, here's my code, the setTag corrected that issues that you are having above:-

Target target = new Target() {
        @Override
        public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {

            if (bitmap.getHeight() > bitmap.getWidth()) {
                imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
                imageView.setImageBitmap(bitmap);
            } else {
                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                imageView.setImageBitmap(bitmap);
            }
        }

        @Override
        public void onBitmapFailed(Drawable errorDrawable) {
            imageView.setImageDrawable(context.getResources().getDrawable(R.drawable.mend_logo));
        }

        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {
            imageView.setImageDrawable(context.getResources().getDrawable(R.drawable.mend_logo));
        }
    };

    Picasso.with(context)
            .load(url)
            .into(target);

    imageView.setTag(target);

@Flywhiter
Copy link

Adding a tag work for this problem

@lwlittayat
Copy link

I have string array = {url1,url2,url3};
and i use For looping Set url to Picasso
for(i){
Picasso.with(this).load(url(i)).into(ImageView,new Callback(){
onSuccess(){
Funtion(ImageViewToByte) // get byte array save data to sqlite
and Funtion(ImageViewToBase64) // get byte array save data to Google script
}
}
}
it's wrong show Byte and Base64 in url1,url2
but work only url3
please help. sorry for my english language

@SayfullahRanak
Copy link

SayfullahRanak commented Nov 7, 2018

Hi

In my case, I am using databinding library and calling the picasso loader from custom bindingAdapter, where i am receiving the layout and image location(internal storage location) in the adapter. I have used "target" for loading the image into the background of the layout.

As picasso keeps a weak reference to the target, i have created a custom layout of that layout and extended the "target" there, implemented reference methods of target ->
"onPrepareLoad", "onBitmapFailed", "onBitmapLoaded". Where I am setting the background image in the "onBitmapLoaded" method.

The problem is : "onPrepareLoad" and "onBitmapFailed" methods are called but the "onBitmapLoaded" method is not getting called.

for loading background image with picasso, i am following this link :
https://stackoverflow.com/questions/31464867/how-to-load-layout-background-using-picasso?answertab=active#tab-top

|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
RELATED CODE
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||


xml code :


<com.myproject.testapp.test.createprofile.view.CustomLayoutUploadImage
android:id="@+id/CreatCampaignConstLyout1"
android:layout_width="match_parent"
android:layout_height="250dp"
app:setBackGroundImage="@{CreateProfileViewModel.UploadPhoto}"
app:setBackGroundColor="@{@color/CreateProfileUploadPicture}"
app:setCurrentState="@{CreateProfileViewModel.EditImageVisibility}"
android:orientation="vertical">
.................
.................
</com.myproject.testapp.test.createprofile.view.CustomLayoutUploadImage>


Binding Adapter Class:


public class BindingAdapterClass {

        .........
        ..........

@BindingAdapter({"bind:setBackGroundImage","bind:setBackGroundColor", "bind:setCurrentState"})
public static void setLayoutBackground(CustomLayoutUploadImage customLayout, String imageUrl, int Color, boolean state) {

     target = (CustomLayoutUploadImage)customLayout;
    if(!state){
        customLayout.setBackgroundColor(Color);
    }
    else {
        if(imageUrl!=null){

            Log.d("NOTSTATE ADAPTER",imageUrl+"");
            Picasso.get()
                    .load(imageUrl)
                    .placeholder(R.drawable.ic_upload_image_black_24dp)
                    .into(target);

            customLayout.setTag(target);
        }
    }
}

Custom Layout Class:


public class CustomLayoutUploadImage extends android.support.constraint.ConstraintLayout implements Target {

public CustomLayoutUploadImage(Context context) { super(context); }

public CustomLayoutUploadImage(Context context, AttributeSet attrs) { super(context, attrs); }

public CustomLayoutUploadImage(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }


@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
    setBackground(new BitmapDrawable(getResources(), bitmap));
    Log.d("NOTSTATE","BitmapLoaded");
}

@Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {
    Log.d("NOTSTATE","BitmapFailed");
}


@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
    //Set your placeholder
    Log.d("NOTSTATE","preloaded : "+placeHolderDrawable.toString());
}

}


view model class :


public class CreateProfileViewModel extends BaseObservable{

private String UserID;
private boolean EditImageVisibility = false;
private String UploadPhoto;;

........................
.......................

@bindable
public boolean isEditImageVisibility() {
return EditImageVisibility;
}

@Bindable
public String getUploadPhoto() {
    return UploadPhoto;
}

public void setEditImageVisibility(boolean editImageVisibility) {
EditImageVisibility = editImageVisibility;
notifyChange();
}
public void setUploadPhoto(String uploadPhoto) {
UploadPhoto = uploadPhoto;
notifyChange();
}

@Hayk985
Copy link

Hayk985 commented Feb 19, 2019

@JakeWharton but why garbage collects it? I can use something like this in Glide library (that's the requestListener in glide) and that will not be garbage collected. So that's a library problem?

@JakeWharton
Copy link
Member

JakeWharton commented Feb 19, 2019 via email

@Hayk985
Copy link

Hayk985 commented Feb 20, 2019

@JakeWharton So why the garbage collector collects it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests