Fresco:我很胖,可是我很强大

安卓基础开发库,让开发简单点。
DevRing & Demo地址https://github.com/LJYcoder/DevRing

学习/参考地址:
https://www.fresco-cn.org/docs/index.html
http://blog.csdn.net/wyb112233/article/details/49637685
http://blog.csdn.net/android_ls/article/details/53137867

前言

Fresco是一个出自Facebook的功能强大的图片加载库

优点:
1)内存自动回收。图片不可见时,会及时自动释放所占用的内存,尽可能地避免OOM
2)三级缓存机制。两级内存缓存(解码的与未解码的)+一级磁盘缓存,提升加载速度,节省内存占用空间
3)支持各种加载场景。如动图加载、高斯模糊等常见的图片加载场景。另外还提供了独特的渐进式加载先加载小图再加载大图加载进度等功能(很强大)。

缺点:
1)体积大(很胖)。较其他主流图片库体积要大不少
2)侵入性较强。须使用它提供的SimpleDraweeView来代替ImageView加载显示图片

综合来说,如果你的应用对图片的显示、加载等要求高的话,那就建议使用Fresco。但如果要求没那么高的话就用Glide或其它库吧。
关于Fresco与Glide的对比可以参考http://www.jianshu.com/p/6729dc17586b


介绍

下面通过 配置、SimpleDraweeView、加载图片、混淆、其他 这几个部分来介绍。

1. 配置

1.1 添加依赖

compile 'com.facebook.fresco:fresco:1.5.0'
compile 'com.facebook.fresco:animated-gif:1.5.0'//加载gif动图需添加此库
compile 'com.facebook.fresco:animated-webp:1.5.0'//加载webp动图需添加此库
compile 'com.facebook.fresco:webpsupport:1.5.0'//支持webp需添加此库
compile 'com.facebook.fresco:imagepipeline-okhttp3:1.5.0'//网络实现层使用okhttp3需添加此库
compile 'jp.wasabeef:fresco-processors:2.1.0@aar'//用于提供fresco的各种图片变换

1.2 设置磁盘缓存

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setMainDiskCacheConfig(DiskCacheConfig.newBuilder(context)
.setBaseDirectoryPath(context.getExternalCacheDir())//设置磁盘缓存的路径
.setBaseDirectoryName(BaseConstants.APP_IMAGE)//设置磁盘缓存文件夹的名称
.setMaxCacheSize(MAX_DISK_CACHE_SIZE)//设置磁盘缓存的大小
.build());

1.3 设置内存缓存

设置已解码的内存缓存(Bitmap缓存)

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setBitmapMemoryCacheParamsSupplier(new Supplier<MemoryCacheParams>() {
       public MemoryCacheParams get() {
           int MAX_HEAP_SIZE = (int) Runtime.getRuntime().maxMemory();
           int MAX_MEMORY_CACHE_SIZE = MAX_HEAP_SIZE / 5;//取手机内存最大值的五分之一作为可用的最大内存数

           MemoryCacheParams bitmapCacheParams = new MemoryCacheParams( //
                   // 可用最大内存数,以字节为单位
                   MAX_MEMORY_CACHE_SIZE,
                   // 内存中允许的最多图片数量
                   Integer.MAX_VALUE,
                   // 内存中准备清理但是尚未删除的总图片所可用的最大内存数,以字节为单位
                   MAX_MEMORY_CACHE_SIZE,
                   // 内存中准备清除的图片最大数量
                   Integer.MAX_VALUE,
                   // 内存中单图片的最大大小
                   Integer.MAX_VALUE);
           return bitmapCacheParams;
       }
   });

设置未解码的内存缓存

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setEncodedMemoryCacheParamsSupplier(new Supplier<MemoryCacheParams>() {
       public MemoryCacheParams get() {

           MemoryCacheParams bitmapCacheParams;
           //设置大小,可参考上面已解码的内存缓存
           return bitmapCacheParams;
       }
   });

