【Android】Glide代码简介

一、摘要

Glide是比较优秀的图片加载框架,对Activity和Fragment可以进行生命周期监听,巧妙的缓存机制,让图片加载变的很流畅

二、背景

Glide在视界和音乐中都有使用,研究其中原理,对图片加载问题的解决比较有帮助

三、推广建议

学习开源框架思想

四、正文

Model:表示数据来源,图片的来源,比如Url或者本地文件
Data:从数据源中获得Model之后,要加工成原始的数据,一般是inputstream
Resources:对得到的原始数据Data进行解码,比如将inputstream解码成bitmap,解码之后的资源就是Resources
TransformedResource:把Resources进行变换,比如图片进行裁剪
TranscodedResource:转码,glide不仅能加载静态图片,也能加载动态图,但是解码之后的bitmap和gif drawable类型不统一,为了逻辑方便处理,所以就会将bitmap转换成Glide Bitmap Drawable,这样类型就统一了
Target:将图片显示到目标上,Glide会将显示的目标封装成target,比如ImageView

Glide采用流式接口调用

Glide.with(context)相当于建立一个Glide客户端,就相当于OkHttpClient
在Activity或者Fragment上,传入this即可,这个Context是图片加载的生命周期,当context生命周期销毁后,图片加载也随之停止。如果传入ApplicationContext,则只有当手机应用进程杀掉,图片加载才会停止
.load(url)指定图片的url
.placeholder 指定图片未成功加载前显示的图片(里面不能放url,因为如果从网络请求,仍然会造成空白,用网络图片作为占位符,就失去原有的作用了)
.error指定图片加载失败实现的图片
.override(300,300)指定图片的尺寸(防止OOM),Glide不会将图片完整的尺寸加载到内存中,而是用多少加载多少,它会自动判断ImageView的大小,只将最优的尺寸加载到内存中,从而节省开支
.fitCenter()缩放图片,使得它的宽和高都小于ImageView的边界范围,它会完全显示出图片,但是可能不会填满整个ImageView
.centerCrop()让图片填充整个ImageView,会裁剪额外的部分,centerCrop会完全填充整个ImageView,但是会裁剪图片
提供fitCenter和centerCrop两种方法,防止在调整宽高的时候,图片失真
.skipMemoryCache(true)跳过内存缓存,则Glide不会将图片放入到内存缓存中,Glide此时只能用磁盘缓存来避免重复的网络请求。Glide默认会将所有的图片放入到内存缓存中,所以不必传入一个false。
.diskCacheStrategy(DiskCacheStrategy.NONE)跳过磁盘缓存
.diskCacheStrategy(DiskCacheStrategy.SOURCE)仅仅只缓存原来的全分辨率的图片
.diskCacheStrategy(DiskCacheStrategy.RESULT)仅仅缓存最终的图像,就是降低分辨率之后的图片
.diskCacheStrategy(DiskCacheStrategy.ALL)缓存所有版本的图像,即会缓存所有版本的图片,原来的全分辨和降低分辨率之后的图片
.priority(Priority.HIGH)指定优先级,Glide将会用他们作为一个准则,并尽可能的处理这些请求(比如想优先加载出运营的图片),但是不会100%按照优先级顺序来加载。
.into(imageView)指定显示图片的ImageView

Glide在加载图片时,实际上隐藏了大量在后台处理图片的场景,比如网络请求等等,这些都是后台子线程中做的,当后台处理完之后,会切换到UI线程中进行显示。

with有很多方法,Glide设计就是将图片加载与组件的生命周期相挂钩。

RequestManager:图片请求管理类,并且完成Glide对象的构造,还控制Glide请求中的各种方法,通过它可以完成对界面生命周期的监听,根据生命周期,来对图片做相应的操作。
RequestManagerRetriever:生成RequestManager类的
retriever.get(context)返回一个RequestManager

Fragment的生命周期与Activity是绑定的,添加Fragment有两种方式,一种是有UI的,一种是没有UI的。
Glide中的RequestManagerFragment就是一种没有UI的添加方式,它是想通过一个没有界面的Fragment去监听Activity的生命周期。因为Glide无法去监听Activity的生命周期,所以它只有通过空的RequestManagerFragment来监听生命周期,从而完成绑定Activity生命周期的过程。

在RequestManagerRetriever中的fragmentGet方法中,如果获得的RequestManager为空,则新创建一个,然后调用RequestManagerFragment中的setRequestManager方法,将requestManager设置进去,调用该方法后,就将requestManager与空界面的RequestManagerFragment进行了绑定,从而将监听Activity生命周期与图片加载绑定到一起。一个RequestManager对应一个RequestManagerFragment,两者是一一对应的。

