Fresco:我很胖嚎于,可是我很強(qiáng)大

安卓基礎(chǔ)開(kāi)發(fā)庫(kù)作谚,讓開(kāi)發(fā)簡(jiǎn)單點(diǎn)型型。
DevRing & Demo地址https://github.com/LJYcoder/DevRing

學(xué)習(xí)/參考地址:
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是一個(gè)出自Facebook的功能強(qiáng)大的圖片加載庫(kù)

優(yōu)點(diǎn):
1)內(nèi)存自動(dòng)回收。圖片不可見(jiàn)時(shí)犹菇,會(huì)及時(shí)自動(dòng)釋放所占用的內(nèi)存德迹,盡可能地避免OOM
2)三級(jí)緩存機(jī)制。兩級(jí)內(nèi)存緩存(解碼的與未解碼的)+一級(jí)磁盤緩存揭芍,提升加載速度胳搞,節(jié)省內(nèi)存占用空間
3)支持各種加載場(chǎng)景。如動(dòng)圖加載称杨、高斯模糊等常見(jiàn)的圖片加載場(chǎng)景肌毅。另外還提供了獨(dú)特的漸進(jìn)式加載先加載小圖再加載大圖姑原,加載進(jìn)度等功能(很強(qiáng)大)悬而。

缺點(diǎn):
1)體積大(很胖)。較其他主流圖片庫(kù)體積要大不少
2)侵入性較強(qiáng)锭汛。須使用它提供的SimpleDraweeView來(lái)代替ImageView加載顯示圖片

綜合來(lái)說(shuō)笨奠,如果你的應(yīng)用對(duì)圖片的顯示袭蝗、加載等要求高的話,那就建議使用Fresco艰躺。但如果要求沒(méi)那么高的話就用Glide或其它庫(kù)吧呻袭。
關(guān)于Fresco與Glide的對(duì)比可以參考http://www.reibang.com/p/6729dc17586b


介紹

下面通過(guò) 配置、SimpleDraweeView腺兴、加載圖片、混淆廉侧、其他 這幾個(gè)部分來(lái)介紹页响。

1. 配置

1.1 添加依賴

compile 'com.facebook.fresco:fresco:1.5.0'
compile 'com.facebook.fresco:animated-gif:1.5.0'//加載gif動(dòng)圖需添加此庫(kù)
compile 'com.facebook.fresco:animated-webp:1.5.0'//加載webp動(dòng)圖需添加此庫(kù)
compile 'com.facebook.fresco:webpsupport:1.5.0'//支持webp需添加此庫(kù)
compile 'com.facebook.fresco:imagepipeline-okhttp3:1.5.0'//網(wǎng)絡(luò)實(shí)現(xiàn)層使用okhttp3需添加此庫(kù)
compile 'jp.wasabeef:fresco-processors:2.1.0@aar'//用于提供fresco的各種圖片變換

1.2 設(shè)置磁盤緩存

ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setMainDiskCacheConfig(DiskCacheConfig.newBuilder(context)
.setBaseDirectoryPath(context.getExternalCacheDir())//設(shè)置磁盤緩存的路徑
.setBaseDirectoryName(BaseConstants.APP_IMAGE)//設(shè)置磁盤緩存文件夾的名稱
.setMaxCacheSize(MAX_DISK_CACHE_SIZE)//設(shè)置磁盤緩存的大小
.build());

1.3 設(shè)置內(nèi)存緩存

設(shè)置已解碼的內(nèi)存緩存(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;//取手機(jī)內(nèi)存最大值的五分之一作為可用的最大內(nèi)存數(shù)

           MemoryCacheParams bitmapCacheParams = new MemoryCacheParams( //
                   // 可用最大內(nèi)存數(shù),以字節(jié)為單位
                   MAX_MEMORY_CACHE_SIZE,
                   // 內(nèi)存中允許的最多圖片數(shù)量
                   Integer.MAX_VALUE,
                   // 內(nèi)存中準(zhǔn)備清理但是尚未刪除的總圖片所可用的最大內(nèi)存數(shù)段誊,以字節(jié)為單位
                   MAX_MEMORY_CACHE_SIZE,
                   // 內(nèi)存中準(zhǔn)備清除的圖片最大數(shù)量
                   Integer.MAX_VALUE,
                   // 內(nèi)存中單圖片的最大大小
                   Integer.MAX_VALUE);
           return bitmapCacheParams;
       }
   });

