Android图片加载之Fresco应用分析



一、引言

在我们平时加载图片(不管是下载还是加载本地图片…..)的时候,我们经常会遇到这样一个需求,那就是当图片正在加载时应该呈现正在加载时的图像,当图片加载失败时应该呈现图片加载时的图像,当我们重新加载这张图片时,应该呈现重试时图像,直到这张图片加载完成。记得最开始是用Asynctack写的,后来用上了Universal Image Loader,UIL用了几年,这东东很久没更新了,近两年选用Fresco或者Glide。

Glide因为其体积小、缓存机制强大等优点,受到了广大程序员的青睐;Fresco虽然体积比较大,缓存机制也没有Glide强大,但它胜在拥有一些炫酷的效果(进度条、淡入效果)等,也有很多人在使用。Fresco这个框架出的有一阵子了,也是现在非常火的一款图片加载框架,Fresco中实现了各种加载过程以及加载后的图片绘制,整体还是很强大的。听说内部实现的挺牛逼的,虽然自己还没研究原理,不过先介绍下基本功能的使用,感受了一下这个框架的强大之处。

二、Fresco介绍

Fresco是美国的Facebook公司出品的,功能全,代码健壮,是一款开源框架,号称是目前最强的Android图片加载库,在内存方面的表现极为优秀。看看有哪些优缺点:

优点:

  • 使用简便,学习成本低
  • 十分强大,使用起来非常流畅,内存管理不用愁,不用担心OOM
  • 自带加载时淡入效果,开发起来不费劲
  • 图片加载时可在布局中直接设置加载动画等等,代码量大大减少

缺点:

  • 必须使用fresco自定义的控件,如果需求更换,想要更换其他图片加载框架会有一定的麻烦,比如必须要改布局
  • 方法数太多,多达近4k方法,整项目方法数超过65k,不得不分包,而且打包之后整个项目整整多了3M,确实大得很
  • 必须全套使用fresco的图片加载

总之,虽然有缺点,但在我的印象中,fresco就是图片加载处理的专业管家,几乎一切繁杂的事情它都帮你处理妥当,用起来简直贴心。

GitHub : Fresco

也有不少热心的开发者基于Fresco做了一些封装并开放给他人使用:

GitHub : fresco-helper

三、Fresco应用

Fresco的用处很强大,这里只介绍一些最基本的应用,若要了解更多,可以去官网或Fresco中文网看看。



3.1 引入依赖

在AS的build.gradle中的dependencies下直接添加如下代码:

//Fresco
implementation 'com.facebook.fresco:fresco:+'  //V1.10.0

同时可以根据项目需求选择添加如下依赖:


// 支持 GIF 动图,需要添加 compile 'com.facebook.fresco:animated-gif:+' // 支持 WebP (静态图+动图),需要添加 compile 'com.facebook.fresco:animated-webp:+' compile 'com.facebook.fresco:webpsupport:+' // 仅支持 WebP 静态图,需要添加 compile 'com.facebook.fresco:webpsupport:+'

3.2 初始化

创建Application类的onCreate方法里初始包:Fresco.initialize(this);

public class FrescoApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        //初始化fresco包
        Fresco.initialize(this);
    }
}

3.3 布局

用Fresco不爽的地方就是什么都要按照人家的要求来,如控件名字,命名空间都要听人家的。Fresco不同于其它几个图片加载库,它是基于自定义控件的,要使用控件SimpleDraweeView来显示图片:

<com.facebook.drawee.view.SimpleDraweeView
        android:layout_width="400dp"
        android:layout_height="match_parent"
        android:id="@+id/imageDraweeView"
        fresco:placeholderImage="@mipmap/ic_launcher"/>

另外,Fresco还提供丰富的可配置功能,当图片加载失败之后我们也是需要有默认图片去进行填充的。实现起来也比较简单:

<com.facebook.drawee.view.SimpleDraweeView
    android:id="@+id/failureImageDraweeView"
    android:layout_width="40dp"
    android:layout_height="40dp"
    android:layout_margin="5dp"
    fresco:failureImage="@drawable/error"
    fresco:failureImageScaleType="centerInside"/>

3.4 UI使用

在activity中加载图片显示如下:

    private SimpleDraweeView sdv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Fresco.initialize(this);//Fresco的初始化,必须调用在setContentView之前
        setContentView(R.layout.activity_main);

        sdv = (SimpleDraweeView) findViewById(R.id.imageDraweeView);

        Uri uri =  Uri.parse("http://xmamiga.com/uploadImages/test.jpg");

        DraweeController controller = Fresco.newDraweeControllerBuilder()
                .setUri(uri)
                .setAutoPlayAnimations(true)
                .build();
        sdv.setController(controller);
    }


   //点击事件加载网络图片代码
    public void loadInternetImage(View view){
        Uri uri = Uri.parse("http://xmamiga.com/uploadImages/test.jpg");
        SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
        draweeView.setImageURI(uri);
    }

