好久没写博客了,最近在对公司的项目进行重构和优化,针对图片加载框架的修改,原来使用的是Glide,现在改成Facebook的fresco来做对比。先介绍fresco的基本使用。
我这里用的IDE是Android Studio,对于库支持的使用配置比较简单,如下:
在module的gradle中加入库的依赖引用。
dependencies {
/************图片加载框架fresco库开始**************/
compile 'com.facebook.fresco:fresco:0.12.0'
/************图片加载框架fresco库结束**************/
}
这样就可以使用fresco库来加载图片了,是不是很简单。
当然fresco库远不止这些,这是一个很强大的库,如要支持webP格式的图片,Gif图片。还可以引入如下的库:
dependencies {
/************图片加载框架fresco库开始**************/
compile 'com.facebook.fresco:fresco:0.12.0'
// 在API < 14的系统如也要支持 webP图片的话加入
compile 'com.facebook.fresco:animated-base-support:0.12.0'
// 支持Gif图片,需加入
compile 'com.facebook.fresco:animated-gif:0.12.0'
// 支持webP图片的动态图,需加入
compile 'com.facebook.fresco:animated-webp:0.12.0'
// 支持webP图片的静态图,需加入
compile 'com.facebook.fresco:webpsupport:0.12.0'
/************图片加载框架fresco库结束**************/
}
webp格式图片,进一步的优化了图片资源的大小,加载的效率更高了,提升了app的性能。
为了迎合官方推广使用Android Studio,这里就不多介绍fresco在Eclipse中的使用,这里附上库的下载包:zip文件
首先在加载图片前需要对框架做初始化的操作,通过调用Fresco.initialize方法一次就可以初始化。因为多次调用此方法是一样的,所以作为初始化的地方最好的位置就是 Application的onCreate里:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Fresco.initialize(this);
}
}
注意事项:一般情况下这样就可以了,但是对于多进程的应用来说,不同的进程都会执行一次Application的onCreate方法,那就意味着在onCreate里的一些初始化操作会多次执行,多少对于应用的性能是有影响的。那对于多进程环境的应用可以有如下解决方案(伪代码,就是这么随意任性):
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
String curProcessName = 获取当前进程的名字
String needUseFrescoProcessName = 需要使用fresco组件库来加载图片的进程名字
if(curProcessName.equals(needUseFrescoProcessName)){
// 当前进程就是需要加载fresco组件的进程,才初始化组件
Fresco.initialize(this);
// 同理其它的一些组件也是,如:第三方分享组件,统计组件,支付组件等等
}
}
}
我们先加载一张网络图片,那就需要网络权限,在manifest中加入,别加入自己的Application:
<manifest
...
>
<uses-permission android:name="android.permission.INTERNET" />
<application
...
android:label="@string/app_name"
android:name=".MyApplication"
>
...
application>
...
manifest>
fresco为我们提供了一个图片显示控件SimpleDraweeView,这个控件基本上能满足我们绝大多数的需求,使用也很简单:
<com.facebook.drawee.view.SimpleDraweeView
xmlns:fresco="http://schemas.android.com/apk/res-auto"
android:id="@+id/sdv_head_image"
android:layout_width="50dp"
android:layout_height="50dp"
fresco:placeholderImage="@drawable/ic_wait_image"
/>
因为要用到控件的自定义属性,所以需要引入自定义的命名空间fresco。
placeholderImage顾名思义,就是图片还在加载,下载的时候,等待期间显示的图片,可以是drawable图片也可以是color颜色。下面来加载一张网络图片:
SimpleDraweeView headImage = (SimpleDraweeView)findViewById(R.id.sdv_head_image);
Uri headUri = Uri.parse("https://raw.githubusercontent.com/facebook/fresco/gh-pages/static/logo.png");
headImage.setUri(headUri);
是不是很简单,只要把图片的地址用Uri包装起来,设置给SimpleDraweeView,fresco就会帮你去显示占位符图片,下载,缓存,在图像不可见的时候及时的回收图片占用的内存。so easy~~
Uri包装不同的图片格式如下:
类型 | SCHEME | 示例 |
---|---|---|
远程图片 | http://, https:// | HttpURLConnection 或者参考 使用其他网络加载方案 |
本地文件 | file:// | FileInputStream |
Content provider | content:// | ContentResolver |
asset目录下的资源 | asset:// | AssetManager |
res目录下的资源 | res:// | Resources.openRawResource |
Uri中指定图片数据 | data:mime/type;base64, | 数据类型必须符合 rfc2397规定 (仅支持 UTF-8) |
<com.facebook.drawee.view.SimpleDraweeView
xmlns:fresco="http://schemas.android.com/apk/res-auto"
android:id="@+id/sdv_head_image"
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"
fresco:viewAspectRatio="1.0"
/>
解释下属性的意思:
属性 | 作用说明 |
---|---|
fadeDuration | 图片渐渐显示的时间,单位毫秒 |
actualImageScaleType | 图片的缩放类型,其值有:none, center,centerCrop,focusCrop,centerInside,fitCenter,fitStart,fitEnd,fitXY。Fresco建议使用focusCrop,它和centerCrop类似,centerCrop是居中后裁切掉超多视图容器的部分图片,而focusCrop可以指定一个焦点后进行裁切。 |
placeholderImage | 占位符预加载图片,可以是图片、颜色值 |
placeholderImageScaleType | 占位符图片的缩放类型,其值同actualImageScaleType一样。 |
failureImage | 图片加载失败后显示的错误图片 |
failureImageScaleType | 错误图片的缩放类型 |
retryImage | 图片加载失败后,显示的重试加载的图片,重试4次后才形式错误的图片 |
retryImageScaleType | 重试图片的缩放类型 |
progressBarImage | 正在加载图片时的加载进度条图片 |
progressBarImageScaleType | 加载进度条图片的缩放类型 |
progressBarAutoRotateInterval | 加载进度条自动旋转的间隔时间,单位毫秒 |
backgroundImage | 背景图片,最先绘制的图片 |
overlayImage | 覆盖在加载完成后图片上的叠加图片 |
pressedStateOverlayImage | 按压状态下的叠加图片 |
roundAsCircle | 是否为圆形图片 |
roundedCornerRadius | 圆角图片时候,圆角的半径大小 |
roundTopLeft | 左上角是否为圆角 |
roundTopRight | 右上角是否为圆角 |
roundBottomLeft | 左下角是否为圆角 |
roundBottomRight | 右下角是否为圆角 |
roundWithOverlayColor | 圆角或圆形图叠加的颜色,只能是颜色 |
roundingBorderWidth | 圆角或圆形图边框的宽度 |
roundingBorderColor | 圆角或圆形图边框的颜色 |
viewAspectRatio | 控件的宽高比,因为fresco不支持wrap_content, 所以有些需求可以layout_width为具体的值,layout_height为wrap_content,同时设置这个属性的比例值,否则无效不显示 |
好了xml中的属性配置就介绍完了,相信有些小伙伴会有个疑问,我第一次使用SimpleDraweeView,怎么会知道它会有这么些自定义的属性可以使用呢!
public class SimpleDraweeView extends GenericDraweeView {
// ...省略一些代码
public SimpleDraweeView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, @Nullable AttributeSet attrs) {
if (isInEditMode()) {
return;
}
Preconditions.checkNotNull(
sDraweeControllerBuilderSupplier,
"SimpleDraweeView was not initialized!");
mSimpleDraweeControllerBuilder = sDraweeControllerBuilderSupplier.get();
if (attrs != null) {
TypedArray gdhAttrs = context.obtainStyledAttributes(
attrs,
R.styleable.SimpleDraweeView);
try {
if (gdhAttrs.hasValue(R.styleable.SimpleDraweeView_actualImageUri)) {
setImageURI(Uri.parse(gdhAttrs.getString(R.styleable.SimpleDraweeView_actualImageUri)), null);
}
} finally {
gdhAttrs.recycle();
}
}
}
}
很容易就找到了声明属性的属性集:R.styleable.SimpleDraweeView,接下来我们需要找到声明这个属性集合的values.xml,如下:
看这就找到了xml里面配置的所有属性,是不是很简单!细心的小伙伴会发现R.styleable.SimpleDraweeView下定义的属性就一个actualImageUri,其它的属性是定义在R.styleable.GenericDraweeHierarchy下的,不禁心想博主你特么在逗我么!我能说冤枉么(55555~~~),别急小伙伴们,下面就来分析问你解惑,fresco是在哪里为我们加载和解析了我们xml配置的那些属性呢。
看SimpleDraweeView的源码会发现,它的父类是GenericDraweeView,我们来跟踪这个父类:
public class GenericDraweeView extends DraweeView<GenericDraweeHierarchy> {
// ...省略一些代码
public GenericDraweeView(Context context, AttributeSet attrs) {
super(context, attrs);
inflateHierarchy(context, attrs);
}
protected void inflateHierarchy(Context context, @Nullable AttributeSet attrs) {
GenericDraweeHierarchyBuilder builder =
GenericDraweeHierarchyInflater.inflateBuilder(context, attrs);
setAspectRatio(builder.getDesiredAspectRatio());
setHierarchy(builder.build());
}
}
可以看到属性的构建交给了GenericDraweeHierarchyInflater.inflateBuilder方法,继续跟踪下去:
public static GenericDraweeHierarchyBuilder inflateBuilder(
Context context,
@Nullable AttributeSet attrs) {
Resources resources = context.getResources();
GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(resources);
return updateBuilder(builder, context, attrs);
}
继续跟踪updateBuilder方法:
public static GenericDraweeHierarchyBuilder updateBuilder(
GenericDraweeHierarchyBuilder builder,
Context context,
@Nullable AttributeSet attrs) {
// ...省略了一些代码
if (attrs != null) {
TypedArray gdhAttrs = context.obtainStyledAttributes(
attrs,
R.styleable.GenericDraweeHierarchy);
try {
final int indexCount = gdhAttrs.getIndexCount();
for (int i = 0; i < indexCount; i++) {
final int attr = gdhAttrs.getIndex(i);
// most popular ones first
if (attr == R.styleable.GenericDraweeHierarchy_actualImageScaleType) {
builder.setActualImageScaleType(getScaleTypeFromXml(gdhAttrs, attr));
} else if (attr == R.styleable.GenericDraweeHierarchy_placeholderImage) {
builder.setPlaceholderImage(getDrawable(context, gdhAttrs, attr));
}
}
}
}
终于找到梦寐以求的R.styleable.GenericDraweeHierarchy,然后是一个个去遍历获取每个属性的值,设置给GenericDraweeHierarchyBuilder构造器。调用触发是在SimpleDraweeView的构造器里的super(context, attrs),经过层层调用由updateBuilder完成了所有属性的加载和解析赋值。
文章有点长,感谢耐心看完!fresco基本的加载图片的使用就是这样。后面我会陆续写一些重构和优化项目时,使用fresco的一些特性,和Glide框架的对比,优缺点等等内容。希望此文能带给小伙伴们些许帮助。谢谢~