1.4 设置内存紧张时的应对措施

MemoryTrimmableRegistry memoryTrimmableRegistry = NoOpMemoryTrimmableRegistry.getInstance();
memoryTrimmableRegistry.registerMemoryTrimmable(new MemoryTrimmable() {
     @Override
     public void trim(MemoryTrimType trimType) {
         final double suggestedTrimRatio = trimType.getSuggestedTrimRatio();

         if (MemoryTrimType.OnCloseToDalvikHeapLimit.getSuggestedTrimRatio() == suggestedTrimRatio 
         || MemoryTrimType.OnSystemLowMemoryWhileAppInBackground.getSuggestedTrimRatio() == suggestedTrimRatio 
         || MemoryTrimType.OnSystemLowMemoryWhileAppInForeground.getSuggestedTrimRatio() == suggestedTrimRatio) { 
                 //清空内存缓存
                 ImagePipelineFactory.getInstance().getImagePipeline().clearMemoryCaches();
         }
     }
 });
 
ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setMemoryTrimmableRegistry(memoryTrimmableRegistry);

1.5 设置渐进式显示的效果

ProgressiveJpegConfig progressiveJpegConfig = new ProgressiveJpegConfig() {
      @Override
      public int getNextScanNumberToDecode(int scanNumber) {
          //返回下一个需要解码的扫描次数
          return scanNumber + 2;
      }    
    
      public QualityInfo getQualityInfo(int scanNumber) {
          boolean isGoodEnough = (scanNumber >= 5);
          //确定多少个扫描次数之后的图片才能开始显示。
          return ImmutableQualityInfo.of(scanNumber, isGoodEnough, false);
      }
};
//具体含义可参考 http://wiki.jikexueyuan.com/project/fresco/progressive-jpegs.html

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setProgressiveJpegConfig(progressiveJpegConfig);
//或者使用默认的效果
//imagePipelineConfigBuilder.setProgressiveJpegConfig(new SimpleProgressiveJpegConfig());

设置完效果后,还需在下面介绍的ImageRequest中开启渐进式加载。

1.6 允许解码时调整图片大小

允许后,即可在后面介绍的ImageRequest中对结合ResizeOptions对解码后的图片大小进行调整,从而优化了图片所占大小。默认只支持JPEG图,所以要设置该属性来支持png、jpg、webp。

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setDownsampleEnabled(true);

1.7 开启Log

FLog.setMinimumLoggingLevel(FLog.VERBOSE);
Set<RequestListener> requestListeners = new HashSet<>();
requestListeners.add(new RequestLoggingListener());

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setRequestListeners(requestListeners);

1.8 初始化

上面的各种配置都是通过ImagePipelineConfig进行的,接着需要进行初始化,在Application中初始化即可

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);

//...进行各种设置

ImagePipelineConfig config = imagePipelineConfigBuilder.build();
Fresco.initialize(context, config);

如果想直接使用默认的配置,可以

Fresco.initialize(context);

2. SimpleDraweeView

Fresco要求使用SimpleDraweeView来替换ImageView进行图片的加载与显示,不少人也是因为这一点而不想使用Fresco。
下面介绍SimpleDraweeView在xml中的各种属性

//在最外层布局的属性中加入xmlns:fresco="http://schemas.android.com/apk/res-auto"