RequestManagerFragment中ActivityFragmentLifecycle用于管理生命周期,RequestManager在RequestManagerFragment中注册了一些回调接口,通过这些接口就可以监听到Activity或Fragment调用onStart, onStop, onDestroy等等方法的时候,可以用于处理图片加载请求。
在RequestManagerFragment中,可以看到生命周期函数onStart, onStop等方法调用super.onXXX方法后,会调用this.lifecycle.onStart

Glide没法知道Activity生命周期的,所以巧妙的使用RequestManagerFragment这个没有UI的Fragment让它绑定到Activity,由这个Fragment来监听生命周期。

Glide.with随着传入不同的context,最终返回RequestManger对象
之后的load就是RequestManager中的方法,
DrawableTypeRequest:里面两个重要方法asBitmap和asGif,它主要是将图片转换成Bitmap或者Gif这两种图片格式,可以方便后的加载,它继承DrawableRequestBuilder,最终是继承自GenericRequestBuilder,它是配置参数最终的一个类,一些初始化操作都是在这个类中实现的。
RequestTracker:负责跟踪图片请求的周期,可以用于取消或者重启一些失败的请求
fromString:在RequestManger中实现的,调动loadGeneric方法,进入该方法中,可以看到首先创建两个ModelLoader(将Model变成原始数据),最后调用的是optionsApplier.apply方法

load方法其实做的也是一些初始化操作,但是它获得了DrawableTypeRequest对象,通过它就可以获得图片请求的Request,在后续的into方法中就会用到typeRequest、RequestTracker等等进行实际的图片加载请求。

into方法:最核心的方法,最终的实现在GenericRequestBuilder类中实现的,首先判断是否在主线程,否则抛出异常,所以可以确认into方法是在主线程中调用的,因为into方法做的是图片操作,即UI的变化,所以必须在主线程中
在最后return,先调用buildImageViewTarget方法,它用于创建target对象,然后传递给into方法
Target类继承自LifecyclleListener接口,里面就是三个生命周期回调方法,它另gilde.with中传入的context来进行组件的监听,然后对图片加载过程进行控制。
看下具体实现类ImageViewTarget,里面有个方法,setResources,是一个抽象方法,它有3个具体实现。

buildImageViewTarget:里面通过imageViewTargetFactory.buildTarget来构建Target,buildTarget方法中,如果没有调用asBitmap方法,则默认返回GildeDrawableImageViewTarget,如果调用了asBitmap,则会返回BitmapImageViewTarget,最后一个DrawableImageViewTarget用的比较少。

into:分3个步骤:

(1)对target绑定的旧的Request进行删除(Request:用于图片加载)
(2)然后新建一个Request,并绑定到target
(3)发送Request,交给RequestTracker(Request管理跟踪器)来进行处理(Request中比较重要的方法是begin方法)

into方法中requestTracker.remoeveRequest(previous)就是将这个Request暂停,然后previous.recycler()中除了将成员变量赋值为null,最后REQUEST_POOL.offer()即当前的Request不再用时则放到这个请求池中复用

移除之前的Request之后,又buildRequest并绑定,将视图target与request绑定到一起,这样在复用的时候,先取消Request,防止图片错位的现象。(setRequest最终调用的是view.setTag方法)。最后requestTracker.runRequest(request)创建完Request之后,交给RequestTracker执行Request

buildRequest:调用buildRequestRecursive,前面很多if判断都是与缩略图相关的,最后调用obtainRequest最终得到Request,里面调用GenericRequest.obtain方法,obtain中REQUEST_POOL.pool先从线程池中获取一个请求(即能复用就先复用,不能复用再去new一个新的)。

runRequest:首先调用add方法,然后判断当前没有正在执行的请求,则request.begin(),如果有请求,则调用pendingRequest.add加入到一个集合中。

begin:如果调用了override,则会调用onSizeReady方法,如果没有调用override,则会target.getSize去重新测量宽高(里面是根据ImageView的宽高来决定图片应该展示的宽高,在计算完宽高后,还是会走onSizeReady方法)。最后经过一些判断,调用target.onLoadStarted方法参数就是图片占位符。

getSize:sizeDeterminer.getSize,里面getViewWidthOrParam获取宽高等信息,最后observer.addOnPreDrawListener,此时view还没有被测量完毕,所以会添加到ViewTreeObserver中来进行事件的监听。当view绘制完成,会调用cb.onSizeReady中

onSizeReady:根据loadProvider来获取ModelLoader(它是从数据源中获取原始数据inputStream,即Data)和ResourcesTranscoder。然后根据modleLoader它获取DataFetcher(将Data数据转换成能直接用的不同形式的图片数据类型),最后调用engine.load将参数传入其中,进行实际的图片加载。

DataFetcher是一个接口,它的实现类有很多,根据前缀可以看出是不同的数据来源(字节,网络,文件、gif等等)

