暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

facebook

yBmZlQzJ 2024-01-18
198

前言

Fresco

是一个强大的系统用于在

Android

应用中展示图片,它能够从网络、本地存储和本地资源中加载图片。它拥有三级缓存

,Fresco

在显示方面是用了

Drawees

,可以显示占位符,直到

图片加载完成。

本教程内容来源于

fresco

中文网

英文官网

Fresco

http://frescolib.org/

更新日期

更新内容

2015-04-01

Fresco

中文版发布

Fresco

是一个强大的系统用于在

Android

应用中展示图片,它能够从网络、本地存储和本地资源中加载图片。它拥有三级缓存,

Fresco

在显示方面是用了

Drawees

,可以显示占位符,

直到图片加载完成。

Fresco

是一个强大的图片加载组件。

Fresco

中设计有一个叫做

image pipeline

的模块。它负责从网络,从本地文件系统,本地资源加载图片。为了最大限度节省空间和

CPU

时间,它含有

3

级缓存设计(

2

级内存,

1

级文

件)。

Fresco

中设计有一个叫做

Drawees

模块,方便地显示

loading

图,当图片不再显示在屏幕上时,及时地释放内存和空间占用。

Fresco

支持

Android2.3(API level 9)

及其以上系统。

特性

内存管理

一个没有未压缩的图片,即

Android

中的

Bitmap

,占用大量的内存。大的内存占用势必引发更加频繁的

GC

。在

5.0

以下,

GC

将会显著地引发界面卡顿。

5.0

以下系统,

Fresco

将图片放到一个特别的内存区域。当然,在图片不显示的时候,占用的内存会自动被释放。这会使得

APP

更加流畅,减少因图片内存占用而引发的

OOM

Fresco

在低端机器上表现一样出色,你再也不用因图片内存占用而思前想后。

图片的渐进式呈现

渐进式的

JPEG

图片格式已经流行数年了,渐进式图片格式先呈现大致的图片轮廓,然后随着图片下载的继续,呈现逐渐清晰的图片,这对于移动设备,尤其是慢网络有极大的利好,

可带来更好的用户体验。

Android

本身的图片库不支持此格式,但是

Fresco

支持。使用时,和往常一样,仅仅需要提供一个图片的

URI

即可,剩下的事情,

Fresco

会处理。

Gif

图和

WebP

格式

是的,支持加载

Gif

图,支持

WebP

格式。

图像的呈现

Fresco

Drawees

设计,带来一些有用的特性:

自定义居中焦点

(

对人脸等图片显示非常有帮助

)

圆角图,当然圆圈也行。

下载失败之后,点击重现下载

自定义占位图,自定义

overlay,

或者进度条

指定用户按压时的

overlay

图像的加载

Fresco

image pipeline

设计,允许用户在多方面控制图片的加载:

为同一个图片指定不同的远程路径,或者使用已经存在本地缓存中的图片

先显示一个低解析度的图片,等高清图下载完之后再显示高清图

加载完成回调通知

对于本地图,如有

EXIF

缩略图,在大图加载完成之前,可先显示缩略图

缩放或者旋转图片

处理已下载的图片

WebP

支持

>

本教程内容来源于:

http://fresco-cn.org

Fresco

官网:

http://frescolib.org/

更新日期

更新内容

2015-04-08

Fresco

中文版发布

1

配置和使用

下载

Fresco

类库发布到了

Maven

中央库

:

Gradle:

groovy

dependencies {

compile 'com.facebook.fresco:fresco:0.1.0+'

}

Maven:

xml

<dependency>

<groupId>com.facebook.fresco</groupId>

<artifactId>fresco</artifactId>

<version>LATEST</version>

</dependency>

Eclipse

呵呵

配置和开始使用

如果你仅仅是想简单下载一张网络图片,在下载完成之前,显示一张占位图,那么简单使用

SimpleDraweeView

即可。

Application

初始化时

:

java

Fresco.initialize(context);

xml

布局文件中

,

加入命名空间

:

xml

<!--

其他元素

-->

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:fresco="http://schemas.android.com/apk/res-auto">

加入

SimpleDraweeView

:

xml

<com.facebook.drawee.view.SimpleDraweeView

android:id="@+id/my_image_view"

android:layout_width="20dp"

android:layout_height="20dp"

fresco:placeholderImage="@drawable/my_drawable"

/>

开始加载图片

draweeView.

setImageURI

(

"http://site.com/uri"

)

;

剩下的,

Fresco

会替你完成

:

显示占位图直到加载完成;

下载图片;

缓存图片;

图片不再显示时,从内存中移除;

等等等等。

关键概念

Drawees

Drawees

负责图片的呈现,包含几个组件,有点像

MVC

模式。

DraweeView

继承于

View

,

负责图片的显示。

一般情况下,使用

SimpleDraweeView

即可

.

简单的用法,在这个页面:

开始使用

它支持很多自定义效果,参见这里

:

自定义显示效果

.

DraweeHierarchy

继承于

Drawable,

包含用于绘制的图像数据。

MVC

中的

M

如果你想在

Java

代码中自定义图片的展示,可以通过这类实现,具体的请参考这里

:

Java

代码中自定义显示效果

DraweeController

DraweeController

负责和

image loader

交互(默认是

Fresco

image pipeline

),可以创建一个这个类的实例,来实现对所要显示的图片做更多的控制。

DraweeControllerBuilder

DraweeControllers

DraweeControllerBuilder

采用

Builder

模式创建,创建之后,不可修改。具体参见

:

使用

ControllerBuilder

Listeners

使用

ControllerListener

的一个场景就是设置一个

Listener

监听图片的下载。

Image Pipeline

Fresco

Image Pipeline

负责图片的获取和管理。图片可以来自远程服务器,本地文件,或者

Content Provider

,本地资源。压缩后的文件缓存在本地存储中,

Bitmap

数据缓存在内存

中。

5.0

系统之后,

Image Pipeline

使用