<com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/sdv"
        android:layout_width="150dp"
        android:layout_height="150dp"
        fresco:actualImageScaleType="centerCrop"
        fresco:fadeDuration="2000"
        fresco:failureImage="@mipmap/ic_launcher"
        fresco:failureImageScaleType="centerCrop"
        fresco:placeholderImage="@mipmap/ic_launcher"
        fresco:placeholderImageScaleType="centerCrop"
        fresco:progressBarAutoRotateInterval="1500"
        fresco:progressBarImage="@drawable/rotate"
        fresco:progressBarImageScaleType="centerCrop"
        fresco:retryImage="@mipmap/ic_launcher"
        fresco:retryImageScaleType="centerCrop"
        fresco:backgroundImage="@mipmap/ic_launcher"
        fresco:overlayImage="@mipmap/ic_launcher"
        fresco:pressedStateOverlayImage="@mipmap/ic_launcher"
        fresco:roundAsCircle="false"
        fresco:roundedCornerRadius="7dp"
        fresco:roundTopLeft="true"
        fresco:roundTopRight="false"
        fresco:roundBottomLeft="false"
        fresco:roundBottomRight="true"
        fresco:roundWithOverlayColor="@color/colorAccent"
        fresco:roundingBorderWidth="2dp"
        fresco:roundingBorderColor="@color/colorPrimary"
        fresco:viewAspectRatio="1"/>

上面各个属性的作用说明:(来自http://www.jianshu.com/p/8ff81be83101

属性 作用说明
actualImageScaleType 加载完成的图片的缩放样式
fadeDuration 由进度条和占位符图片渐变过渡到加载完成的图片所使用的时间间隔
failureImage 加载失败所使用的图片
failureImageScaleType 加载失败所使用的图片的缩放样式
placeholderImage 占位符图片
placeholderImageScaleType 占位符图片的缩放样式
progressBarAutoRotateInterval 旋转进度条旋转1圈所需要的时间
progressBarImage 旋转进度条所使用的图片
progressBarImageScaleType 旋转进度条所使用的图片的缩放样式
retryImage 重试所使用的图片
retryImageScaleType 重试所使用的图片的缩放样式
backgroundImage 背景图片
overlayImage 覆盖在加载完成后图片上的叠加图片
pressedStateOverlayImage 按压状态下的叠加图片
roundAsCircle 是否将图片剪切为圆形
roundedCornerRadius 圆角图片时候,圆角的半径大小
roundTopLeft 左上角是否为圆角
roundTopRight 右上角是否为圆角
roundBottomLeft 左下角是否为圆角
roundBottomRight 右下角是否为圆角
roundWithOverlayColor 圆角或圆形图叠加的颜色,只能是颜色
roundingBorderWidth 圆角或圆形图边框的宽度
roundingBorderColor 圆角或圆形图边框的颜色
viewAspectRatio 设置宽高比

各个属性的效果图,可到http://blog.csdn.net/wyb112233/article/details/49637685查看,我就不重复造轮子了。
*注意:
1)android:src属性对于SimpleDraweeView无效,必要的话可用fresco:placeholderImage来设置。
2)SimpleDraweeView不支持android:layout_width和android:layout_height同时都设为wrap_content。

3. 加载图片

使用Fresco加载图片,大致是按以下流程进行的。

  1. 设置Hierarchay(上面xml中的属性以及显示加载进度条等,可在这进行设置)
  2. 构建ImageRequest(加载路径、开启渐进式加载、图片变换、调整解码图片大小等,可在这进行设置)
  3. 构建DraweeController(动图加载、失败后点击重新加载等,可在这进行设置)
  4. 进行图片加载

3.1 设置Hierarchay

虽然xml中的属性都能在这一步通过代码进行设置,但一般只在这设置一些统一固定的属性,比如加载占位图、加载失败图等。 另外,显示图片的加载进度也是在这里设置。

Resources res = MyApplication.getInstance().getResources();
Drawable retryImage = ResourcesCompat.getDrawable(res, R.mipmap.ic_image_load, null);
Drawable failureImage = ResourcesCompat.getDrawable(res, R.mipmap.ic_image_load, null);
Drawable placeholderImage = ResourcesCompat.getDrawable(res, R.mipmap.ic_image_load, null);

