Glide框架小结

一、四大图片缓存框架基本信息

这里写图片描述
Universal Image Loader
一个强大的图片加载库,包含各种各样的配置,最老牌,使用也最广泛。
优点:
1)可以根据控件(ImageView)的大小对Bitmap进行裁剪,减少内存占用
2)支持图片下载过程的监听
3)可在View滚动中暂停图片加载,通过PauseOnScrollListener接口可在View滚动中暂停图片加载
4)在网络速度较慢时,还可以对图片进行加载
缺点:没有对本地文件压缩处理的相关API方法以及默认都是Src模式设置图片,没有针对Background属性开放的api

Glide
Google推荐的图片加载库,专注于流畅的滚动。
1.优点
1)使用RGB_565,内存占用比Picasso小一些。
2)图片展示和页面的生命周期一致(对context有类型要求)
3)相比Picasso,Glide在缓存策略和加载GIF方面略胜一筹
减少了缓存文件的大小。Picasso和Glide在磁盘缓存策略上有很大的不同。Picasso缓存的是全尺寸的,而Glide缓存的是跟ImageView尺寸相同的。 这样在下次显示的时候不需要重新调整大小,显示的会更快。
4)它还支持video,Gif,SVG格式,支持缩略图请求,旨在打造更好的列表图片滑动体验
5)当列表在滑动的时候,调用pauseRequests()取消请求,滑动停止时,调用resumeRequests()恢复请求。如:Glide.with(context).pauseRequests()。支持当你想清除掉所有的图片加载请求
6)同时因为Glide和Activity/Fragment的生命周期是一致的,因此gif的动画也会自动的随着Activity/Fragment的状态暂停、重放。
2.缺点
1)Glide 功能强大,但代码量大、流转复杂。

fresco
Facebook开源的图片缓存,两个内存缓存和Native缓存构成了三级缓存
1.优点
1)Fresco 的 Drawees 设计,带来一些有用的特性:EXIF缩略图,自定义占位图等
2)支持模糊渐进形式展示图片(类似webView)
3)能够根据View的展示状态控制网络请求和图片解析的状态(在页面不可见时停止对图片的网络请求和解析操作,在页面可见时恢复操作)
4)对多帧动画图片支持更好
5)对外提供清除缓存的方法
2.缺点
1)体积较大,集成后增大apk体积
2)需要使用特定的view,需要xml支持
3)默认使用ARGB_8888
Picasso
picasso是Square公司开源的一个Android图形缓存库,可以实现图片下载和缓存功能
(1) 自带统计监控功能
支持图片缓存使用的监控,包括缓存命中率、已使用内存大小、节省的流量等。
(2) 支持优先级处理
每次任务调度前会选择优先级高的任务,比如 App 页面中 Banner 的优先级高于 Icon 时就很适用。
(3) 支持延迟到图片尺寸计算完成加载
(4) 支持飞行模式、并发线程数根据网络类型而变
手机切换到飞行模式或网络类型变换时会自动调整线程池最大并发数,比如 wifi 最大并发为 4, 4g 为 3,3g 为 2。
这里 Picasso 根据网络类型来决定最大并发数,而不是 CPU 核数。
(5) “无”本地缓存
无”本地缓存,不是说没有本地缓存,而是 Picasso 自己没有实现,交给了 Square 的另外一个网络库 okhttp 去实现,这样的好处是可以通过请求 Response Header 中的 Cache-Control 及 Expired 控制图片的过期时间
(6)Picasso 代码虽然只在一个包下,没有严格的包区分,但代码简单、逻辑清晰

2.缺点
1)Picasso的方式则因为需要在显示之前重新调整大小而导致一些延迟,Glide加载显示更快。
2)默认使用ARGB_8888

二、Glide使用简介

2.1、操作图片基本用法如:
Glide需要项目中添加依赖:
dependencies {
compile ‘com.github.bumptech.glide:glide:3.7.0’
compile ‘com.android.support:support-v4:24.0.0’
}
基本使用模型:
这里写图片描述
Glide 的with() 传入值Context,Activity,Fragment

load() 可以传入需要显示图片的url,file,资源id等

Glide.placeholder() 加载暂时不支持显示进度,可以用占位图来显示,把占位图替换成帧动画,还有错误图片,图片缩放以及自定义大小。当加载网络图片时,由于加载过程中图片未能及时显示,此时可能需要设置等待时的图片,通过placeHolder()方法:

