Android圖片載入框架Fresco使用詳解

Dale_Dawson發表於2019-04-04

官方文件: www.fresco-cn.org/docs/gettin…

前言

Fresco是一個出自Facebook的功能強大的圖片載入庫

優缺點

優點:

1)記憶體自動回收。圖片不可見時,會及時自動釋放所佔用的記憶體,儘可能地避免OOM 2)三級快取機制。兩級記憶體快取(解碼的與未解碼的)+一級磁碟快取,提升載入速度,節省記憶體佔用空間 3)支援各種載入場景。如動圖載入、高斯模糊等常見的圖片載入場景。另外還提供了獨特的漸進式載入、先載入小圖再載入大圖,載入進度等功能(很強大)。

缺點:

1)體積大(很胖)。較其他主流圖片庫體積要大不少 2)侵入性較強。須使用它提供的SimpleDraweeView來代替ImageView載入顯示圖片 綜合來說,如果你的應用對圖片的顯示、載入等要求高的話,那就建議使用Fresco。但如果要求沒那麼高的話就用Glide或其它庫吧。

介紹

下面通過 配置、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="schemas.android.com/apk/res-aut…"

<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: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"/>
複製程式碼

屬性 作用說明

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 設定寬高比

*注意:

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);
}
複製程式碼

載入網路圖片,先載入小圖,待大圖載入完成後再替換掉小圖

這個需要修改一下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檔案中新增以下內容進行混淆配置

-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);
}
複製程式碼

5. 其他

5.1 快取策略 Fresco採用三級快取機制,兩級記憶體快取+一級磁碟快取,其中兩級記憶體快取分為已解碼的圖片快取(Bitmap快取)和未解碼的圖片快取。 下面通過載入流程來了解其快取策略。 1. 根據Uri到已解碼的圖片快取中查詢是否存在對應的Bitmap。如果存在,則返回Bitmap顯示; 如果不存在,則到未解碼的圖片快取中查詢。 2. 如果在未解碼的圖片快取中存在對應的資料,則解碼,返回Bitmap顯示並將其加入到已解碼的圖片快取中;如果不存在,則到磁碟快取中查詢。 3. 如果在磁碟快取中存在對應的資料,則將資料加入到未解碼的圖片快取中,然後解碼,返回Bitmap顯示並將其加入到已解碼的圖片快取中;如果不存在,則進行網路請求或者到本地檔案載入。 4. 請求或載入成功後,將資料加入到磁碟快取和未解碼的圖片快取中,然後解碼,返回Bitmap顯示並將其加入到已解碼的圖片快取中。 簡單整了個示意圖,幫助理解下:

快取策略示意圖.png

5.2 相容共享動畫 android5.0之後加入了共享動畫, 如果直接結合Fresco和共享動畫來實現頁面的過渡效果,會發現無效或異常。 Fresco官方也給出了說明,www.fresco-cn.org/docs/shared… 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) {
        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()) {
                                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) {
        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());
    }
複製程式碼

相關文章