GGWP
GGWP

Reputation: 1121

Picasso can't load image inside StorageReference's onSuccess method

When trying Picasso on the runtime onStart() method it normally loads the image from Firebase using the download link from the console.

But when I try to load an image inside StorageReference onSuccess method it just won't work.

This class is extending Fragment:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == GALLERY_IN && resultCode == Activity.RESULT_OK) {
        isGranted = true;
        String path = FireStorage.retUID() + "/";
        showIndProgress();
        Uri uri = data.getData();

        StorageReference childPathRef = FireStorage.storageRef.child(path + uri.getLastPathSegment());
        childPathRef.putFile(uri)
                .addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>() {
                    @Override
                    public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) {
                        if(task.isSuccessful()) {
                            Picasso.with(mContext).load(task.getResult().getDownloadUrl())
                                    .into(profView);

                            Picasso.Builder build = new Picasso.Builder(mContext);
                            build.listener(new Picasso.Listener() {
                                @Override
                                public void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception) {
                                    Toast.makeText(mContext, "exception\n" + exception.toString(), Toast.LENGTH_SHORT).show();

                                }
                            });
                            progressDialog.dismiss();
                        } else {
                            //failed
                        }

                    }
                });

    }

}

This is my FirebaseStorage instance on another Class:

public static FirebaseStorage storage = FirebaseStorage.getInstance();
public static StorageReference storageRef = 
                           storage.getReferenceFromUrl("gs://myurl.appspot.com/");

Manifest:

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

My XML layout:

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:background="@drawable/gcover"
    android:id="@+id/relativeLayout">

    <de.hdodenhof.circleimageview.CircleImageView xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/profPhoto"
        android:clickable="true"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:padding="20dp"
        android:src="@drawable/default_prof"
        android:scaleType="centerCrop"
        app:civ_border_color="@color/trans"
        app:civ_border_width="2dp"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />

</RelativeLayout>

My views initialization:

@Override
public View onCreateView(LayoutInflater inflater,
                         ViewGroup container,
                         Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_prof, container, false);
    profView = (CircleImageView) view.findViewById(R.id.profPhoto);

    btn1 = (Button) view.findViewById(R.id.btn_1);
    btn2 = (Button) view.findViewById(R.id.btn_2);
    btn3 = (Button) view.findViewById(R.id.btn_3);
    return view;
}

Upvotes: 12

Views: 1470

Answers (5)

Wrobel
Wrobel

Reputation: 1072

It is possible that in this line of code:

     Picasso.with(mContext).load(task.getResult().getDownloadUrl())
                                .into(profView);    

profView is recreating with fragment, and Picasso.into(View view) keeps week reference to view.

From docs:

public void into(android.widget.ImageView target) Asynchronously fulfills the request into the specified ImageView. Note: This method keeps a weak reference to the ImageView instance and will automatically support object recycling.

Try to use:

public void into(@NotNull ImageView target,
             Callback callback)

Upvotes: 1

A Maharaja
A Maharaja

Reputation: 1247

Limitations for de.hdodenhof.circleimageview.CircleImageView

  • The ScaleType is always CENTER_CROP and you'll get an exception if you try to change it. This is (currently) by design as it's perfectly fine for profile images.
  • Enabling adjustViewBounds is not supported as this requires an unsupported ScaleType
  • If you use an image loading library like Picasso or Glide, you need to disable their fade animations to avoid messed up images. For Picasso use the noFade() option, for Glide use dontAnimate(). If you want to keep the fadeIn animation, you have to fetch the image into a Target and apply a custom animation yourself when receiving the Bitmap.
  • Using a TransitionDrawable with CircleImageView doesn't work properly and leads to messed up images.

Upvotes: 2

J&#233;w&#244;m&#39;
J&#233;w&#244;m&#39;

Reputation: 3971

Maybe it's a conflict with the CircleImageView.

