Fresco(一)Fresco的使用介绍

2021-07-25  本文已影响0人  勇敢地追

Fresco是一个出自Facebook的功能强大的图片加载库。本文就来介绍一下它的使用

(1)引入包

    implementation 'com.facebook.fresco:fresco:2.1.0'

最新的是 2.4.0,之所以没用是因为android studio 拉依赖的时候出了点问题,一直没解决,所以降了版本

(2)初始化

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Fresco.initialize(this);
    }
}

初始化两个configura。ImagePipelineConfig 和 DraweeConfig。源码解析放后面说,这里先用默认的。

(3)添加控件 SimpleDraweeView

<com.facebook.drawee.view.SimpleDraweeView
  android:id="@+id/my_image_view"
  android:layout_width="20dp"
  android:layout_height="20dp"
  fresco:fadeDuration="300"
  fresco:actualImageScaleType="focusCrop"
  fresco:placeholderImage="@color/wait_color"
  fresco:placeholderImageScaleType="fitCenter"
  fresco:failureImage="@drawable/error"
  fresco:failureImageScaleType="centerInside"
  fresco:retryImage="@drawable/retrying"
  fresco:retryImageScaleType="centerCrop"
  fresco:progressBarImage="@drawable/progress_bar"
  fresco:progressBarImageScaleType="centerInside"
  fresco:progressBarAutoRotateInterval="1000"
  fresco:backgroundImage="@color/blue"
  fresco:overlayImage="@drawable/watermark"
  fresco:pressedStateOverlayImage="@color/red"
  fresco:roundAsCircle="false"
  fresco:roundedCornerRadius="1dp"
  fresco:roundTopLeft="true"
  fresco:roundTopRight="false"
  fresco:roundBottomLeft="false"
  fresco:roundBottomRight="true"
  fresco:roundWithOverlayColor="@color/corner_color"
  fresco:roundingBorderWidth="2dp"
  fresco:roundingBorderColor="@color/border_color"
/>

以上就是 SimpleDraweeView 可以配置的各种选项。
注意,大小不支持 wrap_content,为什么Fresco中不可以使用wrap_content?主要的原因是,Drawee永远会在getIntrinsicHeight/getIntrinsicWidth中返回-1。Drawee 不像 ImageView 一样。它同一时刻可能会显示多个元素。比如在从占位图渐变到目标图时,两张图会有同时显示的时候。再比如可能有多张目标图片(低清晰度、高清晰度两张)。如果这些图像都是不同的尺寸,那么很难定义”intrinsic”尺寸。(留着这个疑问,我们到源码解析部分去看)
一般情况下,在XML设置显示效果即可, 如果想更多定制化,可以创建一个 builder 然后设置给 DraweeView

        List<Drawable> backgroundsList;
        List<Drawable> overlaysList;
        GenericDraweeHierarchyBuilder builder =
                new GenericDraweeHierarchyBuilder(getResources());
        GenericDraweeHierarchy hierarchy = builder
                .setFadeDuration(300)
                .setPlaceholderImage(new MyCustomDrawable())
                .setBackgrounds(backgroundList)
                .setOverlays(overlaysList)
                .build();
        mSimpleDraweeView.setHierarchy(hierarchy);

点进去看 GenericDraweeHierarchy。里面一共有7层。

  private static final int BACKGROUND_IMAGE_INDEX = 0;
  private static final int PLACEHOLDER_IMAGE_INDEX = 1;
  private static final int ACTUAL_IMAGE_INDEX = 2;
  private static final int PROGRESS_BAR_IMAGE_INDEX = 3;
  private static final int RETRY_IMAGE_INDEX = 4;
  private static final int FAILURE_IMAGE_INDEX = 5;
  private static final int OVERLAY_IMAGES_INDEX = 6;