Glide.error() 传入加载图片失败后显示的图片id

Glide.crossFade() crossFade() 方法还有另外重载法 .crossFade(int duration)。如果你想要去减慢(或加快)动画,随时可以传一个毫秒的时间给这个方法。动画默认的持续时间是 300毫秒。如果你想直接显示图片而没有任何淡入淡出效果,在 Glide 的建造者中调用 .dontAnimate()。

Glide.into() into()传入的可以指定的imageview,所要设置图片(或者gif、video)的位置。

Glide.diskCacheStrategy() 缓存机制的设置
DiskCacheStrategy.NONE 什么都不缓存
DiskCacheStrategy.SOURCE 仅仅只缓存原来的全分辨率的图像
DiskCacheStrategy.RESULT 仅仅缓存最终的图像,即降低分辨率后的(或者是转换后的)
DiskCacheStrategy.ALL 缓存所有版本的图像(默认行为)

补充:
关于Glide.thumbnail的使用
这里写图片描述
加载图片的十分之一的尺寸的缩略图,然后加载全图。

图片的缩放,centerCrop()和fitCenter():
使用centerCrop是利用图片图填充ImageView设置的大小,如果ImageView的Height是match_parent则图片就会被拉伸填充
使用fitCenter即缩放图像让图像都测量出来等于或小于 ImageView 的边界范围该图像将会完全显示,但可能不会填满整个 ImageView。
这里写图片描述

操作gif,与操作图片类似

这里写图片描述
操作本地视频:

这里写图片描述
2.2、自定义控件显示
可以将图片、视频快照和GIFS显示在View上面之外,开发者也可以在自定义的Target上面显示这些媒体文件。
1)SimpleTarget
提供了对Target的简单实现,并且让你专注于对加载结果的处理,为了使用SimpleTarget,开发者需要提供一个宽和高的像素值,用来加载你的资源文件,并且你需要去实现。 (Target是request的载体,各种资源对应的加载类,含有生命周期的回调方法,方便开发人员进行相应的准备以及资源回收工作。)
这里写图片描述
在大多数SimpleTarget的实现当中,需要资源的加载不受组件生命周期的影响,Glide.with(context)当中的context是application context而不是fragment或者activity。(由于一些long running operations可能会导致内存泄露,如果你打算使用一个这样的操作,可以考虑使用一个静态的内部类而不是一个动态的内部类)
2)ViewTarget
如果你想Gidle加载图片的时候可以自定义图片的大小,或者想要设置一个自定义的显示动画,就可以通过ViewTarget来实现,可以通过一个静态的ViewTarget或者动态的内部类来实现相关的功能 。
自定义一个FutureStudioView
这里写图片描述
然后,自定义一个ViewTarget,传入参数FutureStudioView,在函数里this.view.setImage()。此外,你还可以通过GlideAnimation这个接口来提供必要的参数来启动动画。(GlideDrawable是包含动画效果的drawable)

这里写图片描述
2.3、Glide Transformations
Glide的两个关于尺寸的转换器,fitCenter和CenterCrop,二者是继承了BitmapTransformation,覆盖transform方法。其他的转换器可以将图片转为各种形状圆形,圆角型,外玻璃效果,灰度效果,等等https://github.com/wasabeef/glide-transformations

