android glide 简书,Android: Glide笔记

//该死的拖延症,总是要学习做笔记,纸上得来终觉浅。

一、简介、使用。

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);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值