最新源码Glide4.12框架之加载图片流程源码分析
Posted hymKing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最新源码Glide4.12框架之加载图片流程源码分析相关的知识,希望对你有一定的参考价值。
一、前言
android图片加载框架,在android应用开发中是一个常见的话题。在12、13年的时候我记得可能用的最多的是XUtils的一套框架(更早之前叫aFinal框架),这个框架中提供imageUtils用于在android应用的开发中完成远程图片的加载。再后来呢,有Picasso、Fresco、Glide。而这几年的开发经验来看,Glide最为流行。不信,可以查看github上项目地址,分别对比对比watch数、fork数、star数,就能确定Glide确实最为流行。
如何对图片框架技术进行选型?
通过这种对比watch、fork、star数的方式,可以作为我们开发一款非特定需求应用的技术选型方式,毕竟对比清楚每一个框架之间的差异,是一件比较耗时、也是不容易的事情。除了图片加载框架,比如我们平常在为了解决项目中某些其它通用轮子问题的时候,我们可能逛街github,挑选免费的开源源码商品,这个时候,watch、fork、star数也可以作为我们选定某个框架的依据。一般即使是小众一点轮子开源源码star数也至少大于1000,我们才能考虑选型,star数代表阅读过此项目的同学的认可。另外一点,就是看issues的活跃度情况,比如有多少个issues,回复的及时性等情况如何。
上面一部分,介绍Android端图片加载框架的简单历程,插入了对于技术选型的非技术角度的手段的选型技巧概述。接下来我们还是开始介绍一下最流行的Glide图片加载框架。
Glide图片加载框架的优势有哪些?
Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。Glide提供了易用的API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。
Glide 支持拉取,解码和展示视频快照,图片,和GIF动画。Glide的Api是如此的灵活,开发者甚至可以插入和替换成自己喜爱的任何网络栈。默认情况下,Glide使用的是一个定制化的基于HttpUrlConnection
的栈,但同时也提供了与Google Volley和Square OkHttp快速集成的工具库。
虽然Glide 的主要目标是让任何形式的图片列表的滚动尽可能地变得更快、更平滑,但实际上,Glide几乎能满足你对远程图片的拉取/缩放/显示的一切需求。
以上是官方文档对Glide的介绍内容,末尾四个字硬气的很,“一切需求”。因为这篇是源码分析篇,不会过多介绍Glide图片加载框架的使用。
本篇文章的下文主要分为两个部分:(文章内容太长,回过头来补充说明下)
**第一部分:**按照图片的加载流程通读源码实现,并进行注释解释说明。(这部分内容比较枯燥,读者可以自己追踪源码走一遍流程。)
**第二部分:**对整体的图片加载流程进行概括,梳理核心流程,并绘制加载流程图。
另外,除了本篇的加载流程分析之外,会计划Glide源码在设计层面的一些解析和分析,包括Glide生命周期的设计分析、Glide缓存的设计分析。
二、Glide框架图片加载流程原理分析
源码版本为:4.12.0,为当前最新源码版本。
接下来我以一个最简单的Demo调用作为源码分析的入口,开始源码分析。
public class MainActivity extends AppCompatActivity
String url = "";
ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
imageView = findViewById(R.id.img_glide);
//通过链式调用的方式加载一张图片
Glide.with(this).load(url).into(imageView);
//with、load、into三个方法分解后的调用
void glideLoadImg()
RequestManager requestManager = Glide.with(this);
RequestBuilder requestBuilder=requestManager.load(url);
requestBuilder.into(imageView);
2.1 with方法获得RequestManger的对象
上述代码链式调用的代码,分解成了glideLoadImg()方法中的三行代码的调用,首先我们看Glide.with(this)方法的源码。
/**
* Glide是一个单例,简单的静态实现。作用是为RequestBuilder构建Requests和维护Engine、
* BitmapPool以及DiskCache和MemoryCache
*/
public class Glide implements ComponentCallbacks2
/**
* 获取的requestManager,在load之前需要调用,和Activity的lifecycle进行关联
*/
@NonNull
public static RequestManager with(@NonNull Activity activity)
return getRetriever(activity).get(activity);
这个with方法的调用,最终返回的是一个RequestManager对象实例,RequestManager对象的获取是通过RequestManagerRetriever.get()方法获取。RequestManagerRetriever对象的职责就是用来创建和获取RequestMangaer对象的,在RequestManager内部通过RequestManagerFactor工厂进行最终的创建。
/**
* 创建一个新的requestManager或者从现有的Activity、fragment中检索一个已经存在ReqeustManager
*
*/
public class RequestManagerRetriever implements Handler.Callback
private RequestManager fragmentGet(...)
...
//获取已经存在的RequestManager
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
//没有获取到,通过工厂创建
if (requestManager == null)
//获取单例的Glide对象
Glide glide = Glide.get(context);
//通过工厂创建requestManager
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
if (isParentVisible)
requestManager.onStart();
//绑定
current.setRequestManager(requestManager);
return requestManager;
/**
*
* @see Glide#with(android.app.Activity)
* @see Glide#with(androidx.fragment.app.FragmentActivity)
* @see Glide#with(android.app.Fragment)
* @see Glide#with(androidx.fragment.app.Fragment)
* @see Glide#with(Context)
*/
public class RequestManager
@Override
public synchronized void onStart()
*/
@Override
public synchronized void onStop()
@Override
public synchronized void onDestroy()
RequestManager有上述5种重载方法可以得到,最终都会调用到fragmentGet()方法,整体流程是一致的。
RequestManager是一个Glide框架管理和开始请求的一个类。可使用Activity、Fragment以及关联其生命周期事件去智能的停止、开始、重新开始请求。可以理解成Glide的生命周期的管理类,在上述简化版的源码中,我们也看到了类似Activity生命周期的回调方法 onStart()、onStop()、onDestoy()。
2.2 load方法获取RequestBuilder对象
requestBuilder对象的获取是通过RequestManager来完成的,看如下源码实现
public class RequestManager ...
/**
* 这里面也是链式调用,相当于先调用了asDrawable()方法,然后调用requestBuilder.load(String)
* 返回一个新的RequestBuilder去加载使用指定的model去加载drawable
*/
@Override
public RequestBuilder<Drawable> load(@Nullable String string)
return asDrawable().load(string);
上述asDrawable()、load()方法的调用都是返回一个RequestBuilder对象,最终链式调用的结果也是返回一个RequestBuilder的对象。load()方法的实现在RequestBuilder类源文件中。
public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>>
//返回的是一个加载给定字符串的requestBuilder对象
public RequestBuilder<TranscodeType> load(@Nullable String string)
return loadGeneric(string);
@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model)
if (isAutoCloneEnabled())
return clone().loadGeneric(model);
this.model = model;
isModelSet = true;
return selfOrThrowIfLocked();
总之,从上述源码中可以看到,通过两次链式调用后,返回ReqeustBuilder实例,内部调用loadGeneric()对一些全局属性进行了设置,最终以RequestBuilder对象返回。
RequestBuilder:一个通用类,可以处理通用资源类型的设置选项和启动加载。说白了,为后续环节真正的发起图片加载做准备工作的。
2.3 into方法的核心实现
into方法,是我们通过Glide加载图片调用的最后一个方法,with方法、load方法可以理解成都是为into方法的核心逻辑做准备的,into方法中会涉及到发起远程图片加载的核心逻辑。看RequestBuilder.into()方法的源码实现:
/**
* into方法接受的参数是imageView,imageView就是资源(url\\uri...)要被载入的目标
* 取消任何现有的加载到视图中,并释放 Glide 之前可能加载到视图中的任何资源,以便它们可以被重用。
* Return:方法返回的ViewTarget是传入参数imageView的包装
*/
@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view)
....
BaseRequestOptions<?> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null)
// View ScaleType的设置
switch (view.getScaleType())
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
....
default:
// Do nothing.
//会继续调用下面的重载的into方法
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
//主线程池
Executors.mainThreadExecutor());
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor)
Preconditions.checkNotNull(target);
/**这个isModelSet是在load方法中设置为True的,如果未调用load方法直接调用into方法抛出异常
*我们自己在设计一些类中的方法时,如果需要做校验,也可以通过这种抛出异常的方法,来提醒调用者
*使用正确的方式调用*/
if (!isModelSet)
throw new IllegalArgumentException("You must call #load() before calling #into()");
//构建Request
Request request = buildRequest(target, targetListener, options, callbackExecutor);
//获取当前目标对象上先前是否已存在的Request
Request previous = target.getRequest();
//如果这两个request相同&&不跳过内存缓存
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)
/**这个方法的细节,可以查看源码注释*/)
if (!Preconditions.checkNotNull(previous).isRunning())
previous.begin();
//目标request可复用,直接返回
return target;
requestManager.clear(target);
//给目标设置新创建的Request
target.setRequest(request);
//通过RequestManager调用track方法
requestManager.track(target, request);
return target;
当我们调用RequestBuilder.into方法时,RuquestBuilder对象帮我们构造或是重用Request实例对象。最终将request实例通过requestManger.track()方法传入。接下来就重新回到RequestManger方法中来。
public class RequestManager...
synchronized void track(@NonNull Target<?> target, @NonNull Request request)
targetTracker.track(target);
//看到这个方法,是不是感觉真实的请求调用就在此了
requestTracker.runRequest(request);
这里面仍然有一个问题,我们没有去分析,就是在上一段源码中,Request对象的构建过程是怎样的?
2.3.1 requestTracker请求调用的开始
我们先先顺着看requestTracker.runRequest(request)的方法的实现:
public class RequestTracker
//请求队列
private final Set<Request> requests =
Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
//等待队列
private final List<Request> pendingRequests = new ArrayList<>();
/** 开始追踪一个指定的请求 */
public void runRequest(@NonNull Request request)
//将request添加到请求队列
requests.add(request);
if (!isPaused)
//没有被pause,开始发起请求
request.begin();
else
//如果pause了,清理请求
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE))
Log.v(TAG, "Paused, delaying request");
//将其添加到等待队列
pendingRequests.add(request);
上面的逻辑是先为 requests 添加一个请求,看看是否是停止状态,如果不是就调用request.begin(),核心调用逻辑应该就是是request.begin()方法。
查看调用发现,Request只是一个定义了请求相关抽象的接口。Request实现类有ErrorRequestCoordinator,ThumbnailRequestCoordinator,这两个实现类感觉是和Error、Thumbnail相关的,不应该是主流程,另一个实现类是SingleRequest ,上面有提到“Request对象的构建过程是怎样的?”,通过查看request对象的构建过程,我们最终能确认,最终构建返回的就是SingleRequest的一个实例。
看singleRequest.begin()方法的实现:
public final class SingleRequest<R> implements Request, SizeReadyCallback, ResourceCallback
@Override
public void begin()
synchronized (requestLock)
....
if (status == Status.RUNNING)
throw new IllegalArgumentException("Cannot restart a running request");
//表示自愿已经准备完成,直接回调回去
if (status == Status.COMPLETE)
onResourceReady(
resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return;
//如果上面的逻辑没有被返回,则当成一个新的请求走下面的逻辑
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight))
//size校验,合法大小,开始加载
onSizeReady(overrideWidth, overrideHeight);
else
//size为校验好,就获取size
target.getSize(this);
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged())
//给目标对象回调图片加载开始,可以理解成图片起点的回调
target.onLoadStarted(getPlaceholderDrawable());
.....
2.3.2 执行引擎Engine的调度
上述源码中,在大小校验合法的情况下,开始加载的流程是回调了onSizeReady()方法。
@Override
public void onSizeReady(int width, int height)
stateVerifier.throwIfRecycled();
synchronized (requestLock)
...
status = Status.RUNNING;
...
loadStatus =
//这里将请求任务交给engine
engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
//各种请求选项,这里没搞明白为啥不直接把requestOptions作为参数呢
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
....
接下来调用的是Engine.load()方法:
/** 响应开始加载的请求和管理活跃和缓存的资源 */
public class Engine
implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener
//引擎中load方法的实现
public <R> LoadStatus load(...一堆参数(说实话没明白为啥要定义这么多参数的方法)
)
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
//使用key工厂构建一个key
EngineKey key =
keyFactory.buildKey(
....);
//声明一个内存资源对象
EngineResource<?> memoryResource;
synchronized (this)
//从内存中获取资源,内部实现了获取活动资源和cache资源,都属于内存缓存
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
//从内存缓存中未获取到资源
if (memoryResource == null)
//等待一个正在执行的符合条件的任务or启动一个新任务
return waitForExistingOrStartNewJob(
....一堆参数);
//获取到了资源直接回调出去
cb.onResourceReady(
memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return null;
/**等待一个正在执行的符合条件的任务or启动一个新任务*/
private <R> LoadStatus waitForExistingOrStartNewJob(
....一堆参数....)
//获得是否有符合条件的正在执行的任务(这里的条件判断的就是通过key来判断)
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
//获得到
if (current != null)
//添加回调并返回
current.addCallback(cb, callbackExecutor);
return new LoadStatus(cb, current);
//执行到这,说明没有可用的缓存,也没有正在执行的job和当前的请求是一致的,于是创建新的工作引擎
EngineJob<R> engineJob =
//通过引擎工厂构建
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
//创建一个解析资源的工作引擎
以上是关于最新源码Glide4.12框架之加载图片流程源码分析的主要内容,如果未能解决你的问题,请参考以下文章