glide源码中包含了那种设计模式_Glide源码浅析

以下glide源码基于库上最新的4.11版本,

以下很多代码没有写中间的层层调用,因为层级太多了,感兴趣的可以自己通过debugu模式去看或者直接看源码。

上一篇文章说到图片缓存的LRU的原理分析,并简单的说了下glide的使用,今天就开门见山,先看最简单的使用示例:

Glide.with(this)

.load(url).diskCacheStrategy(DiskCacheStrategy.ALL)

.error(R.drawable.icon).placeholder(R.drawable.frank).into(img);

总体介绍

首先看看4.11版本的整体代码:

整体框架图.png

是非常多的,要一个个分析不现实,咱们就抓几个重点理解一下好了。

with()

还是从最初的with方法来分析:

with多种参数类型.png

with可以传入很多种类型的,例如activity,fragment,context,view等,其实都大同小异,都是为了和这个参数类型的生命周期进行绑定。

public static RequestManager with(@NonNull FragmentActivity activity) {

return getRetriever(activity).get(activity);

}

先分析下getRetriever(activity)吧:

private static RequestManagerRetriever getRetriever(@Nullable Context context) {

// 检查context不能为空

···

return Glide.get(context).getRequestManagerRetriever();

}

这里其实就是先对context做了一个非空判断,继续往下看:

public static Glide get(@NonNull Context context) {

if (glide == null) {

//通过反射到GeneratedAppGlideModuleImpl,然后传递给下一个方法

GeneratedAppGlideModule annotationGeneratedModule =

getAnnotationGeneratedGlideModules(context.getApplicationContext());

synchronized (Glide.class) {

if (glide == null) {

//初始化glide的各种参数

checkAndInitializeGlide(context, annotationGeneratedModule);

}

}

}

return glide;

}

private static GeneratedAppGlideModule getAnnotationGeneratedGlideModules(Context context) {

···

//通过反射拿到GeneratedAppGlideModuleImpl类

Class clazz =

(Class)

Class.forName("com.bumptech.glide.GeneratedAppGlideModuleImpl");

result =

clazz.getDeclaredConstructor(Context.class).newInstance(context.getApplicationContext());

···

return result;

}

通过反射拿到了GeneratedAppGlideModuleImpl类,初始化glide的动作,拿到Glide之后,再去获取RequestManagerRetriever。拿到了RequestManagerRetriever之后,要去get了,可以看到get其实就是获取一个requestManager。

public RequestManager get(@NonNull FragmentActivity activity) {

if (Util.isOnBackgroundThread()) {

//如果是在子线程,获取全局的context,并获取requestManager

return get(activity.getApplicationContext());

} else {

//如果是在主线程,调用supportFragmentGet获取requestManager

assertNotDestroyed(activity);

FragmentManager fm = activity.getSupportFragmentManager();

return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));

}

}

具体一点说,分两步走:

1.先会去判断是否在子线程,如果在子线程调用的会去获取一个全局的context,然后用这个全局的context,通过工厂模式拿到requestManager,所以尽量不要在非主线程中去调用。

private RequestManager getApplicationManager(@NonNull Context context) {

// Either an application context or we're on a background thread.

if (applicationManager == null) {

synchronized (this) {

if (applicationManager == null) {

Glide glide = Glide.get(context.getApplicationContext());

//因为正常情况下我们在fragment或activity中可以去绑定生命周期,

//但是这里是在子线程或者没有拿到全局的context,所以需要手动新建一个全局的生命周期管理

applicationManager =

factory.build(

glide,

new ApplicationLifecycle(),

new EmptyRequestManagerTreeNode(),

context.getApplicationContext());

}

}

}

return applicationManager;

}

2.如果是在主线程:

++with的参数是fragmentActivity++:首先获取一个fragmentManager,通过fragmentManager新建一个空的fragment,把这个fragment添加到activity或者fragment中,这样就能感应宿主的生命周期!然后还是一样的,用工厂模式去新建一个requestManager。