通过builder把对应的drawable设置进去。比如常见的占位图(setPlaceholderImage),失败占位图(setFailureImage),进度条(setProgressBarImage)等等。还可以设置圆角(setRoundingParams)
当然,GenericDraweeHierarchy 是对所有的配置图片的说明。如果需要对加载显示的图片做更多的控制和定制,那就需要用到DraweeController

        Postprocessor myPostprocessor = new Postprocessor() {
            @Override
            public CloseableReference<Bitmap> process(Bitmap sourceBitmap, PlatformBitmapFactory bitmapFactory) {
                for (int x = 0; x < sourceBitmap.getWidth(); x+=2) {
                    for (int y = 0; y < sourceBitmap.getHeight(); y+=2) {
                        sourceBitmap.setPixel(x, y, Color.RED);
                    }
                }
                // 举例,给图片加了红色网格
                return bitmapFactory.createBitmap(sourceBitmap);
            }

            @Override
            public String getName() {
                return null;
            }

            @Override
            public CacheKey getPostprocessorCacheKey() {
                return null;
            }
        };
        ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
                .setProgressiveRenderingEnabled(true)//手动开启渐进式加载
                .setPostprocessor(myPostprocessor)//如果需要修改图片,可使用后处理器(Postprocessor)
                .build();//还可以设置 setCacheChoice,缩放和旋转图片setResizeOptions
        ControllerListener listener = new BaseControllerListener() {
            @Override
            public void onFinalImageSet(String id, Object imageInfo, Animatable animatable) {
            }

            @Override
            public void onIntermediateImageSet(String id, Object imageInfo) {
                // 渐进式图片的回调
            }

            @Override
            public void onIntermediateImageFailed(String id, Throwable throwable) {
                // 渐进式图片的回调
            }

            @Override
            public void onFailure(String id, Throwable throwable) {
            }
        };
        DraweeController controller = Fresco.newDraweeControllerBuilder()
                .setUri(uri)
                .setTapToRetryEnabled(true)
                .setOldController(draweeView.getController())//使用setOldController,这可节省不必要的内存分配
                .setControllerListener(listener)//监听下载事件
                .setImageRequest(request)//自定义ImageRequest
                .setAutoPlayAnimations(true)//设置动画图自动播放
                .build();
        draweeView.setController(controller);

本人把常用用法大体都整理了出来。由此可以看到DraweeController功能还是挺强大的
总结:SimpleDraweeView 的配置主要在 GenericDraweeHierarchy(所有图层,包括占位图) 和 DraweeController(定制要显示的图片) 里面

(4)加载图像之 ImagePipelineConfig

对于大多数的应用,Fresco的初始化,只需要以下一句代码:

Fresco.initialize(context);

当然,我们也可以自定义配置,就在 ImagePipelineConfig 里面

ImagePipelineConfig config = ImagePipelineConfig.newBuilder(context)
    .setBitmapMemoryCacheParamsSupplier(bitmapCacheParamsSupplier)
    .setCacheKeyFactory(cacheKeyFactory)
    .setDownsampleEnabled(true)
    .setWebpSupportEnabled(true)
    .setEncodedMemoryCacheParamsSupplier(encodedCacheParamsSupplier)
    .setExecutorSupplier(executorSupplier)
    .setImageCacheStatsTracker(imageCacheStatsTracker)
    .setMainDiskCacheConfig(mainDiskCacheConfig)
    .setMemoryTrimmableRegistry(memoryTrimmableRegistry)
    .setNetworkFetchProducer(networkFetchProducer)
    .setPoolFactory(poolFactory)
    .setProgressiveJpegConfig(progressiveJpegConfig)
    .setRequestListeners(requestListeners)
    .setSmallImageDiskCacheConfig(smallImageDiskCacheConfig)
    .build();
Fresco.initialize(context, config);

代码应该能看懂,本人就简单讲讲每个参数都有啥。先谈谈 Supplier
许多配置的Builder都接受一个Supplier 类型的参数而不是一个配置的实例。

public interface Supplier<T> {
  /**
   * Retrieves an instance of the appropriate type. The returned object may or may not be a new
   * instance, depending on the implementation.
   *
   * @return an instance of the appropriate type
   */
  T get();
}

它其实是一个接口。举个最简单的在freso库里面就有个实现看看,比如 DefaultBitmapMemoryCacheParamsSupplier

public class DefaultBitmapMemoryCacheParamsSupplier implements Supplier<MemoryCacheParams> {
  private static final int MAX_CACHE_ENTRIES = 256;
  private static final int MAX_EVICTION_QUEUE_SIZE = Integer.MAX_VALUE;
  private static final int MAX_EVICTION_QUEUE_ENTRIES = Integer.MAX_VALUE;
  private static final int MAX_CACHE_ENTRY_SIZE = Integer.MAX_VALUE;
  private static final long PARAMS_CHECK_INTERVAL_MS = TimeUnit.MINUTES.toMillis(5);

  private final ActivityManager mActivityManager;

  public DefaultBitmapMemoryCacheParamsSupplier(ActivityManager activityManager) {
    mActivityManager = activityManager;
  }

  @Override
  public MemoryCacheParams get() {
    return new MemoryCacheParams(
        getMaxCacheSize(),
        MAX_CACHE_ENTRIES,
        MAX_EVICTION_QUEUE_SIZE,
        MAX_EVICTION_QUEUE_ENTRIES,
        MAX_CACHE_ENTRY_SIZE,
        PARAMS_CHECK_INTERVAL_MS);
  }

  private int getMaxCacheSize() { ... }
}