//对Hierarchy进行设置,如各种状态下显示的图片
public void setHierarchay(GenericDraweeHierarchy hierarchy) {
    if (hierarchy != null) {
        //重新加载显示的图片
        hierarchy.setRetryImage(retryImage); 
        //加载失败显示的图片
        hierarchy.setFailureImage(failureImage, ScalingUtils.ScaleType.CENTER_CROP); 
        //加载完成前显示的占位图
        hierarchy.setPlaceholderImage(placeholderImage, ScalingUtils.ScaleType.CENTER_CROP);
        //设置加载成功后图片的缩放模式
        hierarchy.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP);

        //显示加载进度条,使用自带的new ProgressBarDrawable()
        //默认会显示在图片的底部,可以设置进度条的颜色。
        hierarchy.setProgressBarImage(new ProgressBarDrawable());
        //设置图片加载为圆形
        hierarchy.setRoundingParams(RoundingParams.asCircle());
        //设置图片加载为圆角,并可设置圆角大小
        hierarchy.setRoundingParams(RoundingParams.fromCornersRadius(radius));

        //其他设置请查看具体API。
        
    }
}

3.2 构建ImageRequest

/**
 * 构建、获取ImageRequest
 * @param uri 加载路径
 * @param simpleDraweeView 加载的图片控件
 * @return ImageRequest
 */
public ImageRequest getImageRequest(Uri uri, SimpleDraweeView simpleDraweeView) {

    int width;
    int height;
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        width = simpleDraweeView.getWidth();
        height = simpleDraweeView.getHeight();
    } else {
        width = simpleDraweeView.getMaxWidth();
        height = simpleDraweeView.getMaxHeight();
    }

    //根据请求路径生成ImageRequest的构造者
    ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(uri);
    //调整解码图片的大小
    if (width > 0 && height > 0) {
        builder.setResizeOptions(new ResizeOptions(width, height));
    }
    //设置是否开启渐进式加载,仅支持JPEG图片
    builder.setProgressiveRenderingEnabled(true);

    //图片变换处理
    CombinePostProcessors.Builder processorBuilder = new CombinePostProcessors.Builder();
    //加入模糊变换
    processorBuilder.add(new BlurPostprocessor(context, radius));
    //加入灰白变换
    processorBuilder.add(new GrayscalePostprocessor());
    //应用加入的变换   
    builder.setPostprocessor(processorBuilder.build());
    //更多图片变换请查看https://github.com/wasabeef/fresco-processors  
    return builder.build();
}

3.3 构建DraweeController

/**
* 构建、获取Controller
* @param request
* @param oldController
* @return
*/
public DraweeController getController(ImageRequest request, @Nullable DraweeController oldController) {

   PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder();
   builder.setImageRequest(request);//设置图片请求
   builder.setTapToRetryEnabled(false);//设置是否允许加载失败时点击再次加载
   builder.setAutoPlayAnimations(true);//设置是否允许动画图自动播放
   builder.setOldController(oldController);
   return builder.build();
}

3.4 进行图片加载

创建一个loadImage方法将上面的Hierarchy、ImageRequest、DraweeController串在一起,供具体的加载场景使用

/**
* 加载图片核心方法
* 
* @param simpleDraweeView              图片加载控件
* @param uri                           图片加载地址

*/
public void loadImage(SimpleDraweeView simpleDraweeView, Uri uri) {
  //设置Hierarchy
   setHierarchay(simpleDraweeView.getHierarchy());
   //构建并获取ImageRequest
   ImageRequest imageRequest = getImageRequest(uri, simpleDraweeView);
   //构建并获取Controller
   DraweeController draweeController = getController(imageRequest, simpleDraweeView.getController());
   //开始加载
   simpleDraweeView.setController(draweeController);
}

具体的加载场景:

  • 加载网络图片,包括gif/webp动图
 public void loadNetImage(SimpleDraweeView simpleDraweeView, String url) {
     Uri uri = Uri.parse(url);
     loadImage(simpleDraweeView, uri);
 }
  • 加载本地文件图片
public void loadLocalImage(SimpleDraweeView simpleDraweeView, String fileName) {
    Uri uri = Uri.parse("file://" + fileName);
    loadImage(simpleDraweeView, uri);
}
  • 加载res下资源图片
