首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Fresco(一)Fresco的使用介绍

Fresco(一)Fresco的使用介绍

作者头像
提莫队长
发布2021-07-27 12:00:55
1.3K0
发布2021-07-27 12:00:55
举报
文章被收录于专栏:刘晓杰刘晓杰

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

简单介绍一下

  • mIoBoundExecutor---io线程池,用于磁盘操作,本地文件的读取,默认2个线程
  • mDecodeExecutor---解析图片的,线城数可以自己设置
  • mBackgroundExecutor---后台工作的,线城数可以自己设置,暂时不知道干嘛的,等后面的代码解析
  • mLightWeightBackgroundExecutor---轻量级后台工作,等解析,默认1个线程

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

大致流程如下:

  • 检查内存缓存,如有,返回
  • 后台线程开始后续工作
  • 检查是否在未解码内存缓存中。如有,解码,变换,返回,然后缓存到内存缓存中。
  • 检查是否在磁盘缓存中,如果有,变换,返回。缓存到未解码缓存和内存缓存中。
  • 从网络或者本地加载。加载完成后,解码,变换,返回。存到各个缓存中。

具体源码下一篇说

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • (1)引入包
  • (2)初始化
  • (3)添加控件 SimpleDraweeView
  • (4)加载图像之 ImagePipelineConfig
  • (4)加载图片 setImageURI
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档