`pinned purgeables*

Bitmap

数据存在

native

内存中。这要求图片不使用时,要显示地释放内存。

SimpleDraweeView

自动处理了这个释放过程,所以没有特殊情况,尽量使用

SimpleDraweeView

,在特殊的场合,如果有需要,也可以直接控制

Image Pipeline

支持的

URIs

Fresco

支持许多

URI

格式。

特别注意:

Fresco

不支持

相对路径的

URI.

所有的

URI

都必须是绝对路径,并且带上该

URI

scheme

如下:

类型

Scheme

示例

远程图片

http://,

https://

HttpURLConnection

或者参考

使用其他网络加载方案

本地文件

file://

FileInputStream

Content provider

content://

ContentResolver

asset

目录下的资源

asset://

AssetManager

res

目录下的资源

res://

Resources.openRawResource

注意,只有图片资源才能使用在

Image pipeline

中,比如

(PNG)

。其他资源类型,比如字符串,或者

XML Drawable

Image pipeline

中没有意义。所以加载的资源不支持这些类型。

ShapeDrawable

这样声明在

XML

中的

drawable

可能引起困惑。注意到这毕竟不是图片,如果想把这样的

drawable

作为图像显示。

那么把这个

drawable

设置为占位图,然后把

URI

设置为

null

2

DRAWEE

指南

XML

中使用

Drawees

Drawees

具有极大的可定制性。

下面的例子给出了可以配置的各种选项:

xml

<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"

/>

必须设置

layout_width

layout_height

如果没有在

XML

中声明这两个属性,将无法正确加载图像。

wrap_content

Drawees

不支持

wrap_content

属性。

所下载的图像可能和占位图尺寸不一致,如果设置出错图或者重试图的话,这些图的尺寸也可能和所下载的图尺寸不一致。

如果大小不一致,图像下载完之后,假设如果是

wrap_content

View

将会重新

layout

,改变大小和位置。这将会导致界面跳跃。

固定宽高比

只有希望显示的固定宽高比时,可以使用

wrap_content

如果希望显示的图片保持一定宽高比例,如果

4:3

,则在

XML

:

xml

<com.facebook.drawee.view.SimpleDraweeView

android:id="@+id/my_image_view"

android:layout_width="20dp"

android:layout_height="wrap_content"

<!-- other attributes -->

然后在代码中指定显示比例:

java

mSimpleDraweeView.setAspectRatio(1.33f);

JAVA

代码中使用

Drawees

设置或更改要显示的图片

mSimpleDraweeView.

setImageURI

(

uri

)

;

如果要更加复杂的配置,可使用

ControllerBuilder

;

自定义显示图

一般情况下,在

XML

设置显示效果即可

,

如果想更多定制化,可以这样

:

创建一个

builder

然后设置给

DraweeView:

java

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

对于同一个

View

,请不要多次调用

setHierarchy

,即使这个

View

是可回收的。创建

DraweeHierarchy

的较为耗时的一个过程,应该多次利用。

如果要改变所要显示的图片可使用

setController

或者

setImageURI

修改

DraweeHierarchy

DraweeHierarchy

的一些属性可以在运行时改变。

要改变这些属性,首先获取一个引用

:

java

GenericDraweeHierarchy hierarchy = mSimpleDraweeView.getHierarchy();

修改占位图

修改占位图为资源

id:

java

hierarchy.setPlaceholderImage(R.drawable.placeholderId);

或者修改为一个

Drawable

:

java

Drawable drawable;

//

创建一个

drawable

hierarchy.setPlaceholderImage(drawable);

修改显示的图像

修改

缩放类型

:

java

hierarchy.setActualImageScaleType(ScalingUtils.ScaleType.CENTER_INSIDE);

当然,如果修改为

focusCrop,

需要指定一个居中点

:

java

hierarchy.setActualImageFocusPoint(point);

或者设置一个

color filter:

java

ColorFilter filter;

//

创建

filter

hierarchy.setActualImageColorFilter(filter);

圆角

All of the

rounding related params

, except the rounding method, can be modified. You get a

RoundingParams

object from the hierarchy, modify it, and set it back again:

除了圆角显示方式(原来为圆角的不能修改为圆圈,反之亦然),其他圆角相关的呈现参数

,

具体参见这里

是可以动态修改的。

如下

:

获取

DraweeHierarchy

的圆角显示参数,修改圆角半径为

10

java

RoundingParams roundingParams = hierarchy.getRoundingParams();

roundingParams.setCornersRadius(10);

hierarchy.setRoundingParams(roundingParams);

Drawee

的各种效果配置

内容导航

定义

设置要加载的图片

占位图

加载失败时的占位图

点击重新加载

显示一个进度条

Backgrounds

Overlays

Pressed State Overlay

定义

本页说明如何设置实现不同的图片呈现效果。

除了要加载的图片,其他各个设置都可以在

xml

中指定。在

xml

中指定的时候,可以是

drawable/

下的资源,也可以颜色。

Java

代码中也可以指定。如果需要

通过程序设定

的话会接触到这个类

: GenericDraweeHierarchyBuilder

通过代码设置是,设置的值可以是资源

id

,也可以是

Drawable

的子类。

创建完

GenericDraweeHierarchy

之后,也可以通过该类的相关方法,重新设置一些效果。

大多数的用户呈现不同效果的

drawables

都是可以

缩放的

.

设置要加载的图

除了需要加载的图片是真正必须的,其他的都是可选的。如前所述,图片可以来自多个地方。

所需加载的图片实际是

DraweeController

的一个属性,而不是

DraweeHierarchy

的属性。

可使用

setImageURI

方法或者通过设置

DraweeController

来进行设置。

对于要加载的图片,除了可以设置缩放类型外,

DraweeHierarchy

还公开出一些其他方法用来控制显示效果

:

focus point (

居中焦点

,

用于

focusCrop

缩放模式

)

color filter

默认的缩放类型是

:

centerCrop

占位图

(Placeholder)

在调用

setController

或者

setImageURI

之后,占位图开始显示,直到图片加载完成。

对于渐进式格式的

JPEG

图片,占位图会显示直到满足已加载的图片解析度到达设定值。

XML

中属性值

:

placeholderImage

Hierarchy builder

中的方法

:

setPlaceholderImage

Hierarchy method:

setPlaceholderImage

默认值

: a transparent

ColorDrawable

默认缩放类型

:

centerInside

设置加载失败占位图

如果

URI

是无效的,或者下载过程中网络不可用,将会导致加载失败。当加载图片出错时,你可以设置一个出错提示图片。

XML

中属性值

:

failureImage

Hierarchy builder

中的方法

:

setFailureImage

默认值

: The placeholder image

默认缩放类型

:

centerInside

点击重新加载图

在加载失败时,可以设置点击重新加载。这时提供一个图片,加载失败时,会显示这个图片(而不是失败提示图片),提示用户点击重试。

ControllerBuilder

中如下设置

:

.

setTapToRetryEnabled

(

true

)

加载失败时,

image pipeline

会重试四次;如果还是加载失败,则显示加载失败提示图片。

XML

中属性值

:

retryImage

Hierarchy builder

中的方法

:

setRetryImage

默认值

: The placeholder image

默认缩放类型

:

centerInside

显示一个进度条

设置一个进度条图片,提示用户正在加载。目前,进度条仅仅是提示正在

loading

,和加载进度无关。

XML

中属性值

:

progressBarImage

Hierarchy builder

中的方法

:

setProgressBarImage

默认值

: None

默认缩放类型

:

centerInside

背景

背景图会最先绘制,在

XML

中只可以指定一个背景图,但是在

JAVA

代码中,可以指定多个背景图。

当指定一个背景图列表的时候,列表中的第一项会被首先绘制,绘制在最下层,然后依次往上绘制。

背景图片不支持缩放类型,会被强制到

Drawee

尺寸大小。

XML

中属性值

:

backgroundImage

Hierarchy builder

中的方法

:

setBackground,

setBackgrounds

默认值

: None

默认缩放类型

: N/A

设置叠加图

(Overlay)

叠加图会最后被绘制。

和背景图一样,

XML

中只可以指定一个,如果想指定多个,可以通过

JAVA

代码实现。

当指定的叠加图是一个列表的时候,列表第一个元素会被先绘制,最后一个元素最后被绘制到最上层。

同样的,不支持各种缩放类型。

XML

中属性值

:

overlayImage

Hierarchy builder

中的方法

:

setOverlay,

setOverlays

默认值

: None

默认缩放类型

: N/A

设置按压状态下的叠加图

同样不支持缩放,用户按压

DraweeView

时呈现。

XML

中属性值

:

pressedStateOverlayImage

Hierarchy builder

中的方法

:

setPressedStateOverlay

默认值

: None

默认缩放类型

: N/A

缩放

对于

Drawee

各种效果配置

,其中一些是支持缩放类型的。

可用的缩放类型

类型

描述

center

居中,无缩放

centerCrop

保持宽高比缩小或放大,使得两边都大于或等于显示边界。居中显示。

focusCrop

centerCrop,

但居中点不是中点,而是指定的某个点

centerInside

使两边都在显示边界内,居中显示。

如果图尺寸大于显示边界,则保持长宽比缩小图片。

fitCenter

保持宽高比,缩小或者放大,使得图片完全显示在显示边界内。居中显示

fitStart

同上。但不居中,和显示边界左上对齐

fitEnd

fitCenter

但不居中,和显示边界右下对齐

fitXY

不保存宽高比,填充满显示边界

none

如要使用

tile mode

显示

,

需要设置为

none

这些缩放类型和

Android

ImageView

支持的缩放类型几乎一样

.

唯一不支持的缩放类型是

matrix.

Fresco

提供了

focusCrop

作为补充。通常这个缩放效果更佳。

focusCrop

centerCrop

缩放模式会保持长宽比,缩放图片,填充满显示边界,居中显示。这个缩放模式在通常情况下很有用。

但是对于人脸等图片时,一味地居中显示,这个模式可能会裁剪掉一些有用的信息。

以人脸图片为例,借助一些类库,我们可以识别出人脸所在位置。如果可以设置以人脸位置居中裁剪显示,那么效果会好很多。

Fresco

focusCrop

缩放模式正是为此而设计。只要提供一个居中聚焦点,显示时就会

尽量

以此点为中心。

居中点是以相对方式给出的,比如

(0.5f, 0.5f)

就是居中显示,

(0f, 0f)

就是左上对齐显示。

如果要使用此缩放模式,首先指定缩放模式。在

XML:

xml

fresco:actualImageScaleType="focusCrop"

Java

代码中

java

PointF focusPoint;

// your app populates the focus point

mSimpleDraweeView

.getHierarchy()

.setActualImageFocusPoint(focusPoint);

none

如果你要使用

tile mode

进行显示,那么需要将

scale type

设置为

none.

圆角和圆圈

Drawee

轻松支持圆角显示,并且显示圆角时,并不复制和修改

Bitmap

对象,那样太耗费内存。

圆角

圆角实际有

2

中呈现方式

:

1.

圆圈

-

设置

roundAsCircle

true

2.

圆角

-

设置

roundedCornerRadius

设置圆角时,支持

4

个角不同的半径。

XML

中无法配置,但可在

Java

代码中配置。

设置圆角

可使用以下两种方式

:

1.

默认使用一个

shader

绘制圆角,但是仅仅占位图所要显示的图有圆角效果。失败示意图和重下载示意图无圆角效果。

2.

叠加一个

solid color

来绘制圆角。但是背景需要固定成指定的颜色。

XML

中指定

roundWithOverlayColor

,

或者通过调用

setOverlayColor

来完成此设定。

XML

中配置

SimpleDraweeView

支持如下几种圆角配置

:

xml

<com.facebook.drawee.view.SimpleDraweeView

...

fresco:roundedCornerRadius="5dp"

fresco:roundBottomLeft="false"

fresco:roundBottomRight="false"

fresco:roundWithOverlayColor="@color/blue"

fresco:roundingBorderWidth="1dp"

fresco:roundingBorderColor="@color/red"

代码中配置

在创建

DraweeHierarchy

时,可以给

GenericDraweeHierarchyBuilder

指定一个

RoundingParams

用来绘制圆角效果。

java

RoundingParams roundingParams = RoundingParams.fromCornersRadius(7f);

roundingParams.setOverlayColor(R.color.green);

//

或用

fromCornersRadii

以及

asCircle

方法

genericDraweeHierarchyBuilder

.setRoundingParams(roundingParams);

你也可以在运行时,改变圆角效果

java

RoundingParams roundingParams =

mSimpleDraweeView.getHierarchy().getRoundingParams();

roundingParams.setBorder(R.color.red, 1.0);

roundingParams.setRoundAsCircle(true);

mSimpleDraweeView.getHierarchy().setRoundingParams(roundingParams);

在运行时,不能改变呈现方式

:

原本是圆角,不能改为圆圈。

使用

ControllerBuilder

SimpleDraweeView

有两个方法可以设置所要加载显示图片,简单的方法就是

setImageURI

如果你需要对加载显示的图片做更多的控制和定制,那就需要用到

DraweeController

,本页说明如何使用。

DraweeController

首先,创建一个

DraweeController,

然后传递图片加载请求给

PipelineDraweeControllerBuilder

随后,你可以控制

controller

的其他选项了

:

java

ControllerListener listener = new BaseControllerListener() {...}

 

DraweeController controller = Fresco.newDraweeControllerBuilder()

.setUri(uri)

.setTapToRetryEnabled(true)

.setOldController(mSimpleDraweeView.getController())

.setControllerListener(listener)

.build();

 

mSimpleDraweeView.setController(controller);

在指定一个新的

controller

的时候,使用

setOldController

,这可节省不必要的内存分配。

自定义图片加载请求

在更进一步的用法中,你需要给

Image pipeline

发送一个

ImageRequest

。下面是一个图片加载后,使用后处理器

(postprocessor)

进行图片后处理的例子

.

java

Uri uri;

Postprocessor myPostprocessor = new Postprocessor() { ... }

ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)

.setPostprocessor(myPostprocessor)

.build();

 

DraweeController controller = Fresco.newDraweeControllerBuilder()

.setImageRequest(request)

.setOldController(mSimpleDraweeView.getController())

//

其他设置

.build();

渐进式

JPEG

注意

:

本页提及的

API

仅是初步设计,后续可能变动

Fresco

支持渐进式的网络

JPEG

图。在开始加载之后,图会从模糊到清晰渐渐呈现。

你可以设置一个清晰度标准,在未达到这个清晰度之前,会一直显示占位图。

渐进式

JPEG

图仅仅支持网络图。

初始化

配置

Image pipeline

需要传递一个

ProgressiveJpegConfig

的实例。

这个实例需要完成两个事情

: 1.

返回下一个需要解码的扫描次数

2.

确定多少个扫描次数之后的图片才能开始显示。

下面的实例中,为了实现节省

CPU

,并不是每个扫描都进行解码。

注意

:

每次解码完之后,调用

getNextScanNumberToDecode

,

等待扫描值大于返回值,才有可能进行解码。

假设,随着下载的进行,下载完的扫描序列如下

:

1, 4, 5, 10

。那么:

1.

首次调用

getNextScanNumberToDecode

返回为

2

因为初始时,解码的扫描数为

0

2.

那么

1

将不会解码,下载完成

4

个扫描时,解码一次。下个解码为扫描数为

6

3.

5

不会解码,

10

才会解码

java

ProgressiveJpegConfig pjpegConfig = 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);

}

}

 

ImagePipelineConfig config = ImagePipelineConfig.newBuilder()

.setProgressiveJpegConfig(pjpeg)

.build();

除了自己实现

ProgressiveJpegConfig

也可以直接使用

SimpleProgressiveJpegConfig

At Request Time

目前,我们必须显式地在加载时,允许渐进式

JPEG

图片加载。

java

Uri uri;

ImageRequest request = ImageRequestBuilder

.newBuilderWithSource(uri)

.setProgressiveRenderingEnabled(true)

.build();

PipelineDraweeController controller = Fresco.newControllerBuilder()

.setImageRequest(requests)

.setOldController(mSimpleDraweeView.getController())

.build();

 

mSimpleDraweeView.setController(controller);

我们希望在后续的版本中,在

setImageURI

方法中可以直接支持渐进式图片加载。

动画图

(gif)

Fresco

支持

GIF

WebP

格式图片;支持

WebP

格式的动画图也支持

(

包括扩展

WebP

格式

)

,支持

2.3

及其以后那些没有原生

WebP

支持的系统。

设置动画图自动播放

如果你希望图片下载完之后自动播放,同时,当

View

从屏幕移除时,停止播放,只需要在

image request

中简单设置,如下

:

java

Uri uri;

ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)

.setAutoPlayAnimation(true)

. // other setters

.build();

 

DraweeController controller = Fresco.newDraweeControllerBuilder()

.setImageRequest(request)

. // other setters

.build();

mSimpleDraweeView.setController(controller);

手动控制动画图播放

也许,你希望在图片加载完之后,手动控制动画的播放,那么这样做:

java

ControllerListener controllerListener = new BaseControllerListener() {

@Override

public void onFinalImageSet(

String id,

@Nullable ImageInfo imageInfo,

@Nullable Animatable anim) {

if (anim != null) {

//

根据业务逻辑

在合适的时机播放动画。

}

};

 

Uri uri;

PipelineDraweeController controller = Fresco.newControllerBuilder()

.setControllerListener(controllerListener)

.setUri(uri);

// other setters

.build();

mSimpleDraweeView.setController(controller);

另外,

controller

提供对

Animatable

的访问。

如果有可用动画的话,可对动画进行灵活的控制

:

java

Animatable animation = mSimpleDraweeView.getController().getAnimatable();

if (animation != null) {

//

开始播放

animation.start();

//

一段时间之后

根据业务逻辑

停止播放

animation.stop();

}

多图请求及图片复用

多图请求需

自定义

ImageRequest

.

先显示低分辨率的图,然后是高分辨率的图

如果你要显示一张高分辨率的图,但是这张图下载比较耗时。你可以在下载前,先提供一张很快能下载完的小缩略图。这比一直显示占位图,用户体验会好很多。

这时,你可以设置两个图片的

URI

,一个是低分辨率的缩略图,一个是高分辨率的图。

java

Uri lowResUri, highResUri;

PipelineDraweeController controller = Fresco.newControllerBuilder()

.setLowResImageRequest(ImageRequest.fromUri(lowResUri))

.setImageRequest(ImageRequest.fromUri(highResUri))

.setOldController(mSimpleDraweeView.getController())

.build();

mSimpleDraweeView.setController(controller);

缩略图预览

本功能仅支持本地

URI

,并且是

JPEG

图片格式

如果本地

JPEG

图,有

EXIF

的缩略图,

image pipeline

会立刻返回一个缩略图。完整的清晰大图,在

decode

完之后再显示。

java

Uri uri;

ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)

.setLocalThumbnailPreviewsEnabled(true)

.build();

 

PipelineDraweeController controller = Fresco.newControllerBuilder()

.setImageRequest(request)

.setOldController(mSimpleDraweeView.getController())

.build();

mSimpleDraweeView.setController(controller);

本地图片复用

大部分的时候,一个图片可能会对应有多个

URI

,比如

:

拍照上传。本地图片较大,上传的图片较小。上传完成之后的图片,有一个

url

,如果要加载这个

url

,可直接加载本地图片。

本地已经有

600x600

尺寸的大图了,需要显示

100x100

的小图

对于一个

URI

image pipeline

会依次检查内存,磁盘,如果没有从网络下载。

而对于一个图片的多个

URI

image pipeline

会先检查他们是否在内存中。如果没有任何一个是在内存中的,会检查是否在本地存储中。如果也没有,才会执行网络下载。

但凡有任何一个检查发现在内存或者在本地存储中,都会进行复用。列表顺序就是要显示的图片的优先顺序。

使用时,创建一个

image request

列表,然后传给

ControllerBuilder:

java

Uri uri1, uri2;

ImageRequest request = ImageRequest.fromUri(uri1);

ImageRequest request2 = ImageRequest.fromUri(uri2);

ImageRequest[] requests = { request1, request2 };

 

PipelineDraweeController controller = Fresco.newControllerBuilder()

.setFirstAvailableImageRequests(requests)

.setOldController(mSimpleDraweeView.getController())

.build();

mSimpleDraweeView.setController(controller);

监听下载事件

你也许想在图片下载完成或者下载失败之后,做一些其他事情。

图片是后台线程异步加载的,我们可以使用一个

ControllerListener

实现事件的监听。

_

在监听事件回调时,无法修改图片,如果需要修改图片,可使用

后处理器

(Postprocessor)

ControllerListener controllerListener = new BaseControllerListener() {

@Override

public void onFinalImageSet(

String id,

@Nullable ImageInfo imageInfo,

@Nullable Animatable anim) {

if (imageInfo == null) {

return;

}

QualityInfo qualityInfo = imageInfo.getQualityInfo();

FLog.d("Final image received! " +

"Size %d x %d",

"Quality level %d, good enough: %s, full quality: %s",

imageInfo.getWidth(),

imageInfo.getHeight(),

qualityInfo.getQuality(),

qualityInfo.isOfGoodEnoughQuality(),

qualityInfo.isOfFullQuality());

}

 

@Override

public void onIntermediateImageSet(String id, @Nullable ImageInfo imageInfo) {

FLog.d("Intermediate image received");

}

 

@Override

public void onFailure(String id, Throwable throwable) {

FLog.e(getClass(), throwable, "Error loading %s", id)

}

};

 

Uri uri;

DraweeController controller = Fresco.newControllerBuilder()

.setControllerListener(controllerListener)

.setUri(uri);

// other setters

.build();

mSimpleDraweeView.setController(controller);

对所有的图片加载,

onFinalImageSet

或者

onFailure

都会被触发。前者在成功时,后者在失败时。

如果允许呈现

渐进式

JPEG

,同时图片也是渐进式图片,

onIntermediateImageSet

会在每个扫描被解码后回调。具体图片的那个扫描会被解码,参见

渐进式

JPEG

缩放和旋转图片

使用这个功能需要直接

创建

image request

缩放图片

什么时候该修改图片尺寸

一般地,当所要显示的图片和显示区域大小不一致时,会按以下方式进行处理。

1.

从服务器下载小一些的图片

2.

显示时缩放图片

3.

调整图片尺寸大小

对于一个图片,如果服务器支持不同尺寸的缩略图,那么每次下载都选择尺寸最匹配的图片,这个不仅节省数据流量也节约本地储存和

CPU

如果服务器不支持,或者处理本地图片的话,第二个选择是

使用缩放类型

。缩放是用

Androi

内置的功能使图像和显示边界相符。在

4.0

之后,支持硬件加速。这在大部分情况下是最

快,同时也是最高效的显示一张和显示边界大小相符的图片的方式。首先指定

layout_width

layout_width

为指定值,然后指定

缩放类型

但当所要显示的图片比显示区域大许多的时候,不推荐这样做,缩放过程会导致大量的内存消耗。

这时,需要改变图片尺寸。

修改图片尺寸

调整大小并不是修改原来的文件,而是在解码之前,在

native

内存中修改。

这个缩放方法,比

Android

内置的缩放范围更大。

Android

相机生成的照片一般尺寸都很大,需要调整大小之后才能被显示。

目前,仅仅支持

JPEG

格式的图片,同时,大部分的

Android

系统相机图片都是

JPEG

的。

如果要修改图片尺寸,创建

ImageRequest

时,提供一个

ResizeOptions:

java

Uri uri = "file:///mnt/sdcard/MyApp/myfile.jpg";

int width = 50, height = 50;

ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)

.setResizeOptions(new ResizeOptions(width, height))

.build();

PipelineDraweeController controller = Fresco.newDraweeControllerBuilder()

.setOldController(mDraweeView.getController())

.setImageRequest(request)

.build();

mSimpleDraweeView.setController(controller);

自动旋转

如果看到的图片是侧着的,用户是难受的。许多设备会在

JPEG

文件的

metadata

中记录下照片的方向。如果你想图片呈现的方向和设备屏幕的方向一致,你可以简单地这样做到

:

java

ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)

.setAutoRotateEnabled(true)

.build();

// as above

修改图片

有时,我们想对从服务器下载,或者本地的图片做些修改,比如在某个坐标统一加个网格什么的。这时使用后处理器

(Postprocessor)

便可达到目的。

例子

:

给图片加个网格

:

java

Uri uri;

Postprocessor redMeshPostprocessor = new Postprocessor() {

@Override

public String getName() {

return "redMeshPostprocessor";

}

 

@Override

public void process(Bitmap bitmap) {

for (int x = 0; x < bitmap.getWidth(); x+=2) {

for (int y = 0; y < bitmap.getHeight(); y+=2) {

bitmap.setPixel(x, y, Color.RED);

}

}

}

}

 

ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)

.setPostprocessor(redMeshPostprocessor)

.build();

 

PipelineDraweeController controller = Fresco.newDraweeControllerBuilder()

.setImageRequest(request)

.setOldController(mSimpleDraweeView.getOldController())

// other setters as you need

.build();

mSimpleDraweeView.setController(controller);

注意点

图片在进入后处理器

(postprocessor)

的图片是原图的一个完整拷贝,原来的图片不受修改的影响。在

5.0

以前的机器上,拷贝后的图片也在

native

内存中。

在开始一个图片显示时,即使是反复显示同一个图片,在每次进行显示时,都需要指定后处理器。

对于同一个图片,每次显示,可以使用不同的后处理器。

Repeated Postprocessors

如果想对同一个图片进行多次后处理,那么继承

BaseRepeatedPostprocessor

即可。该类有一个

update

方法,需要执行后处理时,调用该方法即可。

下面的例子展示了在运行时,后处理改变图片网格的颜色

:

java

public class MeshPostprocessor extends BaseRepeatedPostprocessor {

private int mColor = Color.TRANSPARENT;

 

public void setColor(int color) {

mColor = color;

update();

}

 

@Override

public String getName() {

return "meshPostprocessor";

}

 

@Override

public void process(Bitmap bitmap) {

for (int x = 0; x < bitmap.getWidth(); x+=2) {

for (int y = 0; y < bitmap.getHeight(); y+=2) {

bitmap.setPixel(x, y, mColor);

}

}

}

}

MeshPostprocessor meshPostprocessor = new MeshPostprocessor();

 

// setPostprocessor as in above example

 

//

改变颜色

meshPostprocessor.setColor(Color.RED);

meshPostprocessor.setColor(Color.BLUE);

每个

image request,

仍旧只有一个

Postprocessor

,但是这个后处理器是状态相关了。

图片请求

如果你需要的

ImageRequest

仅仅是一个

URI

,那么

ImageRequest.fromURI

就足够了,在

多图请求及图片复用

中,有这样的用法。

否则,你需要

ImageRequestBuilder

来做更多的事情。

java

Uri uri;

 

ImageDecodeOptions decodeOptions = ImageDecodeOptions.newBuilder()

.setBackgroundColor(Color.GREEN)

.build();

 

ImageRequest request = ImageRequestBuilder

.newBuilderWithSource(uri)

.setAutoRotateEnabled(true)

.setLocalThumbnailPreviewsEnabled(true)

.setLowestPermittedRequestLevel(RequestLevel.FULL_FETCH)

.setProgressiveRenderingEnabled(false)

.setResizeOptions(new ResizeOptions(width, height))

.build();

ImageRequest

的属性和成员

uri

-

唯一的必选的成员

.

参考

支持的

URIs

autoRotateEnabled

-

是否支持

自动旋转

.

progressiveEnabled

-

是否支持

渐进式加载

.

postprocessor

-

后处理器

(postprocess)

.

resizeOptions

-

图片缩放选项,用前请先阅读

缩放和旋转

.

最低请求级别

Image pipeline

加载图片时有一套明确的

请求流程

1.

检查内存缓存,有如,立刻返回。这个操作是实时的。

2.

检查未解码的图片缓存,如有,解码并返回。

3.

检查磁盘缓存,如果有加载,解码,返回。

4.

下载或者加载本地文件。调整大小和旋转(如有),解码并返回。对于网络图来说,这一套流程下来是最耗时的。

setLowestPermittedRequestLevel

允许设置一个最低请求级别,请求级别和上面对应地有以下几个取值

:

BITMAP_MEMORY_CACHE

ENCODED_MEMORY_CACHE

DISK_CACHE

FULL_FETCH

如果你需要立即取到一个图片,或者在相对比较短时间内取到图片,否则就不显示的情况下,这非常有用。

自定义

View

DraweeHolders

总有一些时候,

DraweeViews

是满足不了需求的,在展示图片的时候,我们还需要展示一些其他的内容,或者支持一些其他的操作。在同一个

View

里,我们可能会想显示一张或者多

张图。

在自定义

View

中,

Fresco

提供了两个类来负责图片的展现

:

DraweeHolder

单图情况下用。

MultiDraweeHolder

多图情况下用。

自定义

View

需要完成的事情

Android

呈现

View

对象,只有

View

对象才能得到一些系统事件的通知。

DraweeViews

处理这些事件通知,高效地管理内存。使用

DraweeHolder

时,你需要自己实现这几个方法。

处理

attach/detach

事件

如果没按照以下步骤实现的话,很可能会引起内存泄露

当图片不再在

View

上显示时,比如滑动时

View

滑动到屏幕外,或者不再绘制,图片就不应该再存在在内存中。

Drawees

监听这些事情,并负责释放内存。当图片又需要显示时,重新加

载。

这些在

DraweeView

中是自动的,但是在自定义

View

中,需要我们自己去操作,如下

:

java

DraweeHolder mDraweeHolder;

 

@Override

public void onDetachedFromWindow() {

super.onDetachedToWindow();

mDraweeHolder.onDetach();

}

 

@Override

public void onStartTemporaryDetach() {

super.onStartTemporaryDetach();

mDraweeHolder.onDetach();

}

 

@Override

public void onAttachedToWindow() {

super.onAttachedToWindow();

mDraweeHolder.onAttach();

}

 

@Override

public void onFinishTemporaryDetach() {

super.onFinishTemporaryDetach();

mDraweeHolder.onAttach();

}

处理触摸事件

如果你启用了

点击重新加载

,在自定义

View

中,需要这样

:

java

@Override

public boolean onTouchEvent(MotionEvent event) {

return mDraweeHolder.onTouchEvent(event) || super.onTouchEvent(event);

}

自定义

onDraw

java

Drawable drawable = mDraweeHolder.getHierarchy().getTopLevelDrawable();

drawable.setBounds(...);

否则图片将不会出现

不要向下转换这个

Drawable

不要变换这个

Drawable

其他应该做的

重写

verifyDrawable:

java

@Override

protected boolean verifyDrawable(Drawable who) {

if (who == mDraweeHolder.getHierarchy().getTopLevelDrawable()) {

return true;

}

//

对其他

Drawable

的验证逻辑

}

确保

invalidateDrawable

处理了图片占用的那块区域。

创建

DraweeHolder

这同样需要非常小心和细致

构造函数

我们推荐如下实现构造函数

:

重写

3

个构造函数

在每个构造函数中调用同等签名的父类构造函数,和一个私有的

init

方法。

init

方法中执行初始化操作。

即,不要在构造函数中用

this

来调用另外一个构造。

这样可以保证,不管调用哪个构造,都可以正确地执行初始化流程。然后在

init

方法中创建

holder

创建

Holder

如果有可能,只在

View

创建时,创建

Drawees

。创建

DraweeHierarchy

开销较大,最好只做一次。

java

class CustomView extends View {

DraweeHolder<GenericDraweeHierarchy> mDraweeHolder;

 

// constructors following above pattern

 

private void init() {

GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(getResources());

.set...

.set...

.build();

mDraweeHolder = DraweeHolder.create(hierarchy, context);

}

}

设置要显示的图片

使用

controller builder

创建

DraweeController

,然后调用

holder

setController

方法,而不是设置给自定义

View

java

DraweeController controller = Fresco.newControllerBuilder()

.setUri(uri)

.setOldController(mDraweeHolder.getController())

.build();

mDraweeHolder.setController(controller);

MultiDraweeHolder

DraweeHolder

相比,

MultiDraweeHolder

add

,

remove

,

clear

等方法可以操作

Drawees

。如下

:

java

MultiDraweeHolder<GenericDraweeHierarchy> mMultiDraweeHolder;

 

private void init() {

GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(getResources());

.set...

.build();

mMultiDraweeHolder = new MultiDraweeHolder<GenericDraweeHierarchy>();

mMultiDraweeHolder.add(new DraweeHolder<GenericDraweeHierarchy>(hierarchy, context));

// repeat for more hierarchies

}

同样,也需要处理系统事件,设置声音等等,就想处理单个

DraweeHolder

那样。

一些陷阱

不要向下转换

不要试图把

Fresco

返回的一些对象进行向下转化,这也许会带来一些对象操作上的便利,但是也许在后续的版本中,你会遇到一些因为向下转换特性丢失导致的难以处理的问题。

不要使用

getTopLevelDrawable

DraweeHierarchy.getTopLevelDrawable()

仅仅

应该在

DraweeViews

中用,除了定义

View

中,其他应用代码建议连碰都不要碰这个。

在自定义

View

中,也千万不要将返回值向下转换,也许下个版本,我们会更改这个返回值类型。

不要复用

DraweeHierarchies

永远不要吧

DraweeHierarchy

通过

DraweeView.setHierarchy

设置给不同的

View

DraweeHierarchy

是由一系列

Drawable

组成的。在

Android

, Drawable

不能被多个

View

共享。

不要在多个

DraweeHierarchy

中使用同一个

Drawable

原因同上。当时可以使用不同的资源

ID

Android

实际会创建不同的

Drawable

不要直接给

DraweeView

设置图片。

目前

DraweeView

直接继承于

ImageView

,因此它有

setImageBitmap

,

setImageDrawable

等方法。

如果利用这些方法,直接设置一个图片。内部的

DraweeHierarchy

就会丢失,也就无法取到

image pipeline

的任何图像了。

使用

DraweeView

时,请不要使用任何

ImageView

的属性

在后续的版本中,

DraweeView

会直接从

View

派生。任何属于

ImageView

但是不属于

View

的方法都会被移除。

3

IMAGE PIPELINE

指南

Image Pipeline

介绍

Image pipeline

负责完成加载图像,变成

Android

设备可呈现的形式所要做的每个事情。

大致流程如下

:

1.

检查内存缓存,如有,返回

2.

后台线程开始后续工作

3.

检查是否在未解码内存缓存中。如有,解码,变换,返回,然后缓存到内存缓存中。

4.

检查是否在文件缓存中,如果有,变换,返回。缓存到未解码缓存和内存缓存中。

5.

从网络或者本地加载。加载完成后,解码,变换,返回。存到各个缓存中。

既然本身就是一个图片加载组件,那么一图胜千言。

图片

3.1

Image Pipeline Diagram

上图中,

disk cache

实际包含了未解码的内存缓存在内,统一在一起只是为了逻辑稍微清楚一些。关于缓存,更多细节可以参考

这里

Image pipeline

可以从

本地文件

加载文件,也可以从网络。支持

PNG

GIF

WebP, JPEG

各个

Android

系统的

WebP

适配

3.0

系统之前,

Android

是不支持

WebP

格式的。在

4.1.2

之前,扩展

WebP

格式是不支持的。

Image pipeline

的支持下,从

2.3

之后,都可以使用

WebP

格式。

配置

Image Pipeline

对于大多数的应用,

Fresco

的初始化,只需要以下一句代码

:

Fresco.

initialize

(

context

)

;

对于那些需要更多进一步配置的应用,我们提供了

ImagePipelineConfig

以下是一个示例配置,列出了所有可配置的选项。几乎没有应用是需要以下这所有的配置的,列出来仅仅是为了作为参考。

java

ImagePipelineConfig config = ImagePipelineConfig.newBuilder()

.setBitmapMemoryCacheParamsSupplier(bitmapCacheParamsSupplier)

.setCacheKeyFactory(cacheKeyFactory)

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

请记得将配置好的

ImagePipelineConfig

传递给

Fresco.initialize!

否则仍旧是默认配置。

关于

Supplier

许多配置的

Builder

都接受一个

Supplier

类型的参数而不是一个配置的实例。

创建时也许有一些麻烦,但这带来更多的利好:这允许在运行时改变创建行为。以内存缓存为例,每隔

5

分钟就可检查一下

Supplier

,根据实际情况返回不同类型。

如果你需要动态改变参数,那就是用

Supplier

每次都返回同一个对象。

java

Supplier<X> xSupplier = new Supplier<X>() {

public X get() {

return new X(xparam1, xparam2...);

}

);

// when creating image pipeline

.setXSupplier(xSupplier);

线程池

Image pipeline

默认有

3

个线程池

:

1.

3

个线程用于网络下载

2.

两个线程用于磁盘操作

:

本地文件的读取,磁盘缓存操作。

3.

两个线程用于

CPU

相关的操作

:

解码,转换,以及后处理等后台操作。

对于网络下载,你可以定制网络层的操作,具体参考

:

自定义网络层加载

.

对于其他操作,如果要改变他们的行为,传入一个

ExecutorSupplier

即可。

内存缓存的配置

内存缓存和未解码的内存缓存的配置由一个

Supplier

控制,这个

Supplier

返回一个

MemoryCacheParams

配置磁盘缓存

你可使用

Builder

模式创建一个

DiskCacheConfig:

java

DiskCacheConfig diskCacheConfig = DiskCacheConfig.newBuilder()

.set....

.set....

.build()

 

// when building ImagePipelineConfig

.setMainDiskCacheConfig(diskCacheConfig)

缓存统计

如果你想统计缓存的命中率,你可以实现

ImageCacheStatsTracker,

在这个类中,每个缓存时间都有回调通知,基于这些事件,可以实现缓存的计数和统计。

缓存

三级缓存

1. Bitmap

缓存

Bitmap

缓存存储

Bitmap

对象,这些

Bitmap

对象可以立刻用来显示或者用于后处理

5.0

以下系统,

Bitmap

缓存位于

ashmem

,这样

Bitmap

对象的创建和释放将不会引发

GC

,更少的

GC

会使你的

APP

运行得更加流畅。

5.0

及其以上系统,相比之下,内存管理有了很大改进,所以

Bitmap

缓存直接位于

Java

heap

上。

当应用在后台运行是,该内存会被清空。

2.

未解码图片的内存缓存

这个缓存存储的是原始压缩格式的图片。从该缓存取到的图片在使用之前,需要先进行解码。

如果有调整大小,旋转,或者

WebP

编码转换工作需要完成,这些工作会在解码之前进行。

APP

在后台时,这个缓存同样会被清空。

3.

文件缓存

和未解码的内存缓存相似,文件缓存存储的是未解码的原始压缩格式的图片,在使用之前同样需要经过解码等处理。

和内存缓存不一样,

APP

在后台时,内容是不会被清空的。即使关机也不会。用户可以随时用系统的设置菜单中进行清空缓存操作。

用一个文件还是两个文件缓存

?

如果要使用

2

个缓存,在

配置

image pipeline

时调用

setMainDiskCacheConfig

setSmallImageDiskCacheConfig

方法即可。

大部分的应用有一个文件缓存就够了,但是在一些情况下,你可能需要两个缓存。比如你也许想把小文件放在一个缓存中,大文件放在另外一个文件中,这样小文件就不会因大文件的

频繁变动而被从缓存中移除。

至于什么是小文件,这个由应用来区分,在

创建

image request

,

设置

ImageType

即可

:

java

ImageRequest request = ImageRequest.newBuilderWithSourceUri(uri)

.setImageType(ImageType.SMALL)

如果你仅仅需要一个缓存,那么不调用

setSmallImageDiskCacheConfig

即可。

Image pipeline

默认会使用同一个缓存,同时

ImageType

也会被忽略。

内存用量的缩减

配置

Image pipeline

时,我们可以指定每个缓存最大的内存用量。但是有时我们可能会想缩小内存用量。比如应用中有其他数据需要占用内存,不得不把图片缓存清除或者减小

或者

我们想检查看看手机是否已经内存不够了。

Fresco

的缓存实现了

DiskTrimmable

或者

MemoryTrimmable

接口。这两个接口负责从各自的缓存中移除内容。

在应用中,可以给

Image pipeline

配置上实现了

DiskTrimmableRegistry

MemoryTrimmableRegistry

接口的对象。

实现了这两个接口的对象保持着一个列表,列表中的各个元素在内存不够时,缩减各自的内存用量。

直接使用

Image Pipeline

本页介绍

Image pipeline

的高级用法,大部分的应用使用

Drawees

image pipeline

打交道就好了。

直接使用

Image pipeline

是较为有挑战的事情,这意味着要维护图片的内存使用。

Drawees

会根据各种情况确定图片是否需要在内存缓存中,在需要时加载,在不需要时移除。直接使用

的话,你需要自己完成这些逻辑。

Image pipeline

返回的是一个

CloseableReference

对象。在这些对象不需要时,

Drawees

会调用

.close()

方法。如果你的应用不使用

Drawees

,那你需要自己完成这个事情。

Java

GC

机制会在

Bitmap

不使用时,清理掉

Bitmap

。但要

GC

时总是太迟了,另外

GC

是很昂贵的开销。

GC

大对象也会带来性能问题,尤其是在

5.0

以下系统。

调用

pipeline

首先

创建一个

image request

.

然后传递给

ImagePipeline:

java

ImagePipeline imagePipeline = Fresco.getImagePipeline();

DataSource<CloseableReference<CloseableImage>>

dataSource = imagePipeline.fetchDecodedImage(imageRequest);

关于如果接收数据,请参考

数据源

章节。

忽略解码

如果你不保持图片原始格式,不执行解码,使用

fetchEncodedImage

即可

:

java

DataSource<CloseableReference<PooledByteBuffer>>

dataSource = imagePipeline.fetchEncodedImage(imageRequest);

Bitmap

缓存中立刻取到结果

不像其他缓存,如果图片在内存缓存中有的话,可以在

UI

线程立刻拿到结果。

java

DataSource<CloseableReference<CloseableImage>> dataSource =

mImagePipeline.fetchImageFromBitmapCache(imageRequest);

CloseableReference<CloseableImage> imageReference;

try {

imageReference = dataSource.getResult();

if (imageReference != null) {

CloseableImage image = imageReference.get();

// do something with the image

}

} finally {

dataSource.close();

CloseableReference.closeSafely(imageReference);

}

千万

不要

省略掉

finally

中的代码

!

预加载图片

预加载图片可减少用户等待的时间,如果预加载的图片用户没有真正呈现给用户,那么就浪费了用户的流量,电量,内存等资源了。大多数应用,并不需要预加载。

Image pipeline

提供两种预加载方式。

预加载到文件缓存

:

java

imagePipeline.prefetchToDiskCache(imageRequest);

预加载到内存缓存

:

java

imagePipeline.prefetchToBitmapCache(imageRequest);

数据源和数据订阅者

数据源和

Future

,

有些相似,都是异步计算的结果。

不同点在于,数据源对于一个调用会返回一系列结果,

Future

只返回一个。

提交一个

Image request

之后,

Image pipeline

返回一个数据源。从中获取数据需要使用数据订阅者

(DataSubscriber).

当你仅仅需要

Bitmap

如果你请求

Image pipeline

仅仅是为了获取一个

Bitmap

,

对象。你可以利用简单易用的

BaseBitmapDataSubscriber:

java

dataSource.subscribe(new BaseBitmapDataSubscriber() {

@Override

public void onNewResultImpl(@Nullable Bitmap bitmap) {

// You can use the bitmap in only limited ways

// No need to do any cleanup.

}

 

@Override

public void onFailureImpl(DataSource dataSource) {

// No cleanup required here.

}

});

看起来很简单,对吧。下面是一些小警告

:

千万

不要

bitmap

复制给

onNewResultImpl

函数范围之外的任何变量。订阅者执行完操作之后,

image pipeline

会回收这个

bitmap

,释放内存。在这个函数范围内再次使用这个

Bitmap

对象进行绘制将会导致

IllegalStateException

通用的解决方案

如果你就是想维持对这个

Bitmap

对象的引用,你不能维持纯

Bitmap

对象的引用,可以利用

可关闭的引用

(closeable references)

BaseDataSubscriber:

java

DataSubscriber dataSubscriber =

new BaseDataSubscriber<CloseableReference<CloseableImage>>() {

@Override

public void onNewResultImpl(

DataSource<CloseableReference<CloseableImage>> dataSource) {

 

if (!dataSource.isFinished()) {

FLog.v("Not yet finished - this is just another progressive scan.");

}

 

CloseableReference<CloseableImage> imageReference = dataSource.getResult();

if (imageReference != null) {

try {

CloseableImage image = imageReference.get();

// do something with the image

} finally {

imageReference.close();

}

}

}

@Override

public void onFailureImpl(DataSource dataSource) {

Throwable throwable = dataSource.getFailureCause();

// handle failure

}

};

 

dataSource.subscribe(dataSubscriber, executor);

这样,只要遵守

可关闭的引用使用规则

,你就可以把这个

CloseableReference

复制给其他变量了。

可关闭的引用

本页内容仅为高级使用作参考

大部分的应用,直接使用

Drawees

就好了,不用考虑关闭的事情了。

Java

带有垃圾收集功能,许多开发者习惯于不自觉地创建一大堆乱七八糟的对象,并且想当然地认为他们会从内存中想当然地消失。

5.0

系统之前,这样的做法对于操作

Bitmap

是极其糟糕的。

Bitmap

占用了大量的内存,大量的内存申请和释放引发频繁的

GC

,使得界面卡顿不已。

Bitmap

Java

中为数不多的能让

Java

开发者想念或者羡慕

C++

以及

C++

众多的指针库,比如

Boost

的东西。

Fresco

的解决方案是

:

可关闭的引用

(CloseableReference)

为了正确地使用它,请按以下步骤进行操作

:

1.

调用者拥有这个引用

我们创建一个引用,但我们传递给了一个调用者,调用者将持有这个引用。

java

CloseableReference<Val> foo() {

Val val;

return CloseableReference.of(val);

}

2.

持有者在离开作用域之前,需要关闭引用

创建了一个引用,但是没有传递给其他调用者,在结束时,需要关闭。

java

void gee() {

CloseableReference<Val> ref = foo();

try {

haa(ref);

} finally {

ref.close();

}

}

finally

中最适合做此类事情了。

3.

除了引用的持有者,闲杂人等

不得

关闭引用

作为一个参数传递,调用者持有这个引用,在下面的函数体中,不能关闭引用。

java

void haa(CloseableReference<?> ref) {

Log.println("Haa: " + ref.get());

}

如果调用了

.close()

,

调用者尝试调用

.get()

时,会抛出

IllegalStateException

4.

在赋值给变量前,先进行

clone

在类中使用

:

java

class MyClass {

CloseableReference<Val> myValRef;

 

void mmm(CloseableReference<Val> ref) {

myValRef = ref.clone();

};

// caller can now safely close its copy as we made our own clone.

 

void close() {

CloseableReference.closeSafely(myValRef);

}

}

// MyClass

的调用者需要关闭

myValRef

在内部中使用

:

java

void haa(CloseableReference<?> ref) {

final CloseableReference<?> refClone = ref.clone();

executor.submit(new Runnable() {

public void run() {

try {

Log.println("Haa Async: " + refClone.get());

} finally {

refClone.close();

}

}

});

//

当前函数域内可安全关闭

闭包内为已经

clone

过的引用。

}

4

第三方类库

自定义网络加载

Image pipeline

默认使用

HttpURLConnection

。应用可以根据自己需求使用不同的网络库。

OkHttp

OkHttp

是一个流行的开源网络请求库。

Imagepipeline

有一个使用

OkHttp

替换掉了

Android

默认的网络请求的补充。

如果需要使用

OkHttp,

不要使用这个

下载

页面的

gradle

依赖配置,应该使用下面的依赖配置

groovy

dependencies {

// your project's other dependencies

compile: "com.facebook.fresco:drawee:0.1.0+"

compile: "com.facebook.fresco:imagepipeline-okhttp:0.1.0+"

}

配置

Image pipeline

这时也有一些不同,不再使用

ImagePipelineConfig.newBuilder

,

而是使用

OkHttpImagePipelineConfigFactory

:

java

Context context;

OkHttpClient okHttpClient; // build on your own

ImagePipelineConfig config = OkHttpImagePipelineConfigFactory

.newBuilder(context, okHttpClient)

. // other setters

. // setNetworkFetchProducer is already called for you

.build();

Fresco.initialize(context, config);

使用自定的网络层

For complete control on how the networking layer should behave, you can provide one for your app. You must subclass

为了完全控制网络层的行为,你可以自定义网络层。继承

NetworkFetchProducer ,

这个类包含了网络通信。

你也可以选择性地继承

NfpRequestState ,

这个类是请求时的数据结构描述。

默认的

HttpURLConnection

可以作为一个参考

.

在配置

Image pipeline

时,把

producer

传递给

Image pipeline

java

ImagePipelineConfig config = ImagePipelineConfig.newBuilder()

.setNetworkFetchProducer(myNetworkFetchProducer);

. // other setters

.build();

Fresco.initialize(context, config);

使用其他的

Image Loader

Drawee

并不是吊死在特定的一种图片加载机制上,它同样适用于其他

image loader

不过有一些特性,只有

Fresco image pipeline

才有。前面的提到的需要使用

ImageRequest

和配置

image pipeline

的特性,使用其他

image loader

时都有可能不起作用。

Drawee

Volley ImageLoader

配合使用

我们有一个

Drawee

使用

Volley

ImageLoader

的补充实现。

我们仅仅对那些已经深度使用

Volley ImageLoader

的应用推荐这个组合。

同样地,如要使用,使用下面的依赖,而不是

下载

页面给出的依赖

:

groovy

dependencies {

// your project's other dependencies

compile: "com.facebook.fresco:drawee-volley:0.1.0+"

}

初始化

Volley ImageLoader

这时,不需要再调用

Fresco.initialize

了,需要的是初始化

Volley

java

Context context;

ImageLoader imageLoader; // build yourself

VolleyDraweeControllerBuilderSupplier mControllerBuilderSupplier

= new VolleyDraweeControllerBuilderSupplier(context, imageLoader);

SimpleDraweeView.initialize(mControllerBuilderSupplier);

不要让

VolleyDraweeControllerBuilderSupplier

离开作用域,你需要它来创建

DraweeController

,除非你只使用

SimpleDraweeView.setImageURI

DraweeControllers

Volley ImageLoader

配合使用

不是调用

Fresco.newControllerBuilder

,

而是

:

java

VolleyController controller = mControllerBuilderSupplier

.newControllerBuilder()

. // setters

.build();

mSimpleDraweeView.setController(controller);

Drawee

和其他

Image Loader

配合使用

依照

源码

作为例子,其他

Image Loader

也是可以和

Drawee

配合使用的,但是没有我们还没有

Drawee

和其他

Image loader

的配合使用的补充实现。

更多信息请访问

http://wiki.jikexueyuan.com/project/fresco/

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论