目录
1.AppGlideModule(用于Application)
2.LibraryGlideModule(用于Library)
概述
Picasso:使用简单,代码简洁,依赖包最小;功能简单,性能较差,没有实现本地缓存。
Glide: 支持Gif WebP Video,生命周期继承,高效缓存策略(支持Memory和Disk缓存;支持多种规格图片缓存;内存开销小,默认RGB_565);方法多,比Picasso复杂,比Fresco简单,性能优于Picasso,劣于Fresco。
Fresco(Facebook): 大大减少OOM,底层使用 C++技术解决图片缓存问题,性能优,几乎全部功能都能在 xml 上定制;用法复杂,依赖包(2M ~ 3M)
Picasso And Glide都可以实现一行代码解决加载图片!引入第三方Lib够用即可(个人愚见),综合考虑性能、易用性、包大小等方面,个人推荐Glide。
Glide内存消耗:
Glide默认Bitmap格式是RGB_565,Picasso默认Bitmap格式是ARGB_8888,Glide加载的图片质量要差于Picasso,而内存开销要小一半。
更改Glide默认格式为ARGB_8888,更改之后,Picasso的内存开销仍然远大于Glide
Glide.with(this)
.load("http://url/cover.jpg")
.apply(new RequestOptions().format(DecodeFormat.PREFER_ARGB_8888))
.into(imageView);
更改之后的内存消耗对比图:Picasso的内存开销仍然远大于Glide,原因在于Picasso是加载了全尺寸的图片到内存,然后让GPU来实时重绘大小。而Glide加载的大小和ImageView的大小是一致的(可以自动计算出任意情况下的ImageView大小),因此更小。
原理浅析
with:创建RequestManager
Glide#with() 方法会将 RequestManager 的创建委托给 RequestManagerRetriever,RequestManagerRetriever 为单例类,调用 get(Context) 创建 RequestManager。
Glide.with()方法可以接收Context、Activity、Fragment或者当前应用程序的ApplicationContext类型参数。注意with()方法中传入的实例会决定Glide加载图片的生命周期,如果传入的是Activity或者Fragment的实例,那么当这个Activity或Fragment被销毁的时候,图片加载也会停止。如果传入的是ApplicationContext,那么只有当应用程序被杀掉的时候,图片加载才会停止。
一个Activity / Fragment对应一个RequestManager(单例,生命周期);子线程发起或者Application发起,对应全局RequestManager(单例,生命周期)。
load:创建RequestBuilder
load() 方法返回一个RequestBuilder用于指定待加载的图片资源。Glide支持加载各种各样的图片资源,包括网络图片、本地图片、应用资源、二进制流、Uri对象等等。因此load()方法也有很多个方法重载:
RequestBuilder<Drawable> load(@Nullable Bitmap bitmap);
RequestBuilder<Drawable> load(@Nullable Drawable drawable);
RequestBuilder<Drawable> load(@Nullable String string);
// 加载Uri对象
RequestBuilder<Drawable> load(@Nullable Uri uri);
// 加载本地图片
RequestBuilder<Drawable> load(@Nullable File file);
// 加载应用资源
RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId);
// 加载网络URL
RequestBuilder<Drawable> load(@Nullable URL url);
// 加载二进制流
RequestBuilder<Drawable> load(@Nullable byte[] model);
RequestBuilder<Drawable> load(@Nullable Object model);
RequestBuilder<File> downloadOnly();
RequestBuilder<File> download(@Nullable Object model);
into:创建Request
返回一个Target接口类型,该方法最终创建Request并启动,同时也指定了接收请求结果的 Target,into(ImageView) 作为辅助方法,接受一个 ImageView 参数并为其请求的资源类型包装了一个合适的 ImageViewTarget。
重要类:
Glide关键三步:with,load,into
Glide.with(this)
.load("http://url/cover.jpg")
.into(imageView);
1.Glide:
单例类,提供配置Encoder、Decoder、ModelLoader、Pool入口,提供创建 RequestManager 的接口(Glide#with() 方法)
2.GlideBuilder:
用于创建 Glide 实例的类,其中包含了很多个 get/set 方法,例如设置 BitmapPool、MemoryCache(内存缓存)、DiskCacheFactory(磁盘缓存)等,最终通过这些设置调用 build 方法构建 Glide。
3.RequestManagerRetriever:
根据with方法传入的参数类型,具体实施创建RequestManager操作。
(1)RequestManager fragmentGet(Context context, android.app.FragmentManager fm);
对应参数类型为Activity或者android.app.Fragment
(2)RequestManager supportFragmentGet(Context context, android.support.v4.app.FragmentManager fm);
对应参数类型为android.support.v4.app.Fragment
(3)RequestManager getApplicationManager(Context context);
子线程中调用with方法,或者传入参数为ApplicationContext
4.RequestManager:
创建 RequestBuilder(通过load方法);通过生命周期管理请求的启动结束等。
5.RequestBuilder:
构建请求,例如设置 RequestOption、缩略图、加载失败占位图等等。RequestManager 中 load 重载方法,对应 RequestBuilder 中的重载 load 方法,一般来说 load 方法之后就是调用 into 方法设置 ImageView 或者 Target,into 方法中最后会创建 Request,并启动。
6.Request:
接口类,定义了对请求的开始、结束、状态获取、回收等操作,Request中不仅包含基本的信息,还负责管理请求。
7.SingleRequest:
Request实现类,负责执行请求并将结果反映到 Target 上。
当我们使用 Glide 加载图片时,会先根据 Target 类型创建不同的 Target,然后 RequestBuilder 将这个 target 当做参数创建 Request 对象,Request 与 Target 就是这样关联起来的。这里就会先创建一个包含 Target 的 SingleRequest 对象。考虑到性能问题,可能会连续创建很多个 SingleRequest 对象,所以使用了对象池来做缓存。
我们经常在 Activity#onCreate 方法中直接使用 Glide 方法,但此时的图片大小还未确定,所以调用 Request#begin 时并不会直接发起请求,而是等待 ImageView 初始化完成,对于 ViewTarget 以及其子类来说,会注册View 的 OnPreDrawListener 事件,等待 View 初始化完成后就调用 SingleRequest#onSizeReady 方法,这个方法里就会开始加载图片了。
onSizeReady 方法并不会去直接加载图片,而是调用了 Engine#load 方法加载,这个方法差不多有二十个参数,所以 onSizeReady 方法算是用来构建参数列表并且调用 Engine#load 方法的。
clear 方法用于停止并清除请求,主要就是从 Engine 中移除掉这个任务以及回调接口。
8.Target:
目标类,代表一个可被 Glide 加载并且具有生命周期的资源,Target 负责展示占位符,加载资源到View,并为每个请求决定合适的尺寸。在调用RequestBuilder#into 方法时会根据传入参数创建对应类型的 Target 实现类。
Glide 默认提供了用于放在 ImageView 上的 ImageViewTarget(以及其各种子类)、放在 AppWidget 上的 AppWidgetTarget、用于同步加载图片的 FutureTarget(只有一个实现类:RequestFutureTarget)等等,
(1)ImageViewTarget(继承自ViewTarget —> BaseTarget)
负责把图片资源加载到ImageView上,图片数据加载完成后会回调其中的 onResourceReady 方法,第一步是将图片设置给 ImageView,第二部是判断是否需要使用动画,需要的话就执行动画。
(2)RequestFutureTarget
调用 RequestBuilder#submit 将会返回一个 FutureTarget,调用 get 方法即可获取到加载的资源对象。
try {
Drawable drawable = Glide.with(this)
.load("http://url/cover.jpg")
.apply(new RequestOptions().format(DecodeFormat.PREFER_ARGB_8888))
.override(Target.SIZE_ORIGINAL)
.submit()
.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
(3)AppWidgetTarget:设置图片到RemoteView上。
(4)NotificationTarget:设置图片到Notification的RemoteView上。
9.AppGlideModule
继承自LibraryGlideModule(实现了RegistersComponents接口) 同时实现了AppliesOptions接口。用于延迟设置Glide相关参数。通过这个设置可以保证在 Glide 单例类初始时,所有请求发起之前应用到 Glide。
(1)RegistersComponents接口:用于管理注册 ModelLoader、Encoder、Decoder等组件的,Glide默认使用HttpUrlConnection,可以改为使用OkHttp
(2)AppliesOptions接口 :通过GlideBuilder参数,设置线程池、设置 BitmapPool/ArrayPoll等。
10.MemorySizeCalculator
用来计算 BitmapPool 、ArrayPool 以及 MemoryCache 大小。
缓存机制
Glide的缓存设计可以说是非常先进的,考虑的场景也很周全。在缓存这一功能上,Glide又将它分成了两个模块,一个是内存缓存,一个是硬盘缓存。内存缓存的主要作用是防止应用重复将图片数据读取到内存当中,而硬盘缓存的主要作用是防止应用重复从网络或其他地方重复下载和读取数据,二者相互结合。
Glide在实例化时,会初始化三个与缓存相关的类对象以及一个用于计算缓存大小的类对象:
// 根据当前机器参数计算需要设置的缓存大小
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
// Bitmap池
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
// 创建内存缓存
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
// 创建硬盘缓存
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
1.Glide一级缓存:ActiveResources
弱引用存储当前正在活动的资源,即正在使用的资源。生命周期短,没有大小限制,内部基于Map<Key, WeakReference<EngineResource<?>>> 实现存储。
从Memory、Disk、File、network获取的图片资源,使用时(例如,在页面显示)存储到ActiveResources,使用完将缓存图片从activeResources中移除,然后再将它put到LruResourceCache当中。这样也就实现了正在使用中的图片使用弱引用来进行缓存,不在使用中的图片使用LruCache来进行缓存的功能。
2.Glide二级缓存:MemoryCache
内存缓存实现类为 LruResourceCache,继承了 LruCache,LruCache基于LinkedHashMap实现LRU存储策略。LinkedHashMap是Java提供的一个很好的用来实现 LRU 算法的数据结构,其基于 HashMap 实现,同时又将 HashMap 中的 Entity 串成了一个双向链表。
LruResourceCache 中提供了一个 ResourceRemovedListener 接口,当有资源从 MemoryCache 中被移除时会回调其中的方法,Engine 中接收到这个消息后就会进行 Bitmap 的回收操作。
Glide默认开启内存缓存,只要在它还没从内存中被清除之前,下次使用Glide再加载这张图片都会直接从内存当中读取,而不用重新从网络或硬盘上读取了,这样无疑就可以大幅度提升图片的加载效率。比方说你在一个RecyclerView当中反复上下滑动,RecyclerView中只要是Glide加载过的图片都可以直接从内存当中迅速读取并展示出来,从而大大提升了用户体验。
Glide.with(this)
.load(url)
// 关闭内存缓存
.skipMemoryCache(true)
.into(imageView);
3.Glide三级缓存:DiskLruCache
磁盘缓存路径默认为 Context#getCacheDir() 下面的 image_manager_disk_cache 文件夹,默认缓存大小为 250MB。
磁盘缓存实现类DiskLruCacheWrapper实现了 DiskCache 接口:
File get(Key key);
void put(Key key, Writer writer);
void delete(Key key);
void clear();
调用diskCacheStrategy()方法并传入DiskCacheStrategy.NONE,就可以禁用掉Glide的硬盘缓存功能了。
Glide.with(this)