++with的参数是fragment++:是获取childFragmentManager,其余流程不变;

++with的参数是Activity++:先获取fragmentManager,然后获取requestManagerFragment和requestMangaer;所以和fragment的区别,其实就是是获取supportFragment,还是fragment...

++with的参数是context或view++:判断context的类型,然后走上面的流程;

以下代码是with参数为fragmentActivity时的示例:

private RequestManager supportFragmentGet(

@NonNull Context context,

@NonNull FragmentManager fm,

@Nullable Fragment parentHint,

boolean isParentVisible) {

//获取一个fragment,这个fragment作为感知生命周期的感应者

SupportRequestManagerFragment current =

getSupportRequestManagerFragment(fm, parentHint, isParentVisible);

RequestManager requestManager = current.getRequestManager();

if (requestManager == null) {

//获取glide

Glide glide = Glide.get(context);

//用工厂模式创建一个manager。

requestManager =

factory.build(

glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);

current.setRequestManager(requestManager);

}

return requestManager;

}

private SupportRequestManagerFragment getSupportRequestManagerFragment(

@NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {

//获取fragment

SupportRequestManagerFragment current =

(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);

if (current == null) {

//先判断fragments里面是否有了fragment

current = pendingSupportRequestManagerFragments.get(fm);

if (current == null) {

//没有的话new一个fragment

current = new SupportRequestManagerFragment();

current.setParentFragmentHint(parentHint);

if (isParentVisible) {

//如果父界面是可见的,开始生命周期,并调用onStart;

current.getGlideLifecycle().onStart();

}

//并讲当前的fragment添加到队列中

pendingSupportRequestManagerFragments.put(fm, current);

fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();

//这一步是将fragmentmanager remove!

handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();

}

}

return current;

}

with小结

以上就是Glide.with()发生的事情,稍微小结一下:

with方法会先初始化glide一些所需要的环境,然后调用requestManagerRetriever对象的get,获取requestManager。如果传入的对象是全局的context,则不需要处理生命周期;如果传入的不是全局的context,会添加一个隐藏的fragment去感知生命周期。

下面我们继续分析一个很重要的load(),看看是如何加载资源的:

load()

load同样的,可以传入很多种类型的参数:

load各种参数类型.png

那么可以看到,其实最终都是返回的RequestBuilder。

用file举例:

public RequestBuilder load(@Nullable File file) {

return asDrawable().load(file);

}

public RequestBuilder asDrawable() {

return as(Drawable.class);

}

public RequestBuilder as(

@NonNull Class resourceClass) {

return new RequestBuilder<>(glide, this, resourceClass, context);

}

public RequestBuilder load(@Nullable File file) {

return loadGeneric(file);

}

可以看到方法都很短哈,asDrawable就是获取一个RequestBuilder,然后用这个builder去loadGeneric。来看看这个方法是干啥的:

private RequestBuilder loadGeneric(@Nullable Object model) {

this.model = model;

isModelSet = true;

return this;

}

ok...只是赋值了全局变量···初看的时候觉得也有点绕。那我们先跳过这一块,直接进入最终的into();等会会频繁地介绍到requestBuilder这个类;

into()

这里以into(imageview)举例:

public ViewTarget into(@NonNull ImageView view) {

Util.assertMainThread();

···

return into(

glideContext.buildImageViewTarget(view, transcodeClass),

/*targetListener=*/ null,

requestOptions,

Executors.mainThreadExecutor());

}

省略中间的一大堆代码,我们重点看下最终调用的方法里有个

glideContext.buildImageViewTarget(view, transcodeClass),这个返回的是DrawableImageViewTarget

public ViewTarget buildImageViewTarget(

@NonNull ImageView imageView, @NonNull Class transcodeClass) {

return imageViewTargetFactory.buildTarget(imageView, transcodeClass);

}

public ViewTarget buildTarget(

@NonNull ImageView view, @NonNull Class clazz) {

if (Bitmap.class.equals(clazz)) {

return (ViewTarget) new BitmapImageViewTarget(view);

} else if (Drawable.class.isAssignableFrom(clazz)) {

return (ViewTarget) new DrawableImageViewTarget(view);

}

}

继续看看into():

private > Y into(

@NonNull Y target,

@Nullable RequestListener targetListener,

BaseRequestOptions> options,

Executor callbackExecutor) {

···

//初始化一个请求,这里会去判断是否需要缩略图,从而获取的request不一样

Request request = buildRequest(target, targetListener, options, callbackExecutor);

//从target中获取request

Request previous = target.getRequest();

//检查request和之前的request是否一致

if (request.isEquivalentTo(previous)

&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {

//检查请求是否开始了,如果开始了,则不用new一个,直接执行begin;

if (!Preconditions.checkNotNull(previous).isRunning()) {

previous.begin();

}

return target;

}

//如果之前没有对应的request,则先clear,层层查看发现最终调用的是

//,request.clear已经将target的request设置为null

requestManager.clear(target);

//设置request为刚刚初始化的request,这里setRequest其实看到后面

//发现是调用的view.setTag,将request和view进行的一个绑定;

target.setRequest(request);

//这里的track分两个,一个是targetTrack进行生命周期的绑定

//还有一个是requestTracker执行了request.track。

requestManager.track(target, request);

return target;

}

上面是into的核心代码,看起来挺简单的,其实实现复杂。首先看buildRequest是如何初始化request的,代码很多,先看些简单的:

manager.track()

synchronized void track(@NonNull Target> target, @NonNull Request request) {

targetTracker.track(target);

requestTracker.runRequest(request);

}

public void runRequest(@NonNull Request request) {

requests.add(request);

//判断是否界面是否在pause状态,是的话不开始加载,不是的话立刻开始

if (!isPaused) {

request.begin();

} else {

request.clear();

if (Log.isLoggable(TAG, Log.VERBOSE)) {

Log.v(TAG, "Paused, delaying request");

}

pendingRequests.add(request);

}

}

可以看到,这里glide会去判断界面是否在显示中,如果是在显示中,立刻去执行begin,否则执行clear,并将请求放入到队列中。这个设计还是比较巧妙的,节省功耗;

再来看看是如何初始一个request的

buildRequest()

private Request buildRequestRecursive(···

Executor callbackExecutor) {

···

Request mainRequest =

buildThumbnailRequestRecursive(···);

···

Request errorRequest =

errorBuilder.buildRequestRecursive(···);

errorRequestCoordinator.setRequests(mainRequest, errorRequest);

return errorRequestCoordinator;

}

层层查看,发现最终返回的是singleRequest。

private SingleRequest(··Executor callbackExecutor) {

this.requestLock = requestLock;

···

this.engine = engine;

this.callbackExecutor = callbackExecutor;

status = Status.PENDING;

···

}

来看看,先前说的很重要很重要的方法begin是做了哪些事情:

public void begin() {

synchronized (requestLock) {

···

if (model == null) {

···

//这个最终调用的是设置错误图片

onLoadFailed(new GlideException("Received null model"), logLevel);

return;

}

···

if (status == Status.COMPLETE) {

//加载完成了就直接调用onResourceReady

onResourceReady(resource, DataSource.MEMORY_CACHE);

return;

}

status = Status.WAITING_FOR_SIZE;

if (Util.isValidDimensions(overrideWidth, overrideHeight)) {

onSizeReady(overrideWidth, overrideHeight);

} else {

target.getSize(this);

}

if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)

&& canNotifyStatusChanged()) {

target.onLoadStarted(getPlaceholderDrawable());

}

···

}

}

