毕加索无法在 StorageReference 的 onSuccess 方法中加载图像

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

在运行时 onStart() 方法上尝试 Picasso 时,它通常使用从控制台下载 link 从 Firebase 加载图像。

但是当我尝试在 StorageReference onSuccess 方法中加载图像时,它就是行不通。

此 class 正在扩展片段:

@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
                        }

                    }
                });

    }

}

这是我在另一个 Class:

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

清单:

<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" />

我的XML布局:

<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>

我的观点初始化:

@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;
}

更新二:

虽然我发布的代码对我有效,但您表示它对您无效。我的测试不使用片段,我不知道你使用的是什么版本的库,现在知道片段是如何添加到布局中的,等等。不同行为的可能原因太多,无法有效调试它在 SO.

作为实验,您可以尝试使用 Glide 和 FirebaseUI 代替 Picasso 来执行下载。代码如下所示,对我有用。因为下载是从存储引用而不是 URL,这种方法需要对存储的读取权限。您还需要将这些行添加到构建依赖项中:

// 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());
            }
        }
    });

更新:

额外的调试显示上传和下载都成功完成。问题出在下载图像的渲染上。 documentation for CircleImageView表示从Picasso下载时必须使用noFade()

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().

还发现渲染小图,大图显示为黑色。 fit() 已添加以使 Picasso 调整图像大小。


要同时检测上传成功和失败,请使用 completion listener 而不仅仅是成功侦听器,然后测试 task.isSuccessful()。上传可能因安全规则而失败,或无效 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();
    }
});

有可能在这行代码中:

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

profView 正在使用片段重新创建,Picasso.into(View view) 保留对视图的周引用。

来自文档:

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.

尝试使用:

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

我刚从 CircleImageView 切换到普通的 ImageView,它突然工作了。

可能与CircleImageView冲突。

尝试使用 ImageView 更改它或将 CircleImageView 定义为 ImageView

尝试:

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

de.hdodenhof.circleimageview.CircleImageView

的限制
  • ScaleType 始终是 CENTER_CROP,如果您尝试更改它,将会出现异常。这是(目前)设计的,因为它非常适合个人资料图片。
  • 不支持启用 adjustViewBounds,因为这需要不受支持的 ScaleType
  • 如果你使用像Picasso或Glide这样的图片加载库,你需要禁用它们的淡入淡出动画以避免图片混乱。对于 Picasso 使用 noFade() 选项,对于 Glide 使用 dontAnimate()。如果要保留淡入动画,则必须将图像提取到 Target 中,并在接收 Bitmap 时自己应用自定义动画。
  • 将 TransitionDrawable 与 CircleImageView 一起使用无法正常工作并导致图像混乱。