設(shè)置未解碼的內(nèi)存緩存

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

           MemoryCacheParams bitmapCacheParams;
           //設(shè)置大小闰蚕,可參考上面已解碼的內(nèi)存緩存
           return bitmapCacheParams;
       }
   });

1.4 設(shè)置內(nèi)存緊張時(shí)的應(yīng)對(duì)措施

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) { 
                 //清空內(nèi)存緩存
                 ImagePipelineFactory.getInstance().getImagePipeline().clearMemoryCaches();
         }
     }
 });
 
ImagePipelineConfig.Builder imagePipelineConfigBuilder = ImagePipelineConfig.newBuilder(context);
imagePipelineConfigBuilder.setMemoryTrimmableRegistry(memoryTrimmableRegistry);

1.5 設(shè)置漸進(jìn)式顯示的效果

ProgressiveJpegConfig progressiveJpegConfig = new ProgressiveJpegConfig() {
      @Override
      public int getNextScanNumberToDecode(int scanNumber) {
          //返回下一個(gè)需要解碼的掃描次數(shù)
          return scanNumber + 2;
      }    
    
      public QualityInfo getQualityInfo(int scanNumber) {
          boolean isGoodEnough = (scanNumber >= 5);
          //確定多少個(gè)掃描次數(shù)之后的圖片才能開(kāi)始顯示。
          return ImmutableQualityInfo.of(scanNumber, isGoodEnough, false);
      }
};
//具體含義可參考 http://wiki.jikexueyuan.com/project/fresco/progressive-jpegs.html

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

設(shè)置完效果后连舍,還需在下面介紹的ImageRequest中開(kāi)啟漸進(jìn)式加載没陡。

1.6 允許解碼時(shí)調(diào)整圖片大小

允許后,即可在后面介紹的ImageRequest中對(duì)結(jié)合ResizeOptions對(duì)解碼后的圖片大小進(jìn)行調(diào)整索赏,從而優(yōu)化了圖片所占大小盼玄。默認(rèn)只支持JPEG圖,所以要設(shè)置該屬性來(lái)支持png潜腻、jpg埃儿、webp。

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

1.7 開(kāi)啟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 初始化

上面的各種配置都是通過(guò)ImagePipelineConfig進(jìn)行的融涣,接著需要進(jìn)行初始化童番,在Application中初始化即可

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

//...進(jìn)行各種設(shè)置

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

如果想直接使用默認(rèn)的配置,可以

Fresco.initialize(context);

2. SimpleDraweeView

Fresco要求使用SimpleDraweeView來(lái)替換ImageView進(jìn)行圖片的加載與顯示威鹿,不少人也是因?yàn)檫@一點(diǎn)而不想使用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"/>

上面各個(gè)屬性的作用說(shuō)明:(來(lái)自http://www.reibang.com/p/8ff81be83101

屬性 作用說(shuō)明
actualImageScaleType 加載完成的圖片的縮放樣式
fadeDuration 由進(jìn)度條和占位符圖片漸變過(guò)渡到加載完成的圖片所使用的時(shí)間間隔
failureImage 加載失敗所使用的圖片
failureImageScaleType 加載失敗所使用的圖片的縮放樣式
placeholderImage 占位符圖片
placeholderImageScaleType 占位符圖片的縮放樣式
progressBarAutoRotateInterval 旋轉(zhuǎn)進(jìn)度條旋轉(zhuǎn)1圈所需要的時(shí)間
progressBarImage 旋轉(zhuǎn)進(jìn)度條所使用的圖片
progressBarImageScaleType 旋轉(zhuǎn)進(jìn)度條所使用的圖片的縮放樣式
retryImage 重試所使用的圖片
retryImageScaleType 重試所使用的圖片的縮放樣式
backgroundImage 背景圖片
overlayImage 覆蓋在加載完成后圖片上的疊加圖片
pressedStateOverlayImage 按壓狀態(tài)下的疊加圖片
roundAsCircle 是否將圖片剪切為圓形
roundedCornerRadius 圓角圖片時(shí)候,圓角的半徑大小
roundTopLeft 左上角是否為圓角
roundTopRight 右上角是否為圓角
roundBottomLeft 左下角是否為圓角
roundBottomRight 右下角是否為圓角
roundWithOverlayColor 圓角或圓形圖疊加的顏色忽你,只能是顏色
roundingBorderWidth 圓角或圓形圖邊框的寬度
roundingBorderColor 圓角或圓形圖邊框的顏色
viewAspectRatio 設(shè)置寬高比

各個(gè)屬性的效果圖幼东,可到http://blog.csdn.net/wyb112233/article/details/49637685查看,我就不重復(fù)造輪子了檀夹。
*注意:
1)android:src屬性對(duì)于SimpleDraweeView無(wú)效筋粗,必要的話可用fresco:placeholderImage來(lái)設(shè)置。
2)SimpleDraweeView不支持android:layout_width和android:layout_height同時(shí)都設(shè)為wrap_content炸渡。