这里出现几个方法,(先说结论,层层查看的代码太多了,哈哈哈)简单地说下几个方法的意义:

1.onLoadFailed:当发现model为null时,直接给你报个错,设置个错误图片,model是source来源,所以source都为空了,报错是肯定的啦。

2.onResourceReady:通过层层查看,可以看到,最终调用的还是ViewTarget的onResourceReady,这个里面是直接setImageDrawale,最终再去执行engine的release方法;

3.onSizeReady:执行engine.load代码

4.getSize:最终调用的是view的自己的计算尺寸

5.onLoadStarted:view的依附变化回调

下面来验证下我们的结论,看看onSizeReady的源码

public void onSizeReady(int width, int height) {

stateVerifier.throwIfRecycled();

synchronized (requestLock) {

···

loadStatus =

engine.load(

glideContext,

model,

···

priority,

requestOptions.getDiskCacheStrategy(),

requestOptions.getTransformations(),

···

);

···

}

}

好家伙,又出来一个新的engine。大胆猜测小心求证,engine应该是真正负责加载图片的工具人,哦不对,工具类!直接看engine.load做了啥:

public LoadStatus load(···) {

···

EngineResource> memoryResource;

synchronized (this) {

//先从缓存中去取,有就直接拿,没有就新建

memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);

if (memoryResource == null) {

return waitForExistingOrStartNewJob(

glideContext,

model,

···);

}

}

