Fresco image loading callback

2019-02-27 06:21发布

问题:

I have just migrated to the Fresco library for loading images in my app.

I need to listen to Image Loading Events, of course I read this article in documentation Listening to download events

This is exactly what I need, but.... There few things that I don't like.

My goal is to hide View if it fails to download it from the net.

  1. I cannot reference SimpleDraweeView from controller, even on callback method. I need to hide View, but it seems that I cannot get reference to it.

  2. Each time I need to load image, I need to create object of controller using Builder, and this can cause performance issues when using this approach with list of a lot of items with images.

holder.simpleDraweeViewImage.setController(Fresco.newDraweeControllerBuilder() .setControllerListener(controllerListener) .setUri(currentItem.getImage()) .build());

I need to able to have reference to the SimpleDraweeView from controller, and in MVC pattern approach it seems okay if controller is aware about view.

Please suggest the best way to rich my goal.

Thanks.

回答1:

Regarding 1, perhaps you can do something like this:

class ControllerListenerWithView() extends BaseControllerListener {
   private final WeakReference<View> mViewReference;

   ControllerListenerWithView(View view) {
     mViewReference = new WeakReference<>(view);
   }

   @Nullable
   protected View getView() {
     return mViewReference.get();
   }
}

Then:

ControllerListener controllerListener = new ControllerListenerWithView(holder.simpleDraweeViewImage) {
  @Override
  public void onFailure(String id, Throwable throwable) {
    View view = getView();
    if (view != null) {
      view.setVisibility(View.GONE);
    }
  }
};

If you don't have the view accessible at the listener creation time, instead of passing view via listener constructor, you can add a setter method and do:

controllerListener.setView(holder.simpleDraweeViewImage);
controller = ...
holder.simpleDraweeViewImage.setController(controller);

If this looks ugly to you, well, that's because it is ugly :) Design that involves circular references is just ugly. DraweeController doesn't have a reference to the view (not directly at least). DraweeController references a DraweeHierarchy which references Drawables and the top-level drawable has a WeakReference to the parent view in order to propagate Drawable.Callback events. But that's it. DraweeController doesn't need view and we can't/won't keep reference to the view in it. The reason for that is that DraweeControllers and DraweeHierarchies can be used in contexts other than View and it is unnecessary for controller to have a back reference to the view. DraweeController controls DraweeHierarchy, not the view.

Regarding 2, while building controller, you can specify setOldController(view.getController()). That way the old controller which you are replacing will be reused while building a new one. This saves allocations and helps scroll-perf.



回答2:

Can hide on onFailure method:

ControllerListener listener = new BaseControllerListener<ImageInfo>() {

                @Override
                public void onFinalImageSet(String id, @Nullable ImageInfo imageInfo, @Nullable Animatable animatable) {
                   //Action on final image load
                }
                @Override
                public void onFailure(String id, Throwable throwable) {
                    //Action on failure
                }

            };
            DraweeController controller = Fresco.newDraweeControllerBuilder()
                    .setUri(uri)
                    .setControllerListener(listener)
                    .build();
            draweeView.setController(controller);