其实很好理解,就是通过统一的接口来实现对参数的管理。好了,接下来就看看各个参数配置了啥
(1)setBitmapMemoryCacheParamsSupplier
默认 DefaultBitmapMemoryCacheParamsSupplier,就是上面贴出来的
最大cache项是256,PARAMS_CHECK_INTERVAL_MS 就是 每隔5分钟就可检查一下Supplier。
getMaxCacheSize 根据系统为你整个app分配的memory大小来决定。系统给的小于32M,就是4M,系统给的小于64M,那就8M
(2)setCacheKeyFactory
默认 DefaultCacheKeyFactory。它提供了4种key

  @Override
  public CacheKey getBitmapCacheKey(ImageRequest request, Object callerContext) {
    return new BitmapMemoryCacheKey(
        getCacheKeySourceUri(request.getSourceUri()).toString(),
        request.getResizeOptions(),
        request.getRotationOptions(),
        request.getImageDecodeOptions(),
        null,
        null,
        callerContext);
  }

  @Override
  public CacheKey getPostprocessedBitmapCacheKey(ImageRequest request, Object callerContext) {
    final Postprocessor postprocessor = request.getPostprocessor();
    final CacheKey postprocessorCacheKey;
    final String postprocessorName;
    if (postprocessor != null) {
      postprocessorCacheKey = postprocessor.getPostprocessorCacheKey();
      postprocessorName = postprocessor.getClass().getName();
    } else {
      postprocessorCacheKey = null;
      postprocessorName = null;
    }
    return new BitmapMemoryCacheKey(
        getCacheKeySourceUri(request.getSourceUri()).toString(),
        request.getResizeOptions(),
        request.getRotationOptions(),
        request.getImageDecodeOptions(),
        postprocessorCacheKey,
        postprocessorName,
        callerContext);
  }

  @Override
  public CacheKey getEncodedCacheKey(ImageRequest request, @Nullable Object callerContext) {
    return getEncodedCacheKey(request, request.getSourceUri(), callerContext);
  }

  @Override
  public CacheKey getEncodedCacheKey(
      ImageRequest request, Uri sourceUri, @Nullable Object callerContext) {//点进去看就是sourceUri
    return new SimpleCacheKey(getCacheKeySourceUri(sourceUri).toString());
  }

  protected Uri getCacheKeySourceUri(Uri sourceUri) {
    return sourceUri;
  }

(3)setEncodedMemoryCacheParamsSupplier
默认 DefaultEncodedMemoryCacheParamsSupplier.挺简单的,不贴代码了
(4)setExecutorSupplier
默认 DefaultExecutorSupplier

public class DefaultExecutorSupplier implements ExecutorSupplier {
  // Allows for simultaneous reads and writes.
  private static final int NUM_IO_BOUND_THREADS = 2;
  private static final int NUM_LIGHTWEIGHT_BACKGROUND_THREADS = 1;

  private final Executor mIoBoundExecutor;
  private final Executor mDecodeExecutor;
  private final Executor mBackgroundExecutor;
  private final Executor mLightWeightBackgroundExecutor;

  public DefaultExecutorSupplier(int numCpuBoundThreads) {
    mIoBoundExecutor =
        Executors.newFixedThreadPool(
            NUM_IO_BOUND_THREADS,
            new PriorityThreadFactory(
                Process.THREAD_PRIORITY_BACKGROUND, "FrescoIoBoundExecutor", true));
    mDecodeExecutor =
        Executors.newFixedThreadPool(
            numCpuBoundThreads,
            new PriorityThreadFactory(
                Process.THREAD_PRIORITY_BACKGROUND, "FrescoDecodeExecutor", true));
    mBackgroundExecutor =
        Executors.newFixedThreadPool(
            numCpuBoundThreads,
            new PriorityThreadFactory(
                Process.THREAD_PRIORITY_BACKGROUND, "FrescoBackgroundExecutor", true));
    mLightWeightBackgroundExecutor =
        Executors.newFixedThreadPool(
            NUM_LIGHTWEIGHT_BACKGROUND_THREADS,
            new PriorityThreadFactory(
                Process.THREAD_PRIORITY_BACKGROUND, "FrescoLightWeightBackgroundExecutor", true));
  }
}

简单介绍一下

(5)setMainDiskCacheConfig
默认 DiskCacheConfig。我摘出了重要的代码

    private long mMaxCacheSize = 40 * ByteConstants.MB;
    private long mMaxCacheSizeOnLowDiskSpace = 10 * ByteConstants.MB;
    private long mMaxCacheSizeOnVeryLowDiskSpace = 2 * ByteConstants.MB;
    private EntryEvictionComparatorSupplier mEntryEvictionComparatorSupplier =
        new DefaultEntryEvictionComparatorSupplier();

注意 DefaultEntryEvictionComparatorSupplier。里面就是 Comparator,根据时间戳来决定是否排除响应cache,先排除时间久的

(4)加载图片 setImageURI

        Uri uri = Uri.parse("https://t7.baidu.com/it/u=4036010509,3445021118&fm=193&f=GIF");
        SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
        draweeView.setImageURI(uri);

大致流程如下:

具体源码下一篇说

上一篇下一篇

猜你喜欢

热点阅读