//这里也调用了onResourceReady

cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);

return null;

}

会先去缓存中取,有就直接用,执行OnResourceReady。

没有,就执行waitForExistingOrStartNewJob():

private LoadStatus waitForExistingOrStartNewJob(···) {

···

EngineJob engineJob =

engineJobFactory.build(

key,

isMemoryCacheable,···);

DecodeJob decodeJob =

decodeJobFactory.build(

model,

key,

···

engineJob);

jobs.put(key, engineJob);

engineJob.addCallback(cb, callbackExecutor);

engineJob.start(decodeJob);

if (VERBOSE_IS_LOGGABLE) {

logWithTimeAndKey("Started new load", startTime, key);

}

return new LoadStatus(cb, engineJob);

}

出现了两个关键的类:

engineJob和decodeJob,先剧透一下,decodeJob是一个runnable,主要负责解析工作,engineJob是负责加载过程中的管理一些回调之类的。先看engineJob.start做了什么:

public synchronized void start(DecodeJob decodeJob) {

this.decodeJob = decodeJob;

GlideExecutor executor =

decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();

executor.execute(decodeJob);

}

GlideExecutor是个继承了ExcutorService的类,很明显是个线程池。而且这里通过decodeJob来判断是不是从缓存中解析,如果是从缓存中解析,调用diskCacheExecutor,否则调用getActiveSourceExecutor;

再来看看decodeJob的run方法:

public void run() {

···

runWrapped();

···

if (stage != Stage.ENCODE) {

throwables.add(t);

notifyFailed();

}

if (!isCancelled) {

throw t;

}

throw t;

}

继续看runWrapped():

private void runWrapped() {

switch (runReason) {

case INITIALIZE:

stage = getNextStage(Stage.INITIALIZE);

currentGenerator = getNextGenerator();

runGenerators();

break;

case SWITCH_TO_SOURCE_SERVICE:

runGenerators();

break;

case DECODE_DATA:

decodeFromRetrievedData();

break;

default:

throw new IllegalStateException("Unrecognized run reason: " + runReason);

}

}

会去判断run的原因:分为三种,初始化,转化为source_service,还有decode_data:

这里我们先看initialize的runGenerators():

private void runGenerators() {

currentThread = Thread.currentThread();

startFetchTime = LogTime.getLogTime();

boolean isStarted = false;

while (!isCancelled

&& currentGenerator != null

&& !(isStarted = currentGenerator.startNext())) {

stage = getNextStage(stage);

currentGenerator = getNextGenerator();

if (stage == Stage.SOURCE) {

reschedule();

return;

}

···

}

}

出现了1个新的类:currentGenerator,实现了DataFetcherGenerator的接口,这个接口主要是用来产生一系列的modelLoader和model。哈哈,又来了两个不认识的类,还是先剧透下,这两个类其实就是根据不同的source来判断是选择用哪种方式来加载资源的,后面还会牵扯到一个叫streamFactory的类。