很简单吧,剩下的,Fresco会替你完成,比如:显示占位图直到加载完成;下载图片;缓存图片;图片不再显示时,从内存中移除等等等等。当然,有时候需要加载很多图片,这个时候如果网络情况不是很好的情况下,一直显示占位图,体验也不是很好,我们可以先显示一个低分辨率的图片,等到大图加载完成之后,再显示真正的图片。

    DraweeController controller = Fresco.newDraweeControllerBuilder()
        .setLowResImageRequest(ImageRequest.fromUri(lowResUri))
        .setImageRequest(ImageRequest.fromUri(highResUri))
        .setOldController(draweeView.getController())
        .build();
    draweeView.setController(controller);

如果想监听加载的过程,就加一个ControllerListen,在ControllerListener中可以对下载过程进行监听。代码如下

    //写一个监听器 监听图片加载
    ControllerListener listener = new BaseControllerListener(){

        /**
         * 当图片加载成功时会执行的方法
         * @param id
         * @param imageInfo
         * @param animatable
         */
        @Override
        public void onFinalImageSet(String id, Object imageInfo, Animatable animatable) {
            super.onFinalImageSet(id, imageInfo, animatable);
        }

        /**
         * 图片加载失败时调用的方法
         * @param id
         * @param throwable
         */
        @Override
        public void onFailure(String id, Throwable throwable) {
            super.onFailure(id, throwable);
        }

        /**
         *  如果图片使用渐进式,这个方法将会被回调
         * @param id
         * @param throwable
         */
        @Override
        public void onIntermediateImageFailed(String id, Throwable throwable) {
            super.onIntermediateImageFailed(id, throwable);
        }
    };

    DraweeController controller = Fresco.newDraweeControllerBuilder()
            .setUri(uri)
            .setAutoPlayAnimations(true)
            .setControllerListener(listener)
            .build();

    sdv.setController(controller);

3.5 更多属性

drawee支持如下属性:

    <com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/id_main_sdv"
        fresco:actualImageScaleType="focusCrop"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp" //加载得到的图片的缩放类型 
        fresco:fadeDuration="1000" //进度条,占位图片消失,加载图片展现的时间间隔 
        fresco:failureImage="@drawable/imgbg" //加载失败之后显示的图片 
        fresco:failureImageScaleType="centerInside" //图片缩放类型
        fresco:placeholderImage="@drawable/imgbg" //占位图片(未加载之前显示的图片) 
        fresco:placeholderImageScaleType="fitCenter" 
        fresco:progressBarAutoRotateInterval="1000" //加载进度条图片旋转周期
        fresco:progressBarImage="@drawable/progress_bar" //加载进度条图片 
        fresco:progressBarImageScaleType="centerInside" 
        fresco:retryImage="@mipmap/ic_launcher" //提示重新加载的图片资源
        fresco:retryImageScaleType="centerCrop" 
        fresco:backgroundImage="@color/colorWhite" //背景图片 
        fresco:roundAsCircle="false" //是否要将图片剪切成圆形 
        fresco:viewAspectRatio="1" //图片宽高比
        fresco:overlayImage="@drawable/overlay" //在图片上方覆盖一个图片资源 
        fresco:pressedStateOverlayImage="@color/colorBlack" 
        fresco:roundedCornerRadius="20dp" //圆角角度, 
        fresco:roundTopLeft="true" //设置哪个角需要变成圆角
        fresco:roundTopRight="false" 
        fresco:roundBottomLeft="false" 
        fresco:roundBottomRight="true" 
        fresco:roundWithOverlayColor="@color/colorWhite" //圆角部分填充色 
        fresco:roundingBorderWidth="2dp" //边框宽度
        fresco:roundingBorderColor="@color/colorBlack" //边框填充色 
    />

3.6 支持的URI

Fresco 支持许多URI格式。但不支持相对路径的URI,所有的URI都必须是绝对路径,并且带上该URI的scheme。

res 示例:

Uri uri = Uri.parse("res://包名(实际可以是任何字符串甚至留空)/" + R.drawable.ic_launcher);

3.7 注意事项

  • SimpleDraweeView控件的宽高不能设置为wrap_content,只能是match_parent、具体值或使用viewAspectRatio属性设置宽高比
  • 如果在一个页面中加载多张图片,不要将SimpleDraweeView直接放在ScollView中,建议使用RecyclerView、ListView或GridView,因为ScrollView中的SimpleDraweeView会持有内存直到Activity或Fragment销毁
  • 使用SimpleDraweeView时,不要使用imageView中有、View中没有的属性

四、总结

如果只是想对网络图片进行加载以及显示一些特效,那么SimpleDraweeView就基本上可以满足需求了;Fresco是使用Image Pipeline来实现对图片的管理和获取的,SimpleDraweeView是对ImageView的一个封装,并且内部使用了Image Pipeline管理图片的思想和方式。总之,Fresco太强大太优秀了,上面所描述的只是沧海一栗。


参考