//该死的拖延症,总是要学习做笔记,纸上得来终觉浅。
一、简介、使用。
1.1简介
略,见 主页
1.2基本使用
Glide.with(imageView.getContext())
.load(url)
.placeholder(defaultImage)
.error(failImage) .diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageView);
其中 load()重载方法。load也支持多参数,这里我们可以传入的有Uri、File、byte[]、Object、Bitmap、String、Drawable、Integer、URl,
DiskCacheStrategy是缓存策略,有以下几个参数,并详细说明该策略的保存信息。
1) ALL 网络资源执行DATA、RESOURCE,本地资源执行RESOURCE
2) NONE 不缓存任何内容
3) DATA 缓存原始图片
4) RESOURCE 缓存解码后的图片
5) AUTOMATIC 根据图片资源智能地选择使用哪一种缓存策略(默认选项)
1.3 其他用法
1.3.1
listener 这里我们可以对加载图片进行处理,加载成功和加载失败两个方法
1.3.2
addListener 和listener不同的是,listener是清空了监听,把当前监听加入进去,addListener是不清空监听,既不影响其他地方调用的监听,仅仅是把当前的监听加入进去,建议使用addListener,其他和listener相同
1.3.3
.centerInside() .fitCenter() .centerCrop() 这三种图片处理方式,centerInside等比例缩小显示,在横向或竖向上相等,不会进行放大。fitCenter等比例放大或缩小。centerCrop等比例放大。
1.3.4
override(int w,int h)这个方法会将图片以制定的高宽显示在ImageView上
1.3.5
apply(RequestOptions.bitmapTransform(new RoundedCorners(dp2px(60)))) 这个方法可以设置为圆形图了。
.optionalFitCenter() 同样可以圆形
二、原理分析
2.1原理
缓存:
先去LruCache中寻找图片,如果LruCache中有,则直接取出来使用,如果LruCache中没有,则去WeakReference中寻找,如果WeakReference中有,则从WeakReference中取出图片使用,同时将图片重新放回到LruCache中,如果WeakReference中也没有图片,则去文件系统中寻找,如果有则取出来使用,同时将图片添加到LruCache中,如果没有,则连接网络从网上下载图片。图片下载完成后,将图片保存到文件系统中,然后放到LruCache中。
Glide的缓存只有两个模块,一个是内存缓存,一个是磁盘缓存。其中内存缓存又分为Lru算法的缓存和弱引用缓存。
LruCache算法,Least Recently Used,又称为近期最少使用算法。主要算法原理就是把最近所使用的对象的强引用存储在LinkedHashMap上,并且,把最近最少使用的对象在缓存池达到预设值之前从内存中移除。
读取的顺序是:Lru算法缓存、弱引用缓存、磁盘缓存
写入的顺序是:弱引用缓存、Lru算法缓存、磁盘缓存(不准确)
2.2 代码顺序
2.2.1 with()
with()方法重载的代码都非常简单,都是先调用RequestManagerRetriever的静态get()方法得到一个RequestManagerRetriever对象,这个静态get()方法就是一个单例实现。然后再调用RequestManagerRetriever的实例get()方法,去获取RequestManager对象。
RequestManagerRetriever类中看似有很多个get()方法的重载,实际上只有两种情况而已,即传入Application类型的参数,和传入非Application类型的参数。
a.传入Application参数
如果在Glide.with()方法中传入的是一个Application对象,那么这里就会调用带有Context参数的get()方法重载,调用getApplicationManager()方法来获取一个RequestManager对象。其实这是最简单的一种情况,因为Application对象的生命周期即应用程序的生命周期,因此它自动就是和应用程序的生命周期是同步的,如果应用程序关闭的话,Glide的加载也会同时终止。
b.非Application参数的情况。
最终的流程都是一样的,那就是会向当前的Activity当中添加一个隐藏的Fragment。因为Glide需要知道加载的生命周期。Fragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以监听到的,这样Glide就可以捕获这个事件并停止图片加载了。
2.2.2 load()方法。
根据传入的参数类型作为泛型,创建一个图片请求对象DrawableTypeRequest.
DrawableTypeRequest的父类是DrawableRequestBuilder,DrawableRequestBuilder中有很多个方法,这些方法其实就是Glide绝大多数的API比如说placeholder()方法、error()方法、diskCacheStrategy()方法、override()方法等。
RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode, RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
this.context = context.getApplicationContext();
this.lifecycle = lifecycle;//与页面生命周期绑定的lifecycle对象
/*treeNode:
*提供了可以访问当前上下文中所有的RequestManager,也就是说基于Context层次结构建立了RequestManager的层次结构,
*而上下文的层次结构是在Activity / Fragment中嵌套的。
*不过,注意,如果当前上下文是Application Context ,只能访问当前上下文的RequestManager. 总而言之就是更方便的管理所有的RequestManager
*/
this.treeNode = treeNode;
this.requestTracker = requestTracker;//请求管理控制类,start、pause、resume、clear、restart、addRequest...
this.glide = Glide.get(context);//保存有一个单例的glide对象
this.optionsApplier = new OptionsApplier();//optionsApplier的apply方法返回一个图片请求对象DrawableTypeRequest
/*网络状态的监听接口,实现了LifecycleListener ,与Activity/Fragment 生命周期绑定在一起。
*具体实现DefaultConnectivityMonitor 以及NullConnectivityMonitor ,其中前者是一个网络监听的具体实现,
*后者是一个空实现,类似于Null Object Pattern ,可以避免空指针异常
*/
ConnectivityMonitor connectivityMonitor = factory.build(context, new RequestManagerConnectivityListener(requestTracker));
if (Util.isOnBackgroundThread()) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
lifecycle.addListener(RequestManager.this);
}
});
} else {
lifecycle.addListener(this);
}
lifecycle.addListener(connectivityMonitor);
}
/**
* 根据传入的参数类型作为泛型,创建一个图片请求对象DrawableTypeRequest
* DrawableTypeRequest中存有 glide、requestTracker、lifecycle等的引用
* 还有ModelLoader,load方法传入的数据不一定是url,传入的数据通过ModelLoader处理后转为真正的图片数据源
*/
public DrawableTypeRequest load(String string) {
return (DrawableTypeRequest) fromString().load(string);
}
public DrawableTypeRequest fromString() {
return loadGeneric(String.class);
}
private DrawableTypeRequest loadGeneric(Class modelClass) {
ModelLoader streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
ModelLoader fileDescriptorModelLoader = Glide.buildFileDescriptorModelLoader(modelClass, context);
if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
+ " which there is a registered ModelLoader, if you are using a custom model, you must first call"
+ " Glide#register with a ModelLoaderFactory for your custom model class");
}
return optionsApplier.apply(new DrawableTypeRequest(modelClass, streamModelLoader, fileDescriptorModelLoader, context, glide, requestTracker, lifecycle, optionsApplier));
}
//DrawableTypeRequest的load方法只是把传入的model持有,并未真正加载
public GenericRequestBuilder load(ModelType model) {
this.model = model;
isModelSet = true;
return this;
}
2.2.3 into()
into()方法的具体逻辑都是在DrawableRequestBuilder的父类当中了。
public Target into(ImageView view) {
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
return into(glide.buildImageViewTarget(view, transcodeClass));
}
DrawableRequestBuilder的父类是GenericRequestBuilder,在这里根据显示拉伸类型,调用了glide.buildImageViewTarget()方法,这个方法会构建出一个Target对象,Target对象则是用来最终展示图片用的。其实是调用的为ImageViewTargetFactory的buildTarget()方法。然后传递给GenericRequestBuilder另一个接收Target对象的into()方法当中了。
public class ImageViewTargetFactory {
@SuppressWarnings("unchecked")
public Target buildTarget(ImageView view, Class clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
return (Target) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
return (Target) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz
+ ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
在buildTarget()方法中会根据传入的class参数来构建不同的Target对象,这个class参数其实基本上只有两种情况,如果你在使用Glide加载图片的时候调用了asBitmap()方法,那么这里就会构建出BitmapImageViewTarget对象,否则的话构建的都是GlideDrawableImageViewTarget对象。
接上文第一次into(imageview)通过imageViewTargetFactory.buildTarget(imageView, transcodedClass)生成GlideDrawableImageViewTarget对象有传递给第二个into(),第二个into对象
public > Y into(Y target) {
Util.assertMainThread();
if (target == null) {
throw new IllegalArgumentException("You must pass in a non null Target");
}
if (!isModelSet) {
throw new IllegalArgumentException("You must first set a model (try #load())");
}
Request previous = target.getRequest();
if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);
return target;
}
2.2.4 request
可以看出来调用buildRequest()方法构建出了一个Request对象,然后执行这个Request。Request是用来发出加载图片请求的,buildRequestRecursive实则调用了buildRequestRecursive函数。
顺序为如果配置了thumbnail(缩略图)请求,则构建一个ThumbnailRequestCoordinator(包含了FullRequest和ThumbnailRequest)请求,否则简单的构建一个Request,调用了obtainRequest函数。至此请求对象创建成功。
然后执行runrequest
public void runRequest(Request request) {
this.requests.add(request);
//添加request对象到集合中,requests是一个set,Set是一个不包含重复元素的 collection,在这里是无序的。
if(!this.isPaused) {
request.begin();//如果当前状态是非暂停的,调用begin方法发送请求
} else {
this.pendingRequests.add(request);
//将请求加入到挂起的请求集合,RequestTracker在RequestManager类中初始化,并且跟踪着生命周期
}
}
begin函数,首先做了几个判断,验证宽高是否合法,加载占位图等,然后执行onsizeReady()方法中load() ,进入Engine逻辑
this.engine.load(this.signature, width, height, dataFetcher, this.loadProvider, this.transformation, transcoder, this.priority, this.isMemoryCacheable, this.diskCacheStrategy, this);
load的策略,据调用loadFromCache从内存加载,若返回值为空再次从活动的资源中加载,若再次为空查看jobs是否提交过任务,若没有提交则创建EngineRunnable,并将任务提交到engineJob中。
Transformation类负责处理资源,这里面出现BitmapPool类,达到Bitmap复用。
先从cache中寻找资源,如果找到则将其从cache中移除并放入activeResources中,否则从activeResources中寻找。cache是LruResourceCache对象,作为资源的LRU缓存;activeResources是以弱引用为值的Map,用于缓存使用中的资源。比一般内存缓存额外多一级缓存的意义在于,当内存不足时清理cache中的资源时,不会对使用中的Bitmap造成影响。
3.1
with(Context context) - 需要上下文,这里还可以使用 Activity、FragmentActivity、android.support.v4.app.Fragment、android.app.Fragment 的对象。将 Activity/Fragment 对象作为参数的好处是,图片的加载会和 Activity/Fragment 的生命周期保持一致,例如:onPaused 时暂停加载,onResume 时又会自动重新加载。所以在传参的时候建议使用 Activity/Fragment 对象,而不是 Context。
3.2Module
Glide 的 Module 是一个可以全局改变 Glide 的行为的东西,为了定制 Glide 的行为我们要去实现 interface GlideModule 来写我们自己的代码。
public class ExampleModule implements GlideModule{
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// todo
}
@Override
public void registerComponents(Context context, Glide glide) {
// todo
}
}
可以看到 GlideModule 为我们提供了两个方法,这里我们主要使用的是 applyOptions(Context context, GlideBuilder builder) , 我们自己的需要重新定义的代码写在该方法里就可以了。然后我们还需要去 AndroidManifest.xml 中使用 meta 声明我们上面实现的 Module
android:name="com.xxx.ExampleModule"
android:value="GlideModule" />
...
applyOptions(Context context, GlideBuilder builder) 中有两个参数, 我们通过使用 GlideBuilder 来实现我们的需求。先看看 GlideBuilder 中可用的方法
.setMemoryCache(MemoryCache memoryCache)
.setBitmapPool(BitmapPool bitmapPool)
.setDiskCache(DiskCache.Factory diskCacheFactory)
.setDiskCacheService(ExecutorService service)
.setResizeService(ExecutorService service)
.setDecodeFormat(DecodeFormat decodeFormat)
3.3Transformations篇
图片进行处理操作,比如:图片切圆角,灰阶处理等等;这些需求我们通过 Transformations 操作 bitmap 来实现,我们可以修改图片的任意属性:尺寸,范围,颜色,像素位置等等。其实我们之前已经提到过两个 Transformation 了,即 fitCenter 和 centerCrop ,这两个是 Glide 已经实现的。
怎么样来实现自己的 Transformation ,我们需要创建一个类去实现 Transformation 接口,但是要实现这个方法还是比较复杂的,接口中 transform 方法提供的参数 Resource resource 不是那么好处理的。如果你只是想要对图片(不是 Gif 和 video)做常规的 bitmap 转换,我们推荐你使用抽象类 BitmapTransformation。它简化了很多的实现,这应该能覆盖 95% 的应用场景啦。
下面的代码实现了对图片切圆角的操作,其中 getId() 方法描述了这个 Transformation 的唯一标识,为避免意外我们需要确保它是唯一的。
public class RoundTransformation extends BitmapTransformation {
private float radius = 0f;
public RoundTransformation(Context context) {
this(context, 4);
}
public RoundTransformation(Context context, int px) {
super(context);
this.radius = px;
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return roundCrop(pool, toTransform);
}
private Bitmap roundCrop(BitmapPool pool, Bitmap source) {
if (source == null)
return null;
Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
if (result == null) {
result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
canvas.drawRoundRect(rectF, radius, radius, paint);
return result;
}
@Override
public String getId() {
return getClass().getName() + Math.round(radius);
}
}
使用
调用 .transform() 方法,将自定义的 Transformation 的对象作为参数传递进去就可以使用你的 Transformation 了,这里也可以使用 .bitmaoTransform() 但是它只能用于 bitmap 的转换。
Glide.with(context)
.load(mUrl)
.transform(new RoundTransformation(context , 20))
//.bitmapTransform( new RoundTransformation(context , 20) )
.into(mImageView);