public void loadResourceImage(SimpleDraweeView simpleDraweeView, @DrawableRes int resId) {
    Uri uri = Uri.parse("res:///" + resId);
    loadImage(simpleDraweeView, uri);
}
  • 加载ContentProvider下的图片
public void loadContentProviderImage(SimpleDraweeView simpleDraweeView, int resId) {
    Uri uri = Uri.parse("content:///" + resId);
    loadImage(simpleDraweeView, uri);
}
  • 加载asset下的图片
public void loadAssetImage(SimpleDraweeView simpleDraweeView, int resId) {
    Uri uri = Uri.parse("asset:///" + resId);
    loadImage(simpleDraweeView, uri);
}
  • 加载网络图片,先加载小图,待大图加载完成后再替换掉小图

这个需要修改一下DraweeController的构建,通过setLowResImageRequest来添加小图请求

public DraweeController getSmallToBigController(ImageRequest smallRequest, ImageRequest bigRequest, @Nullable DraweeController oldController) {

    PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder();
    builder.setLowResImageRequest(smallRequest);//小图的图片请求
    builder.setImageRequest(bigRequest);//大图的图片请求
    builder.setTapToRetryEnabled(false);//设置是否允许加载失败时点击再次加载
    builder.setAutoPlayAnimations(true);//设置是否允许动画图自动播放
    builder.setOldController(oldController);
    return builder.build();
}
public void loadImageSmallToBig(SimpleDraweeView simpleDraweeView, Uri smallUri, Uri bigUri) {
    //设置Hierarchy
    setHierarchay(simpleDraweeView.getHierarchy());
    //构建小图的图片请求
    ImageRequest smallRequest = getImageRequest(smallUri, simpleDraweeView);
    //构建大图的图片请求
    ImageRequest bigRequest = getImageRequest(bigUri, simpleDraweeView);
    //构建Controller
    DraweeController draweeController = getSmallToBigController(smallRequest, bigRequest, simpleDraweeView.getController());
    //开始加载
    simpleDraweeView.setController(draweeController);
}
//加载网络图片,先加载小图,待大图加载完成后替换
public void loadNetImageSmallToBig(SimpleDraweeView simpleDraweeView, String smallUrl, String bigUrl) {
    Uri smallUri = Uri.parse(smallUrl);
    Uri bigUri = Uri.parse(bigUrl);
    loadImageSmallToBig(simpleDraweeView, smallUri, bigUri);
}

4. 混淆

在proguard-rules.pro文件中添加以下内容进行混淆配置

#fresco开始
-keep class com.facebook.fresco.** { *; }
-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
-keep @com.facebook.common.internal.DoNotStrip class *
-keepclassmembers class * {
     @com.facebook.common.internal.DoNotStrip *;
}
-keep class com.facebook.imagepipeline.gif.** { *; }
-keep class com.facebook.imagepipeline.webp.* { *; }
-keepclassmembers class * {
    native <methods>;
}
-dontwarn okio.**
-dontwarn com.squareup.okhttp.**
-dontwarn okhttp3.**
-dontwarn javax.annotation.**
-dontwarn com.android.volley.toolbox.**
-keep class com.facebook.imagepipeline.animated.factory.AnimatedFactoryImpl {
    public AnimatedFactoryImpl(com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory,com.facebook.imagepipeline.core.ExecutorSupplier);
}
#fresco结束

5. 其他

5.1 缓存策略

Fresco采用三级缓存机制,两级内存缓存+一级磁盘缓存,其中两级内存缓存分为已解码的图片缓存(Bitmap缓存)和未解码的图片缓存。
下面通过加载流程来了解其缓存策略。

  1. 根据Uri到已解码的图片缓存中查找是否存在对应的Bitmap。如果存在,则返回Bitmap显示;
    如果不存在,则到未解码的图片缓存中查找。
  2. 如果在未解码的图片缓存中存在对应的数据,则解码,返回Bitmap显示并将其加入到已解码的图片缓存中;如果不存在,则到磁盘缓存中查找。
  3. 如果在磁盘缓存中存在对应的数据,则将数据加入到未解码的图片缓存中,然后解码,返回Bitmap显示并将其加入到已解码的图片缓存中;如果不存在,则进行网络请求或者到本地文件加载。
  4. 请求或加载成功后,将数据加入到磁盘缓存和未解码的图片缓存中,然后解码,返回Bitmap显示并将其加入到已解码的图片缓存中。


    简单整了个示意图,帮助理解下:
    缓存策略示意图