3. 加載圖片

使用Fresco加載圖片娜亿,大致是按以下流程進(jìn)行的。

  1. 設(shè)置Hierarchay(上面xml中的屬性以及顯示加載進(jìn)度條等蚌堵,可在這進(jìn)行設(shè)置)
  2. 構(gòu)建ImageRequest(加載路徑买决、開(kāi)啟漸進(jìn)式加載沛婴、圖片變換、調(diào)整解碼圖片大小等督赤,可在這進(jìn)行設(shè)置)
  3. 構(gòu)建DraweeController(動(dòng)圖加載嘁灯、失敗后點(diǎn)擊重新加載等,可在這進(jìn)行設(shè)置)
  4. 進(jìn)行圖片加載

3.1 設(shè)置Hierarchay

雖然xml中的屬性都能在這一步通過(guò)代碼進(jìn)行設(shè)置躲舌,但一般只在這設(shè)置一些統(tǒng)一固定的屬性丑婿,比如加載占位圖、加載失敗圖等没卸。 另外羹奉,顯示圖片的加載進(jìn)度也是在這里設(shè)置。

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

//對(duì)Hierarchy進(jìn)行設(shè)置约计,如各種狀態(tài)下顯示的圖片
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);
        //設(shè)置加載成功后圖片的縮放模式
        hierarchy.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_CROP);

        //顯示加載進(jìn)度條诀拭,使用自帶的new ProgressBarDrawable()
        //默認(rèn)會(huì)顯示在圖片的底部,可以設(shè)置進(jìn)度條的顏色煤蚌。
        hierarchy.setProgressBarImage(new ProgressBarDrawable());
        //設(shè)置圖片加載為圓形
        hierarchy.setRoundingParams(RoundingParams.asCircle());
        //設(shè)置圖片加載為圓角耕挨,并可設(shè)置圓角大小
        hierarchy.setRoundingParams(RoundingParams.fromCornersRadius(radius));

        //其他設(shè)置請(qǐng)查看具體API。
        
    }
}

3.2 構(gòu)建ImageRequest

/**
 * 構(gòu)建尉桩、獲取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();
    }

    //根據(jù)請(qǐng)求路徑生成ImageRequest的構(gòu)造者
    ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(uri);
    //調(diào)整解碼圖片的大小
    if (width > 0 && height > 0) {
        builder.setResizeOptions(new ResizeOptions(width, height));
    }
    //設(shè)置是否開(kāi)啟漸進(jìn)式加載筒占,僅支持JPEG圖片
    builder.setProgressiveRenderingEnabled(true);

    //圖片變換處理
    CombinePostProcessors.Builder processorBuilder = new CombinePostProcessors.Builder();
    //加入模糊變換
    processorBuilder.add(new BlurPostprocessor(context, radius));
    //加入灰白變換
    processorBuilder.add(new GrayscalePostprocessor());
    //應(yīng)用加入的變換   
    builder.setPostprocessor(processorBuilder.build());
    //更多圖片變換請(qǐng)查看https://github.com/wasabeef/fresco-processors  
    return builder.build();
}

3.3 構(gòu)建DraweeController

/**
* 構(gòu)建、獲取Controller
* @param request
* @param oldController
* @return
*/
public DraweeController getController(ImageRequest request, @Nullable DraweeController oldController) {

   PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder();
   builder.setImageRequest(request);//設(shè)置圖片請(qǐng)求
   builder.setTapToRetryEnabled(false);//設(shè)置是否允許加載失敗時(shí)點(diǎn)擊再次加載
   builder.setAutoPlayAnimations(true);//設(shè)置是否允許動(dòng)畫圖自動(dòng)播放
   builder.setOldController(oldController);
   return builder.build();
}