我们可以自定义转换器,BlurTransformatino( )可以是图片出现毛玻璃效果的转换器。
具体实现见:
(https://futurestud.io/tutorials/glide-custom-transformation)
//BlurTransformatino.java
public class BlurTransformation implements Transformation {

private static int MAX_RADIUS = 25;
private static int DEFAULT_DOWN_SAMPLING = 1;

private Context mContext;
private BitmapPool mBitmapPool;

private int mRadius;
private int mSampling;

public BlurTransformation(Context context) {
this(context, Glide.get(context).getBitmapPool(), MAX_RADIUS, DEFAULT_DOWN_SAMPLING);
}

public BlurTransformation(Context context, BitmapPool pool) {
this(context, pool, MAX_RADIUS, DEFAULT_DOWN_SAMPLING);
}

public BlurTransformation(Context context, BitmapPool pool, int radius) {
this(context, pool, radius, DEFAULT_DOWN_SAMPLING);
}

public BlurTransformation(Context context, int radius) {
this(context, Glide.get(context).getBitmapPool(), radius, DEFAULT_DOWN_SAMPLING);
}

public BlurTransformation(Context context, int radius, int sampling) {
this(context, Glide.get(context).getBitmapPool(), radius, sampling);
}

public BlurTransformation(Context context, BitmapPool pool, int radius, int sampling) {
mContext = context.getApplicationContext();
mBitmapPool = pool;
mRadius = radius;
mSampling = sampling;
}

@Override
public Resource transform(Resource resource, int outWidth, int outHeight) {
Bitmap source = resource.get();

int width = source.getWidth();
int height = source.getHeight();
int scaledWidth = width / mSampling;
int scaledHeight = height / mSampling;

Bitmap bitmap = mBitmapPool.get(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
if (bitmap == null) {
  bitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
}

Canvas canvas = new Canvas(bitmap);
canvas.scale(1 / (float) mSampling, 1 / (float) mSampling);
Paint paint = new Paint();
paint.setFlags(Paint.FILTER_BITMAP_FLAG);
canvas.drawBitmap(source, 0, 0, paint);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
  try {
    bitmap = RSBlur.blur(mContext, bitmap, mRadius);
  } catch (RSRuntimeException e) {
    bitmap = FastBlur.blur(bitmap, mRadius, true);
  }
} else {
  bitmap = FastBlur.blur(bitmap, mRadius, true);
}

return BitmapResource.obtain(bitmap, mBitmapPool);

}

@Override public String getId() {
return “BlurTransformation(radius=” + mRadius + “, sampling=” + mSampling + “)”;
}
}
注意:自定义转换器的时候,要使用transform()去代替fitCenter ( )/centerCrop( )

这里写图片描述

多转换器的实现:
这里写图片描述

2.4、关于调试问题
因为在使用Glide调试时是不会出现调试信息的,这样使得加载失败的原因难以调试。到底是网络错误还是图片根本就不存在亦或者解码出错,通过以下方法可以实现:
1、为了在异常发生时可以看到它们,你可以打开Glide中处理所有媒体加载响应的类GenericRequest的log开关
命令行下面执行:
adb shell setprop log.tag.GenericRequest DEBUG
关闭:adb shell setprop log.tag.GenericRequest ERROR
2、Glide不提供直接获取常规请求的日志,但是你可以在请求出错时抓取异常的日志。例如,如果图片不存在,Glide会(静静地)抛出一个异常,并显示出你.error()里指定的图片。如果你明确想要知道异常,创建一个listener,然后传递给Glide的.listener()方法。

private RequestListener<String, GlideDrawable> requestListener = new RequestListener<String, GlideDrawable>() {  
    @Override
    public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
        // todo log exception

        // important to return false so the error placeholder can be placed
        return false;
    }

    @Override
    public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
        return false;
    }
};

你可以在Glide中的构造方法里设置listener:

Glide  
    .with( context )
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .listener( requestListener )
    .error( R.drawable.cupcake )
    .into( imageViewPlaceholder );

.error()是否设置不影响日志正常工作。但只有在listener的onException方法里返回false,占位图.R.drawable.cupcake才会显示出来。
2.5、Glide module
先了解GlideBuilder:为Glide设置一些默认配置,比如:Engine,MemoryCache,DiskCache,RequestOptions,GlideExecutor,MemorySizeCalculator。Glide modules是一个可以全局改变Glide行为的抽象方法,可以通过GlideBuilder进行一些延迟的配置和ModelLoaders的注册。可以通过创建一个公共的类,实现GlideModule的接口来定制Glide,比如:去改变编码格式,disk cache, memory cache等,也可以注册相关组件,比如主动适配图片尺寸等。
实现过程:
自己去实现一个GlideModule:
首先在androidmanifest.xml中加入代码,全局中声明这个类,这样Glide知道它应该加载并使用它。
这里写图片描述
自己实现GlideModule
这里写图片描述

a)GlideModule的applyOptions()方法中
我们可以通过GlideBuilder去获得Glide的重要核心组件,可以去改变编码格式,disk cache, memory cache等
比如:Glide默认编码格式是RGB565,现在可以将其设置为 DecodeFormat.PREFER_ARGB_8888,或者其他。
这里写图片描述

GlideBuilder还可以设置其他属性:
• .setMemoryCache(MemoryCache memoryCache)
• .setBitmapPool(BitmapPool bitmapPool)
• .setDiskCache(DiskCache.Factory diskCacheFactory)
• .setDiskCacheService(ExecutorService service)
• .setResizeService(ExecutorService service)
• .setDecodeFormat(DecodeFormat decodeFormat)