5.2 兼容共享动画

android5.0之后加入了共享动画,
相关的学习地址:
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0201/2394.html
http://jcodecraeer.com/a/opensource/2015/0113/2311.html?1422794518

如果直接结合Fresco和共享动画来实现页面的过渡效果,会发现无效或异常。
Fresco官方也给出了说明,https://www.fresco-cn.org/docs/shared-transitions.html

兼容方法:
1.重写共享动画转换效果的xml文件,注释掉changeImageTransform,并将该文件放于res/transition文件夹下

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <explode/>
    <changeBounds/>
    <changeTransform/>
    <changeClipBounds/>
    <!--<changeImageTransform/>-->
    <!-- Fresco图片框架不支持changeImageTransform变换,
    默认情况是这五个变换都使用,所以需要重写xml并注释掉changeImageTransform -->
</transitionSet>

2.在style文件中使用上一步重写的xml文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AppTheme" parent="AppTheme.Base">
        <!-- 允许使用transitions -->
        <item name="android:windowContentTransitions">true</item>
        <!-- 指定shared element transitions -->
        <item name="android:windowSharedElementEnterTransition">
        @transition/share_element_transition</item>
        <item name="android:windowSharedElementExitTransition">
        @transition/share_element_transition</item>
    </style>
</resources>

5.3 浏览大图

“点击小图浏览大图,并且大图支持缩放。”
这种需求经常能见到,上面提到的SimpleDraweeView并不支持缩放等功能,所以需要重新定制一个控件来显示。
官方给出了一个ZoomableDraweeView来支持该场景,另外也可以参考PhotoDraweeView

5.4 获取网络请求回来的Bitmap

有时候,我们需要拿到网络请求回来的Bitmap对象,那么我们可以这么做:

//加载图片,在ImageListener回调里获取返回的Bitmap
public void getBitmap(Context context, String url, final ImageListener<Bitmap> imageListener) {
    //参考自https://github.com/hpdx/fresco-helper/blob/master/fresco-helper/src/main/java/com/facebook/fresco/helper/ImageLoader.java
    Uri uri = Uri.parse(url);
    ImagePipeline imagePipeline = Fresco.getImagePipeline();
    ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(uri);
    ImageRequest imageRequest = builder.build();
    DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage(imageRequest, context);
    dataSource.subscribe(new BaseDataSubscriber<CloseableReference<CloseableImage>>() {
        @Override
        public void onNewResultImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
            if (!dataSource.isFinished()) {
                return;
            }

            CloseableReference<CloseableImage> imageReference = dataSource.getResult();
            if (imageReference != null) {
                final CloseableReference<CloseableImage> closeableReference = imageReference.clone();
                try {
                    CloseableImage closeableImage = closeableReference.get();
                    //动图处理
                    if (closeableImage instanceof CloseableAnimatedImage) {
                        AnimatedImageResult animatedImageResult = ((CloseableAnimatedImage) closeableImage).getImageResult();
                        if (animatedImageResult != null && animatedImageResult.getImage() != null) {
                            int imageWidth = animatedImageResult.getImage().getWidth();
                            int imageHeight = animatedImageResult.getImage().getHeight();

                            Bitmap.Config bitmapConfig = Bitmap.Config.ARGB_8888;
                            Bitmap bitmap = Bitmap.createBitmap(imageWidth, imageHeight, bitmapConfig);
                            animatedImageResult.getImage().getFrame(0).renderFrame(imageWidth, imageHeight, bitmap);
                            if (imageListener != null) {
                                imageListener.onSuccess(bitmap);
                            }
                        }
                    }
                    //非动图处理
                    else if (closeableImage instanceof CloseableBitmap) {
                        CloseableBitmap closeableBitmap = (CloseableBitmap) closeableImage;
                        Bitmap bitmap = closeableBitmap.getUnderlyingBitmap();
                        if (bitmap != null && !bitmap.isRecycled()) {
                            // https://github.com/facebook/fresco/issues/648
                            final Bitmap tempBitmap = bitmap.copy(bitmap.getConfig(), false);
                            if (imageListener != null) {
                                imageListener.onSuccess(tempBitmap);
                            }
                        }
                    }
                } finally {
                    imageReference.close();
                    closeableReference.close();
                }
            }
        }

        @Override
        public void onFailureImpl(DataSource dataSource) {
            Throwable throwable = dataSource.getFailureCause();
            if (imageListener != null) {
                imageListener.onFail(throwable);
            }
        }
    }, UiThreadImmediateExecutorService.getInstance());
}