3.4 進(jìn)行圖片加載

創(chuàng)建一個(gè)loadImage方法將上面的Hierarchy魄健、ImageRequest赋铝、DraweeController串在一起,供具體的加載場(chǎng)景使用

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

*/
public void loadImage(SimpleDraweeView simpleDraweeView, Uri uri) {
  //設(shè)置Hierarchy
   setHierarchay(simpleDraweeView.getHierarchy());
   //構(gòu)建并獲取ImageRequest
   ImageRequest imageRequest = getImageRequest(uri, simpleDraweeView);
   //構(gòu)建并獲取Controller
   DraweeController draweeController = getController(imageRequest, simpleDraweeView.getController());
   //開(kāi)始加載
   simpleDraweeView.setController(draweeController);
}

具體的加載場(chǎng)景:

  • 加載網(wǎng)絡(luò)圖片沽瘦,包括gif/webp動(dòng)圖
 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);
}
  • 加載網(wǎng)絡(luò)圖片革骨,先加載小圖,待大圖加載完成后再替換掉小圖

這個(gè)需要修改一下DraweeController的構(gòu)建析恋,通過(guò)setLowResImageRequest來(lái)添加小圖請(qǐng)求

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

    PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder();
    builder.setLowResImageRequest(smallRequest);//小圖的圖片請(qǐng)求
    builder.setImageRequest(bigRequest);//大圖的圖片請(qǐng)求
    builder.setTapToRetryEnabled(false);//設(shè)置是否允許加載失敗時(shí)點(diǎn)擊再次加載
    builder.setAutoPlayAnimations(true);//設(shè)置是否允許動(dòng)畫圖自動(dòng)播放
    builder.setOldController(oldController);
    return builder.build();
}
public void loadImageSmallToBig(SimpleDraweeView simpleDraweeView, Uri smallUri, Uri bigUri) {
    //設(shè)置Hierarchy
    setHierarchay(simpleDraweeView.getHierarchy());
    //構(gòu)建小圖的圖片請(qǐng)求
    ImageRequest smallRequest = getImageRequest(smallUri, simpleDraweeView);
    //構(gòu)建大圖的圖片請(qǐng)求
    ImageRequest bigRequest = getImageRequest(bigUri, simpleDraweeView);
    //構(gòu)建Controller
    DraweeController draweeController = getSmallToBigController(smallRequest, bigRequest, simpleDraweeView.getController());
    //開(kāi)始加載
    simpleDraweeView.setController(draweeController);
}
//加載網(wǎng)絡(luò)圖片良哲,先加載小圖,待大圖加載完成后替換
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文件中添加以下內(nèi)容進(jìn)行混淆配置

#fresco開(kāi)始
-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結(jié)束

5. 其他

5.1 緩存策略

Fresco采用三級(jí)緩存機(jī)制助隧,兩級(jí)內(nèi)存緩存+一級(jí)磁盤緩存筑凫,其中兩級(jí)內(nèi)存緩存分為已解碼的圖片緩存(Bitmap緩存)和未解碼的圖片緩存。
下面通過(guò)加載流程來(lái)了解其緩存策略并村。

  1. 根據(jù)Uri到已解碼的圖片緩存中查找是否存在對(duì)應(yīng)的Bitmap巍实。如果存在,則返回Bitmap顯示哩牍;
    如果不存在棚潦,則到未解碼的圖片緩存中查找。
  2. 如果在未解碼的圖片緩存中存在對(duì)應(yīng)的數(shù)據(jù)膝昆,則解碼丸边,返回Bitmap顯示并將其加入到已解碼的圖片緩存中叠必;如果不存在,則到磁盤緩存中查找妹窖。
  3. 如果在磁盤緩存中存在對(duì)應(yīng)的數(shù)據(jù)纬朝,則將數(shù)據(jù)加入到未解碼的圖片緩存中,然后解碼骄呼,返回Bitmap顯示并將其加入到已解碼的圖片緩存中共苛;如果不存在,則進(jìn)行網(wǎng)絡(luò)請(qǐng)求或者到本地文件加載谒麦。
  4. 請(qǐng)求或加載成功后俄讹,將數(shù)據(jù)加入到磁盤緩存和未解碼的圖片緩存中,然后解碼绕德,返回Bitmap顯示并將其加入到已解碼的圖片緩存中。


    簡(jiǎn)單整了個(gè)示意圖摊阀,幫助理解下:
    緩存策略示意圖