ResourcesTranscoder:对原始数据解码的对象,将获取的原始数据io流解码成bitmap,解码后的资源称之为resources。

LoaderProvider是在GenericRequest的子类中赋值的,比如DrawableTypeRequest中buildProvider,返回一个FixedLoadProvider(是LoadProvider的一个实现类,包含的作用很多,可以获取到原始图片,还可以进行解码、编码、转码等等),里面的DataLoadProvider是用于图片编解码,ImageVideoModelLoader(传入的参数是前面的两个modeLoader,imageVideoModeLoader是两者的包装类,即它里面的成员变量就是这两个loader)

DataLoaderProvider是一个接口,负责编解码的,Data是从数据源中获取的原始数据,Resources是解码后的资源。解码即从Data转换为Resources的过程。而编码的概念和前面不同,是将Data或者Resources持久化到本地文件的过程,主要是因为Glide中会实现一个磁盘缓存。
DataLoaderProvider的实现类中有LoadProvider,两个方法getModelLoader和getTranscoder

engine.load
engine是开启图片加载的一个类,并且管理正在使用的和缓存中的图片的类
fetcher.getId用于加载图片的唯一标识(如果是网络图片,可以理解成url地址),
keyFactory.buildKey方法传入的参数非常多,传入这么多参数的作用就是让它具有唯一性,比如宽高是它的参数,如果宽高稍微变化一点,EngineKey就会发生变化。EngineKey是Glide缓存中的一个Key,它实现了Key接口,里面的equals和hashCode方法,这两个方法决定这个EngineKey的唯一性。

loadFromCache:从缓存中获取图片,如果缓存不为空,则调用onResourceReady方法,如果为空,则调用loadFromActiveResources获取缓存图片,如果仍然没有获得到,则开启一个EngineJob从磁盘或者网络获取图片,

Glide中的缓存:内存+磁盘
两者的作用是相似的,内存缓存就是防止图片重复的读入到内存中,磁盘缓存就是防止重复的从网络下载或者磁盘上复制读取图片。

内存缓存:LruCache算法(近期最少使用),把最近使用的对象强引用存储到LinkedHashMap上,并把最近最少使用的对象从内存中移除。
另外,Glide还使用弱引用机制来完成缓存

在Engine中load方法中,loadFromCache和loadFromActiveResources两个方法涉及到缓存,他们的参数中都有enginekey
loadFromCache:得到的cache是最近使用过的,而当前不再使用的,内部使用LinkedHashMap,当大小大于阈值之后,会通过LRU算法去清除
getEngineResourceFromCache中获取缓存,

loadFromActiveResources:得到是当前被使用的engineResource对象,它是一个HashMap,以engineResources弱引用为值。

load方法中:首先从内存缓存中去读取要加载的图片,如果有则直接进行回调onResourceReady。如果没有获得缓存,则调用loadFromActiveResources,即在缓存中如果没有,则从正在使用的图片中去找,如果找到调用onResouceReady。只有这两种情况都没有找到的情况下,才会去创建EngineJob去加载所需要的图片。

ResourcesCallback接口中的方法,onResourceReady,具体实现在EngineJob中,在它的onResourceReady方法中会定义一个MAIN_THREAD_HANDLER来发送message,在创建这个Handler的时候,传入一个EngineJob.MainThreadCallback,这个Callback里面有handleMessage,当消息为1时,调用handleResultOnMainThread。总结起来,就是通过Handler来发送一条消息,然后切换到主线程中。

handleResultOnMainThread:首先判断是否被取消了,如果被取消,就recycle回收资源。之后,会通过engineResourceFactory创建一个包含图片资源的engineResources对象,然后engine.acquire(里面变量++,就代表图片被引用的次数,值大于0,表示有图片正在使用中,即也会将图片放入ActiveResources弱引用缓存中),之后listen.onEngineJboComplete将结果回调。在onEngineJobComplete中会调用activeResources.put方法(此时写入的是弱引用缓存)

engineResources.release:里面对acquire–,当减为0时,即此时图片不再被使用,然后调用listener.onResourceReleased方法,在它的实现类中,Engine#onResourcesReleased中,首先activeResources.remove,即先从Resources中删除,然后才会调用cache.put方法,将图片加入到LRUCache缓存中。

从而实现,正在使用的图片,使用弱引用来缓存,不再使用的图片,使用LRUCache来缓存。

load方法总结
首先调用loadFromCache,尝试从MemoryCache中获取图片资源,如果命中缓存,则将图片从MemoryCache中移除,然后放入到ActiveResources中。
如果缓存没有得到,则会从ActiveResources中去获取,只有这个两种缓存都失效。则会开启EngineJob(是一个Runnable)从磁盘或者网络中获取。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值