b)GlideModule的registerComponents()
https://futurestud.io/tutorials/glide-module-example-accepting-self-signed-https-certificates

c)一个关于GlideModule的例子–请求特定尺寸的图片
比如:项目用到一个媒体服务器,提供非常高的分辨率图片服务(图片分辨率在6000x4500像素)。我们可以使用直接链接到源文件,在考虑设备带宽、内存和电池的时候是相当低效的。即使在今天的设备上显示高分辨率,对于显示低端的分辨率设备上,还是没有任何好处。因此Glide总是测量ImageView的尺寸,然后降低图片的内存消耗以匹配它。然而,无法避免要去下载和计算,完成降低图片大小的任务。因此,媒体服务器有一个新特性:它可以提供指定分辨率的图片。通过下面的方法实现:
是的,当然,我们会声明一个新的Glide module。我们必须使用glide.register()方法注册一个新的模型:
这里写图片描述
CustomImageSizeModel只是一个接口,添加宽度和高度作为参数。那样,我们可以从媒体服务器请求精确像素的图片。
这里写图片描述
CustomImageSizeModelFactory这个类只是实现 Glide 的ModelLoadFactory接口,它创建了我们CustomImageSizeLoader的一个新实例,一旦Glide请求被创建,将会去处理图片加载:
这里写图片描述
为了用CustomImageSizeModel传递请求到Glide,我们需要一个类CustomImageModelFutureStudio,创建定制图片大小的URL。
这里写图片描述

这里写图片描述
不需要给出传递准确的尺寸。Glide会测量ImageView并用我们的请求传递它。现在,服务器会用一个完美优化的图片作为响应!
你可以只添加额外的CustomImageSizeModel模型实现,如果你有多个服务器,它们使用不同的逻辑创建URL。只要创建一个CustomImageSizeModel实现,并传递到你的Glide请求中。
https://futurestud.io/tutorials/glide-module-example-optimizing-by-loading-images-in-custom-sizes

关于冲突问题:
虽然Glide允许一个应用当中存在多个GlideModules,Glide并不会按照一个特殊的顺序去调用已注册的GlideModules,如果一个应用的多个依赖工程当中有多个相同的Modules,就有可能会产生冲突。
如果一个冲突是不可避免的,应用应该默认去定义一个自己的Module,用来手动地处理这个冲突,在进行Manifest合并的时候,可以用下面的标签排除冲突的module
2.6、通过GlideBuilder配置全局配置文件
GlideModule→applyOptions方法里面的GlideBuilder参数实现,
1)Memory Cache
Glide内存缓存的目的是减少I/O,提高效率
可以通过GlideBuidler的setMemoryCache(MemoryCache memoryCache)去设置缓存的大小,开发者可以通过LruResourceCache去设置缓存区的大小
这里写图片描述

2) Bitmap Pool
可以通过GlideBuilder的setBitmapPool()方法设置池子的大小,LruBitmapPool是Glide的默认实现,使用如下:
这里写图片描述
3)DiskCache
通过GlideBuilder的setDiskCache(DiskCache.Factory df)方法设置存储的位置和大小,也可以通过传入DiskCacheAdapter或者自定义一个DiskCache来完全禁用缓存,硬盘缓存是在一个线程当中,通过一个DiskCache.Factory接口进行缓存的。
设置缓存大小,使用这个内部缓存不超过250M,其他应用不可访问。
这里写图片描述
设置缓存路径
实现DiskCache.Factory,然后使用DiskLruCacheWrapper创建一个新的缓存目录,比如,可以通过如下方式在外存当中创建缓存目录:

三、关于Glide的源码部分