首先目前glide的版本中有三类generator:dataCache,resourceCache,sourceCache,顾名思义,也是根据三种来源的,就先看下source的startnext是如何实现的:

public boolean startNext() {

if (dataToCache != null) {

Object data = dataToCache;

dataToCache = null;

cacheData(data);

}

if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {

return true;

}

···

return started;

}

会先去判断,cache如果不为空,则调用cacheData;否则,获取loadData,然后执行startNextLoad()。loadData是什么?我们上面已经有图片了,这里直接说下loadData的fetcher是真正来处理图片的加载的:

private void cacheData(Object dataToCache) {

···

try {

//获取sourceencoder

Encoder encoder = helper.getSourceEncoder(dataToCache);

//new 一个 dataCacheWriter

DataCacheWriter writer =

new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());

originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());

helper.getDiskCache().put(originalKey, writer);

···

} finally {

loadData.fetcher.cleanup();

}

sourceCacheGenerator =

new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);

}

这里先说个结论:调用cacheData会先去清理数据,然后返回一个generator;

finally方法块里最终调用了loadData.fetcher.cleanup();出现了熟悉的身影,loadData,先搞清楚loadData是个啥吧:

ModelLoader.LoadData。这里只截取部分代码图:

modeLoader和loadData.png

前面说过了,loadData和model是将来源和使用方法通过entry绑定在一起的,这里再扩展下,model,loadData是在何时绑定的:

loadData注册时间

在cacheData里出现了getSourceEncoder来获取encoder,从这里开始看:

Encoder getSourceEncoder(X data) throws Registry.NoSourceEncoderAvailableException {

return glideContext.getRegistry().getSourceEncoder(data);

}

通过context获取registry,然后再去获取Encoder;

public synchronized Encoder getEncoder(@NonNull Class dataClass) {

for (Entry> entry : encoders) {

if (entry.handles(dataClass)) {

return (Encoder) entry.encoder;

}

}

return null;

}

这个registry是啥呢...我们看看这个类中有两个方法append和prepend:

看看是谁在调用它的,经过层层调查,最终发现是在这调用的:

Glide的构造方法中!调用了好多append,来进行注册动作!

registry

.append(ByteBuffer.class, new ByteBufferEncoder())

.append(InputStream.class, new StreamEncoder(arrayPool))

/* Bitmaps */

.append(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder)

.append(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class, streamBitmapDecoder);

所以回到前面的getSourceEncoder,就是每个数据类型对应一个encoder;

ModelLoader,LoadData

modelLoader是将各种类型的数据转化为resource;

LoadData是modelLoader的内部类;

class LoadData {

public final Key sourceKey;

public final List alternateKeys;

public final DataFetcher fetcher;

···

}

这个fetcher其实就承担了加载这一块的工作,我们举个例子分析下fetacher的结构:

HttpUrlFetcher

public void loadData(···DataCallback super InputStream> callback) {

try {

InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());

callback.onDataReady(result);

} finally {···

}

}

loadData里调用到了loadDataWithRedirects来获取一个inputStream。

private InputStream loadDataWithRedirects(

URL url,···, Map headers) throws IOException {

···

urlConnection = connectionFactory.build(url);

···

urlConnection.setConnectTimeout(timeout);

···

final int statusCode = urlConnection.getResponseCode();

if (isHttpOk(statusCode)) {

return getStreamForSuccessfulRequest(urlConnection);

}···

}

省去一大堆的网络通信,咱们知道最后是调用了HttpUrlConnection来获取流的就行了。拿到流后去进行一系列的操作进行设置图片。代码特别多,一篇文章讲不完,这篇文章的源码级解析就先到这了。

总结

Glide的设计是非常完美,非常经典的。glide总体来说不是仅仅想做个图片的框架,它是一个可以将一个资源转换成不同形式的框架。下一篇文章来分析下glide的缓存机制。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值