今天在看郭霖大神的glide分析 感触良多 以前只会用 根本不知道每一步是什么意思 也没有看过源码 这次正好来解析看看
glide.with(this).load(url).into(imageView); 这是glide框架加载图片很简单的一步
with(this) 是干什么的?有什么作用? 不知道 以前只知道这句话可以把url路径的图片加载到 imageView控件中显示。 深度解析一下这行代码。
with(this)是创建一个加载图片的实例 里面可以传activity、fragment的context,这决定了这个实例的生命周期,如果不在activity或者fragment中,也可以传一个全局的生命周期,它的生命周期和应用一样。
如果传入的是activity、fragment生命周期 activity、fragment销毁,图片也开始停止加载,如果传入的是全局的context,只有应用销毁了,才会停止加载数据。
load(url)
是指定图片加载的资源,glide支持多种图片加载路径本地图片,网络图片,二进制流,source资源,uri对象等等,所有load()方法肯定有很多重载方法,可以这么使用
// 加载本地图片
File file = new File(getExternalCacheDir() + "/image.jpg");
Glide.with(this).load(file).into(imageView);
// 加载应用资源 int resource = R.drawable.image; Glide.with(this).load(resource).into(imageView); // 加载二进制流 byte[] image = getImageBytes(); Glide.with(this).load(image).into(imageView); // 加载Uri对象 Uri imageUri = getImageUri(); Glide.with(this).load(imageUri).into(imageView);
into(imageView)
就是想在哪个控件上加载图片就把哪个控件传进去,当然不止接收imageView 还接收很多别的类型,属于高级用法 然而现在我还不知道。
占位图
加载图片需要时间,点击后一段时间无响应的状态,用户体验是比较差的,如果加上占位图,图片没有加载出来,放一个加载进度条会比较好。
在into()之前 加 .placeholder(R.drawable.loading)
加上这一段代码,就Ok了 R.drawable.loading是预先准备好的加载进度条。
glide的缓存机制很强大 加载完的图片 glide就自动缓存了下来 我们可以对缓存机制做修改
.diskCacheStrategy(DiskCacheStrategy.NONE) 这段代码是让glide不做任何缓存
异常占位图
如果图片加载错误,加载此图片 使用方法和占位图一样 先准备一张error的资源图片
.error(R.drawable.error)
glide还有一点比较牛逼的地方 也是我学习glide不去学习picasso的原因 picasso不支持动态图的加载 这一点使用起来就很不方便
glide可以直接加载动态图 内部做了图片类型的判断 如果GIF类型的就加载GIF 如果是JPG的就加载JPG的
我们也可以让glide只加载静态图
.asBitmap() 这段代码 不让glide后台去做判断 只加载静态图 那么如果是动态图回怎么样呢? 只会加载动态图的第一帧,动态图不会动了
既然glide只能加载静态图 也可以只加载动态图
.asGIF() 这个方法可以只加载动态图 注意 : 如果在这时加载静态图是加载不出来的,只能加载动态图的时候不能加载静态图
指定图片大小
正常我们在使用的时候绝大多数使用场景是不需要指定图片大小的。
我们先理解一个概念,内存浪费,比如我们加载一个1000*1000像素的图片,加载到一个像素200*200的控件上,如果不对图片进行压缩处理,直接加载到内存,就会造成内存浪费,根本用不到这么高像素的图片。
glide会自动根据我们控件的像素和图片的像素比例,自己判断应该加载图片的大小。
也可以直接指定加载图片的像素大小 .override(width,height) 给定宽高,就可以对应加载多大的图片了。glide中会自动压缩。
with(context)源码流程分析
主要是获取 RequestManager对象 RequestManagerRetriever中有很多get()的重载方法,其实主要分为两类,一个是有application类型的参数 ,一个是非application类型的参数
如果传入application参数会直接得到全局context,glide的生命周期和应用程序的生命周期一样,应用程序销毁,glide也就停止加载。
如果传入非application参数,如activity、fragment,glide的生命周期和activity、fragment生命周期一样,界面销毁,glide停止加载。
但glide怎么知道activity的生命周期呢? 当判断到是activity的时候,glide会向activity添加一个隐藏的fragment fragment和activity的生命周期是一样的, 由此glide可以知道activity的生命周期。
当在非主线程使用glide,无论传入的是application或者非application都直接使用application,它的生命周期和应用的生命周期一样。
load(url) load主要是得到DrawableTypeRequeset对象,可以通过asBitmap() asGIF() 方法强制得到BitmapTypeRequest或者 GIFTypeRequest对象 如果没有限制 得到的就是DrawableTypeRequest对象,自己去判断是动态图还是静态图
最终load()方法返回的其实就是一个DrawableTypeRequest对象。那么接下来我们就要进行分析into()方法中的逻辑。
into(imageView)
跟着大神的脚步,一点一点去看glide源码,楞是晕住了, 真的没有看懂,怎么办?突然感觉好尴尬,不过尴尬就尴尬了,会用就可以,这边等将来自己花时间认真看了,再把自己的理解写上来,只想说一句,glide太强大了。
glide缓存机制
分为两种,磁盘缓存和内存缓存,两个缓存策略作用各不相同,却又相辅相成,内存缓存作用防止应用将重复的图片数据读取到内存中,磁盘缓存防止应用从网络或者其他地方重复下载和读取数据。两者配合才实现glide极佳的图片缓存效果
既然有内存缓存就要有内存缓存对应的缓存key,对应的key对应对应的缓存。glide的EngineKey生成的参数很多,宽、高、等等,只有所有参数都相同才会生成相同的key。可以理解为同一张图片,不同的显示尺寸,glide都会重新缓存,所以加载的快。
内存缓存:glide默认是开启内存缓存的,就是只要EngineKey相同,内存缓存中有图片,glide就会优先从内存中取数据出来。
.skipMemoryCache(true)
此方法是关闭内存缓存,传入参数true关闭。 一般使用时不会关闭,特殊情况下才会使用。
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null; }
当 skipMemoryCache()传入true的时候, isMemoryCacheable为false, 直接返回null,不走缓存机制。
glide的内容缓存实现是使用LurCache算法配合软引用机制共同实现的。
EngineJob当中通过Handler发送一条消息将执行逻辑切回到主线程当中,从而执行handleResultOnMainThread()方法。
engineResource.acquire() 和 engineResource.release(), 一个是获得对象,是个是释放对象。
执行acquire(), 实际上是把资源从LurCache中删除,加载到弱引用中 , ++acquire;
在执行release(),把资源从弱引用删除,添加到LurCache , --acquire;
其实就是当图片在使用的时候acquire > 0 ,资源在弱引用中,防止LurCache回收, 在acquire = 0 的时候, 资源在LurCache中 ,用来最大时间的保存资源。
磁盘缓存: 底层还是通过LurCache算法实现的。
调用diskCacheStrategy()方法并传入DiskCacheStrategy.NONE,就可以禁用掉Glide的硬盘缓存功能了。
这个diskCacheStrategy()方法基本上就是Glide硬盘缓存功能的一切,它可以接收四种参数:
- DiskCacheStrategy.NONE: 表示不缓存任何内容。
- DiskCacheStrategy.SOURCE: 表示只缓存原始图片。
- DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。
- DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。
Glide的使用技巧:
1.请求网络图片的时候,服务器防止攻击,或者服务器的缓存,都会携带一个token参数,作为一个标示,但是token是会变化的。Glide的EngineKey生成条件很严格,有任何的一点改变都会生成不同的key,
这个时候token不停的在变,glide的缓存也就没有了作用,所以需要注意此时glide的使用情况。
因为有这个token才会生成不同的EngineKey,所以如果url把token去掉,再去生成EngineKey就可以了。
写一个MyGlideUrl,继承GlideUrl:
public class MyGlideUrl extends GlideUrl { private String mUrl; public MyGlideUrl(String url) { super(url); mUrl = url; } @Override public String getCacheKey() { return mUrl.replace(findTokenParam(), ""); } private String findTokenParam() { String tokenParam = ""; int tokenKeyIndex = mUrl.indexOf("?token=") >= 0 ? mUrl.indexOf("?token=") : mUrl.indexOf("&token="); if (tokenKeyIndex != -1) { int nextAndIndex = mUrl.indexOf("&", tokenKeyIndex + 1); if (nextAndIndex != -1) { tokenParam = mUrl.substring(tokenKeyIndex + 1, nextAndIndex + 1); } else { tokenParam = mUrl.substring(tokenKeyIndex); } } return tokenParam; } }
这样就可以把token去掉,使用glide的load方法要有一点改变:
Glide.with(this)
.load(new MyGlideUrl(url))
.into(imageView);
如此解决以上问题。 使用起来还是很方便,但不会出现缓存无效的情况了。
2.glide加载图片的监听 如果图片加载失败了怎么办? 怎么才能制动 Glide这么强大的框架,肯定预留好了接口。
.listener(new RequestListener<String, GlideDrawable>() {
@Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
log.d("加载失败的Url : " + model + " ; 加载失败的类型 : " + e);
return false;
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
log.d("加载成功的Url : " + model);
return false;
}
})
在glide.load()后面加上这段代码就OK了。成功和失败都有回调。