或者如果缓存里有数据,可以从缓存中取出然后转为bitmap

FileBinaryResource resource = (FileBinaryResource) Fresco.getImagePipelineFactory().getMainFileCache().getResource(new SimpleCacheKey(url));
if (resource != null && resource.getFile() != null) {
    Bitmap bitmap = BitmapFactory.decodeFile(resource.getFile().getAbsolutePath());
}

5.5 下载图片

下载图片到指定位置,在ImageListener回调里得到下载结果

public void downLoadImage(Context context, String url, final File saveFile, final ImageListener<File> imageListener) {
    //参考自https://github.com/hpdx/fresco-helper/blob/master/fresco-helper/src/main/java/com/facebook/fresco/helper/ImageLoader.java
    Uri uri = Uri.parse(url);
    ImagePipeline imagePipeline = Fresco.getImagePipeline();
    ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(uri);
    ImageRequest imageRequest = builder.build();

    // 获取未解码的图片数据
    DataSource<CloseableReference<PooledByteBuffer>> dataSource = imagePipeline.fetchEncodedImage(imageRequest, context);
    dataSource.subscribe(new BaseDataSubscriber<CloseableReference<PooledByteBuffer>>() {
        @Override
        public void onNewResultImpl(DataSource<CloseableReference<PooledByteBuffer>> dataSource) {
            if (!dataSource.isFinished()) {
                return;
            }

            CloseableReference<PooledByteBuffer> imageReference = dataSource.getResult();
            if (imageReference != null) {

                final CloseableReference<PooledByteBuffer> closeableReference = imageReference.clone();
                try {
                    PooledByteBuffer pooledByteBuffer = closeableReference.get();
                    InputStream inputStream = new PooledByteBufferInputStream(pooledByteBuffer);
                    OutputStream outputStream = new FileOutputStream(saveFile);

                    if (FileUtil.saveFile(inputStream, outputStream) && imageListener != null) {
                        imageListener.onSuccess(saveFile);
                    }
                } catch (Exception e) {
                    if (imageListener != null) {
                        imageListener.onFail(e);
                    }
                    e.printStackTrace();
                } finally {
                    imageReference.close();
                    closeableReference.close();
                }
            }
        }

        @Override
        public void onProgressUpdate(DataSource<CloseableReference<PooledByteBuffer>> dataSource) {
            int progress = (int) (dataSource.getProgress() * 100);
            RingLog.d("fresco下载图片进度:" + progress);
        }

        @Override
        public void onFailureImpl(DataSource dataSource) {
            Throwable throwable = dataSource.getFailureCause();
            if (imageListener != null) {
                imageListener.onFail(throwable);
            }
        }
    }, Executors.newSingleThreadExecutor());
}

demo中已将前面介绍的配置、加载图片的方法等都封装到FrescoManager类中


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350