美文网首页
Fresco(一)Fresco的使用介绍

Fresco(一)Fresco的使用介绍

作者: 勇敢地追 | 来源:发表于2021-07-25 13:36 被阅读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));
      }
    }
    

    简单介绍一下

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

    大致流程如下:

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

    具体源码下一篇说

    相关文章

      网友评论

          本文标题:Fresco(一)Fresco的使用介绍

          本文链接:https://www.haomeiwen.com/subject/usdlkltx.html