5.2 兼容共享動(dòng)畫

android5.0之后加入了共享動(dòng)畫耻蛇,
相關(guān)的學(xué)習(xí)地址:
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0201/2394.html
http://jcodecraeer.com/a/opensource/2015/0113/2311.html?1422794518

如果直接結(jié)合Fresco和共享動(dòng)畫來(lái)實(shí)現(xiàn)頁(yè)面的過(guò)渡效果,會(huì)發(fā)現(xiàn)無(wú)效或異常胞此。
Fresco官方也給出了說(shuō)明臣咖,https://www.fresco-cn.org/docs/shared-transitions.html

兼容方法:
1.重寫共享動(dòng)畫轉(zhuǎn)換效果的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變換夺蛇,
    默認(rèn)情況是這五個(gè)變換都使用,所以需要重寫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 瀏覽大圖

“點(diǎn)擊小圖瀏覽大圖酣胀,并且大圖支持縮放刁赦。”
這種需求經(jīng)常能見(jiàn)到闻镶,上面提到的SimpleDraweeView并不支持縮放等功能甚脉,所以需要重新定制一個(gè)控件來(lái)顯示。
官方給出了一個(gè)ZoomableDraweeView來(lái)支持該場(chǎng)景铆农,另外也可以參考PhotoDraweeView

5.4 獲取網(wǎng)絡(luò)請(qǐng)求回來(lái)的Bitmap

有時(shí)候牺氨,我們需要拿到網(wǎng)絡(luò)請(qǐng)求回來(lái)的Bitmap對(duì)象,那么我們可以這么做:

//加載圖片墩剖,在ImageListener回調(diào)里獲取返回的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();
                    //動(dòng)圖處理
                    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);
                            }
                        }
                    }
                    //非動(dòng)圖處理
                    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());
}

或者如果緩存里有數(shù)據(jù)猴凹,可以從緩存中取出然后轉(zhuǎn)為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回調(diào)里得到下載結(jié)果

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

    // 獲取未解碼的圖片數(shù)據(jù)
    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下載圖片進(jìn)度:" + progress);
        }

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

demo中已將前面介紹的配置岭皂、加載圖片的方法等都封裝到FrescoManager類中


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末郊霎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蒲障,更是在濱河造成了極大的恐慌歹篓,老刑警劉巖瘫证,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異庄撮,居然都是意外死亡背捌,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門洞斯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)毡庆,“玉大人,你說(shuō)我怎么就攤上這事烙如∶纯梗” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵亚铁,是天一觀的道長(zhǎng)蝇刀。 經(jīng)常有香客問(wèn)我,道長(zhǎng)徘溢,這世上最難降的妖魔是什么吞琐? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮然爆,結(jié)果婚禮上站粟,老公的妹妹穿的比我還像新娘。我一直安慰自己曾雕,他們只是感情好奴烙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著剖张,像睡著了一般切诀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上修械,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天趾牧,我揣著相機(jī)與錄音,去河邊找鬼肯污。 笑死翘单,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蹦渣。 我是一名探鬼主播哄芜,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼柬唯!你這毒婦竟也來(lái)了认臊?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤锄奢,失蹤者是張志新(化名)和其女友劉穎失晴,沒(méi)想到半個(gè)月后剧腻,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涂屁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年书在,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拆又。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡儒旬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出帖族,到底是詐尸還是另有隱情栈源,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布竖般,位于F島的核電站甚垦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏涣雕。R本人自食惡果不足惜耙蔑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一宇植、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧符衔,春花似錦男杈、人聲如沸丈屹。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)旺垒。三九已至,卻和暖如春肤无,著一層夾襖步出監(jiān)牢的瞬間先蒋,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工宛渐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留竞漾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓窥翩,卻偏偏與公主長(zhǎng)得像业岁,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子寇蚊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353