3.1、总体设计图:
这里写图片描述
一些重要的概念:
GlideBuilder :为Glide设置一些默认配置,比如:Engine,MemoryCache,DiskCache,RequestOptions,GlideExecutor,MemorySizeCalculator
GlideModule :可以通过GlideBuilder进行一些延迟的配置和ModelLoaders的注册。
RequestManagerFragment:与当前上下文绑定的Fragment,统一管理当前上下文下的所有childFragment的请求。每一个Context都会拥有一个RequestManagerFragment,在自身的Fragment生命周期方法中触发listener相应的生命周期方法。
RequestCoordinator:一个接口,用于协调各种请求,普通图片请求,缩略图请求等。
EngineJob:调度DecodeJob,添加,移除资源回调,并notify回调
DecodeJob:调度任务的核心类,整个请求的繁重工作都在这里完成:处理来自缓存或者原始的资源,应用转换动画以及transcode。
ModelLoader:各种资源的ModelLoader, ModelLoader是一个工厂接口。将任意复杂的model转换为准确具体的可以被DataFetcher获取的数据类型。 将任意复杂的model转换为可以被decode的数据类型,允许model结合View的尺寸获取特定大小的资源
Resource:对资源进行包装的接口,提供get,recycle,getSize,以及原始类的getResourceClass方法。
RequestTracker:追踪,取消,重启失败,正在处理或者已经完成的请求
Target :request的载体,各种资源对应的加载类,含有生命周期的回调方法,方便开发人员进行相应的准备以及资源回收工作。
DataFetcher 一个接口,使用懒检索数据的方式来载入数据资源的,每一次通过ModelLoader加载资源的时候都会创建的实例。
loadData :异步方法,如果目标资源没有在缓存中找到时才会被调用,cancel方法也是。
cleanup:清理或者回收DataFetcher使用的资源,在loadData提供的数据被decode完成后调用。

3.2、请求加载流程
这里写图片描述

3.2.1、构建请求
主要是观察RequestBuilder,它的作用创建请求,资源类型配置,缩略图配置,以及通过BaseRequestOptions进行一些默认图,图片处理的配置。注意其中的方法: buildRequest(Target target)是用来创建请求,如果配置了thumbnail(缩略图)请求,则构建一个ThumbnailRequestCoordinator(包含了FullRequest和ThumbnailRequest)请求,否则简单的构建一个Request。下面是构建请求的流程:
这里写图片描述
3.2.2、load调用处理流程
从GenericRequest.java 的onSizeReady()方法跟入,可以发现:
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,priority, isMemoryCacheable, diskCacheStrategy, this);
从Engine.java方法中具体看该方法的load(),代码具体实现:为当前的请求创建一个Enginekey,根据这个key分别从cache、ActiveResource中去寻找资源,先从cache中寻找资源,如果找到则将其从cache中移除并放入activeResources中,否则从activeResources中寻找。cache是LruResourceCache对象,作为资源的LRU缓存;activeResources是以弱引用为值的Map,用于缓存使用中的资源。比一般内存缓存额外多一级缓存的意义在于,当内存不足时清理cache中的资源时,不会对使用中的资源造成影响。然后,资源命中的话通过onResourceReady回调对资源进行处理,没有命中则需要通过关联EngineJob来决定是否需要DecodeJob来处理当前请求。

从Engine.load()方法中跟入代码,可以看到加载流程中方法详细地调用,具体跟代码可以参考:http://www.jianshu.com/p/96fc561eada1(比较详细)。

在Engine.java代码中,EngineJob.start方法中,创建一个EngineRunnable线程,放入到一个FifoPriorityThreadPoolExecutor线程池中,跟入他的run方法中,其中onLoadComplete方法会调用manager.onResourceReady(resource),主要是为切换到主线程中,准备设置图像操作。其中decode方法中,根据缓存策略来选择decodeFromCache还是decodeFromSource。
EngineRunnable的decodefromcache中根据diskCacheStrategy的SOURCE 或者RESULT 值分别进入decodeSourceFromCache和decodeResultFromCache方法中,SOURCE 是指仅仅只缓存原来的全分辨率的图像,RESULT 是指仅仅缓存最终的图像,即降低分辨率后的(或者是转换后的)。
注意:transformEncodeAndTranscode方法还有writeTransformedToCache ,该方法还是writeTransformedToCache依然根据diskCacheStrategy决定了是否会将resource写入cache中。

参考链接:
https://futurestud.io/tutorials/glide-custom-transformation(这个很全)
http://www.jianshu.com/p/7610bdbbad17(中文版)
http://blog.csdn.net/a910626/article/details/50688637
http://blog.csdn.net/u013278099/article/details/50459128
http://blog.csdn.net/fandong12388/article/details/46372255
http://www.trinea.cn/android/android-image-cache-compare/
http://www.lightskystreet.com/2015/10/12/glide_source_analysis/#comments
http://www.jianshu.com/p/96fc561eada1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值