概述
随着摄影技术的不断发展和普及,我们的应用程序大多数都具有显示图片这样的需求,在Android
移动设备上,由于平台计算能力的差异、设备硬件的差异,我们很难将图片在不同的设备上完美地显示,经常出现的情况是一份展示图片的代码在高性能手机上运行很流畅,但是部署到硬件稍微差劲一点的设备上就会出现卡顿、占用内存比例过大、甚至应用崩溃的现象。所以我们最好能寻求一种框架,可以帮我们处理不同计算能力平台上的差异性,对图片的显示进行内存等方面的优化,使我们的应用可以在不同的设备上完美地显示图片,Fresco
就是这样一个强大的图片加载组件,它是Facebook
开发及开源的一个框架,通过Fresco
,你不需要关心图片的加载和显示这些琐碎的事情,只需要告诉它要以何种方式显示图片即可,剩下的事情都交给它,真正体现了代码重用、避免重复造轮子的原则。
本文将带大家逐步入门Fresco
的使用和基本特性,带大家体会使用Fresco
的优点,本文不涉及过多Fresco
源码部分及底层原理部分的内容,宗旨是先带大家入门,后面我会陆续推出关于Frecsco
源码分析的文章,力争带大家深入Fresco
的底层原理。
Fresco初步尝鲜
本节带大家一步一步通过Fresco
显示一张网络图片,最终效果是这样的:
即在我们的手机上通过Fresco
显示一张小熊熊的照片。
引入Fresco
Fresco
官方github
地址:https://github.com/facebook/fresco
使用所有开源库的第一步一定是引入相关依赖,将下面这行代码加入到Android
项目的build.gradle(app)
文件中即可:
implementation 'com.facebook.fresco:fresco:2.0.0'
上面的是Fresco
的基本依赖,Fresco
还提供了其他的一些高级功能,比如展示Gif
之类的,为了控制库的大小,Fresco
将这些子功能分开到其他子库中,大家按需添加即可:
dependencies {
// 在 API < 14 上的机器支持 WebP 时,需要添加
compile 'com.facebook.fresco:animated-base-support:2.0.0'
// 如果要显示 GIF 动图,需要添加
compile 'com.facebook.fresco:animated-gif:2.0.0'
// 支持显示 WebP (静态图+动图)
compile 'com.facebook.fresco:animated-webp:2.0.0'
compile 'com.facebook.fresco:webpsupport:2.0.0'
// 仅支持显示 WebP 静态图
compile 'com.facebook.fresco:webpsupport:2.0.0'
}
使用Fresco
在使用Fresco
之前,我们需要在我们的应用中初始化Fresco
,初始化的代码为:
Fresco.initialize(this);
那么这行代码应该写在我们应用的什么位置?Fresco
官方的说法是,Fresco
的初始化工作在整个应用的生命周期只需要初始化一次,多次初始化没有任何意义,联想一下,什么地方的代码在整个应用的生命周期只会执行一次?答案是应用Application
的onCreate()
方法啊,所以我们新建一个Application
并初始化Fresco
:
package edu.nuaa.aiexception.learnopensource;
import android.app.Application;
import com.facebook.drawee.backends.pipeline.Fresco;
/**
* @author dmrfcoder
* @date 2019-06-30
*/
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Fresco.initialize(this);
}
}
不要忘记在项目的Manifest
文件中指定Application
:
<application
android:name=".MyApplication"
...
/>
至此,Fresco
已经初始化好了。
我们需要从网络加载图片,所以必须要在Manifest
文件中为应用加上网络请求的权限:
<uses-permission android:name="android.permission.INTERNET"/>
然后我们需要在界面上放置一个Fresco
的控件,因为我们仅仅是想简单下载一张网络图片,在下载完成之前,显示一张占位图,可以使用最简单的 SimpleDraweeView
:
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/fresco_view"
android:layout_width="130dp"
android:layout_height="130dp"
android:layout_centerHorizontal="true"
app:placeholderImage="@mipmap/ic_launcher" />
然后我们就可以在Java
代码中为SimpleDraweeView
设置图像了:
public void loadPicFromNet() {
Uri uri = Uri.parse("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2133031131,1997928181&fm=26&gp=0.jpg");
imageView.setImageURI(uri);
}
然后直接运行代码,这里笔者运行的时候遇到了如下报错:
java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/core/util/Pools$SynchronizedPool;
解决方案是在你项目的gradle.properties文件中加入如下两行即可:
android.enableJetifier = true
android.useAndroidX=true
希望可以帮大家避坑。
然后运行代码,最终效果如下:
可以看到,在图片从网络上下载下来之前,会展示我们设置的占位图,当图片下载完成之后,Fresco
才会将下载下来的图片显示到屏幕上,这样用户体验会更好。
总结一下,在上例中,Fresco
替我们完成的事情有:
剩下的,Fresco
会替你完成:
- 显示占位图直到加载完成;
- 下载图片;
- 缓存图片;
- 图片不再显示时,从内存中移除;
- …
可以看到,通过Fresco
的使用,我们不再需要关心图片的下载、缓存、清除等一系列事件,只需要关注图片的显示即可,将我们的意图描述给Fresco
,剩下的事情都由它来完成,是不是很方便啊~
Fresco中的几个关键概念
Drawees
Drawees
的作用是在图片加载完成前显示占位图,加载成功后自动替换为目标图片。当图片不再显示在屏幕上时,它会及时地释放内存和空间占用。
Drawees
由三个元素组成,有点像MVC
模式,分别是:
DraweeView
继承于 View
, 负责图片的显示。
一般情况下,使用 SimpleDraweeView
即可。 你可以在 XML
或者在 Java
代码中使用它,通过 setImageUri
给它设置一个 URI
来使用,上面的实例就是借助SimpleDraweeView
来实现的网络图片显示。
我们可以设置DraweeView
的XML
属性来达到各式各样的效果,这一点我会在下面详细讲解。
DraweeHierarchy
DraweeHierarchy
用于组织和维护最终绘制和呈现的Drawable
对象,相当于MVC
中的M
。
你可以通过它来在Java
代码中自定义图片的展示。
DraweeController
DraweeController
负责和 image loader
交互( Fresco
中默认为 image pipeline
, 当然你也可以指定别的),可以创建一个这个类的实例,来实现对所要显示的图片做更多的控制。
如果你还需要对Uri
加载到的图片做一些额外的处理,那么你会需要这个类的。
DraweeControllerBuilder
DraweeControllers
由 DraweeControllerBuilder
采用 Builder
模式创建,创建之后,不可修改。
Listeners
使用 ControllerListener
的一个场景就是设置一个Listener
监听图片的下载。
The Image Pipeline
Fresco
的 Image Pipeline
负责图片的获取和管理。图片可以来自远程服务器,本地文件,或者Content Provider
,本地资源。压缩后的文件缓存在本地存储中,Bitmap
数据缓存在内存中。
在5.0系统以下,Image Pipeline
使用 pinned purgeables
将Bitmap
数据避开Java
堆内存,存在ashmem
中。这要求图片不使用时,要显式地释放内存。
SimpleDraweeView
自动处理了这个释放过程,所以没有特殊情况,尽量使用SimpleDraweeView
,在特殊的场合,如果有需要,也可以直接控制Image Pipeline
。
Fresco都支持哪些类型的URL?
Fresco
支持许多URI
格式。
特别注意:Fresco
不支持 相对路径的URI
. 所有的 URI
都必须是绝对路径,并且带上该 URI
的 scheme
。
如下:
类型 | 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) |
res 示例:
Uri uri = Uri.parse("res://包名(实际可以是任何字符串甚至留空)/" + R.drawable.ic_launcher);
注意,只有图片资源才能使用在Image pipeline
中,比如(PNG
)。其他资源类型,比如字符串,或者XML
Drawable
在Image pipeline
中没有意义。所以加载的资源不支持这些类型。
像ShapeDrawable
这样声明在XML
中的drawable
可能引起困惑。注意到这毕竟不是图片。如果想把这样的drawable
作为图像显示,那么把这个drawable
设置为占位图,然后把URI
设置为null
。
总结
本文我们通过一个实例带大家过了一遍Fresco
的基本使用流程,并且对Fresco
中的基本概念做了简单介绍,由于Fresco Drawee
和Fresco Image PipelineFresco
比较重要,且知识点比较繁杂,我会在最近分别就Drawee
和 Image PipelineFresco
做详细介绍,届时将向大家展示一些Fresco
的高级用法,欢迎大家关注。