Try to change it with an ImageView or define the CircleImageView like an ImageView.

Try :

ImageView profView = (ImageView) view.findViewById(R.id.profPhoto);

Upvotes: 0

GGWP
GGWP

Reputation: 1121

I just switched from CircleImageView to a normal ImageView and it suddenly worked.

Upvotes: 1

Bob Snyder
Bob Snyder

Reputation: 38299

Update 2:

While the code I posted works for me, you indicate that it fails for you. My test does not use a fragment, I don't know what version of the libraries you are using, it's now known how the fragment is being added to the layout, etc. There are too many possible causes of the different behavior to effectively debug it on SO.

As an experiment, you can try using Glide and FirebaseUI to perform the download instead of Picasso. The code is shown below and works for me. Because the download is from the storage reference and not the URL, this approach requires read access to the storage. You also need to add these lines to your build dependencies:

// FirebaseUI 2.0.1 is the version to use for Firebase SDK version 11.0.1
// SDK/Firebase version compatibility is important.
// See the FirebaseUI docs for a table of compatible versions.
compile 'com.firebaseui:firebase-ui-storage:2.0.1'
compile 'com.github.bumptech.glide:glide:3.8.0'

.

childPathRef.putFile(Uri.fromFile(file))
    .addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>() {
        @Override
        public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) {
            if (task.isSuccessful()) {
                Log.d(TAG, "Upload: SUCCESS");

                Glide.with(mContext)
                    .using(new FirebaseImageLoader())
                    .load(childPathRef)
                    .dontAnimate()
                    .listener(new RequestListener<StorageReference, GlideDrawable>() {
                        @Override
                        public boolean onException(Exception e, StorageReference model,
                               Target<GlideDrawable> target, boolean isFirstResource) {
                            Log.e(TAG, "Download: FAILED: ", e);
                            return false;
                        }
                        @Override
                        public boolean onResourceReady(GlideDrawable resource,
                               StorageReference model, Target<GlideDrawable> target,
                               boolean isFromMemoryCache, boolean isFirstResource) {
                            Log.d(TAG, "Download: SUCCESS");
                            return false;
                        }
                    })
                    .into(mCircleImageView);
            } else {
                Log.e(TAG, "Upload: FAILED: " + task.getException().getMessage());
            }
        }
    });

Update:

Additional debugging showed that the upload and download were both completing sucessfully. The problem was with the rendering of the downloaded image. The documentation for CircleImageView indicates that noFade() must be used when downloading from Picasso:

If you use an image loading library like Picasso or Glide, you need to disable their fade animations to avoid messed up images. For Picasso use the noFade() option, for Glide use dontAnimate().

It was also found that small images were rendered but large images were displayed as black. fit() was added to cause Picasso to resize the image.


To detect both upload success and failure, use a completion listener instead of just a success listener, and test task.isSuccessful(). The upload is probably failing because of security rules, or invalid StorageReference:

StorageReference childPathRef = Class.storageRef.child(path + uri.getLastPathSegment());
childPathRef.putFile(uri).addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>() {
    @Override
    public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) {
        if (task.isSuccessful()) {
            Log.d(TAG, "Upload: SUCCESS");
            Picasso.with(mContext)
                .load(task.getResult().getDownloadUrl())
                .noFade()
                .fit()
                .into(profView, new Callback() {
                    @Override
                    public void onSuccess() {
                        Log.d(TAG, "Download: SUCCESS");
                        Toast.makeText(mContext, "download done" , Toast.LENGTH_SHORT).show();                        }

                    @Override
                    public void onError() {
                        Log.d(TAG, "Download: FAILED");
                    }
                });
            Toast.makeText(mContext, "upload done" , Toast.LENGTH_SHORT).show();
        } else {
            Log.e(TAG, "Upload: FAILED: " + task.getException().getMessage());
        }
        progressDialog.dismiss();
    }
});

Upvotes: 9

Related Questions