android glide代码,Android Glide 源码分析和学习

1.引言

最近在开发产品过程中,项目经理提了一个问题:"为什么图片加载这么缓慢?",我看了看的确非常缓慢,图片加载用的经典的Glide框架,慢难道仅仅只是网络的原因?带着这份不解,我下了一个glide源码,开启了学习之路。

2.正题

通过学习想彻底弄懂这几个问题:

Glide加载流程

Glide切换Activity,是否有中断和恢复操作

Glide的内存管理

Glide的弱网管理机制

2.1 Glide总体加载流程

64fab1a85487

未命名文件.jpg

Glide框架就像是一条流水线,每个环节做什么,每个环节为下一步输出什么。都非常清楚明了。把握其中的主要流程和环节,那么搞懂全貌只是时间问题。

接下来我们以下面的代码为基础开始正式分析我们的glide源码加载流程:

Glide.with(Context context)

.load(Strint url)

.into(ImageView imageView);

2.1.1 Request产生阶段

Glide类:

Glide类和OkhttpClient一样,首先都是一个单例,目的是为整个框架做初始化以及为使用者提供唯一的使用窗口:

Glide.with 接受如下几个参数:

@see #with(android.app.Activity)

@see #with(android.app.Fragment)

@see #with(androidx.fragment.app.Fragment)

@see #with(androidx.fragment.app.FragmentActivity)

不同的参数,所代表的lifecycle也就不同。这样在生命周期处理这块就有所不同。with()方法最终会调用RequestManagerRetriever的get方法得到一个RequestManager

RequestManagerRetriever类:

RequestManagerRetriever类中是直接生产RequestManager的,生产主要是靠RequestManagerFactory。其次调用checkAndInitializeGlide()来初始化Glide

GlideBuilder类:

checkAndInitializeGlide()方法最终会调用到GlideBuilder.build. 对整个Glide框架中的GlideExecutor,加载引擎Engine,DiskCache 等做初始化。一个非常典型的建造者模式使用场景

RequestManager类:

RequestManager以Manager,肯定是包含了:

请求的创建,setRequestOptions

请求的管理队列 移除、等待

请求开始、请求暂停、取消、重新开始,设置RequestListener

请求的暂停、取消等是通过:RequestTracker类去跟踪处理

请求的创建以及各种配置是通过:RequestBuilder类完成

RequestBuilder类:

为Request设置完毕RequestOptions之后就会开始,进行build得到真正的Request对象。这一步骤是通过调用into(Target t)方法。

private > Y into(

@NonNull Y target,

@Nullable RequestListener targetListener,

BaseRequestOptions> options,

Executor callbackExecutor) {

Preconditions.checkNotNull(target);

if (!isModelSet) {

throw new IllegalArgumentException("You must call #load() before calling #into()");

}

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

Request previous = target.getRequest();//判断此请求是否被加载过

if (request.isEquivalentTo(previous)

&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {

// If the request is completed, beginning again will ensure the result is re-delivered,

// triggering RequestListeners and Targets. If the request is failed, beginning again will

// restart the request, giving it another chance to complete. If the request is already

// running, we can let it continue running without interruption.

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

// Use the previous request rather than the new one to allow for optimizations like skipping

// setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions

// that are done in the individual Request.

previous.begin();//假如Request存在的话 就直接走begin开始走Engin流程

}

return target;

}

requestManager.clear(target);

target.setRequest(request);

requestManager.track(target, request);//开启网络请求

return target;

}

这一步骤应该有俩个疑问:

传入的ImageView如何转换成Target的

buildRequest方法中是如何产生一个Request的

解答1:

load(ImageView )方法最终会调用GlideContext.buildImageViewTarget 方法转换得到Target。源码如下:

public class ImageViewTargetFactory {

@NonNull

@SuppressWarnings("unchecked")

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

} else {

throw new IllegalArgumentException(

"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");

}

}

}

解答2:

Request是通过GlideContext.buildRequestRecursive 产生的。Request是一个抽象类:具体的实现有这几个:

64fab1a85487

image.png

根据配置不同生成的Request就不同。一般网络请求产生的是SingleRequest; 到这里 前期工作做好了,Request也产生了。于是开始交给Engine引擎去深层次加工。

小结:Glide前期代码执行流程:

64fab1a85487

image.png

2.1.2 加载Request

上面得到Request之后,会调用RequestManager.track()开启 加载。RequestManager中对请求的管理。最终都是通过RequestTracker实现。

RequestTracker类:

作用:开始、暂停、取消请求

64fab1a85487

RunRequest方法 代码如下:

/** Starts tracking the given request. */

public void runRequest(@NonNull Request request) {

requests.add(request);//添加Request到set中

if (!isPaused) {

request.begin();//开启真正的请求

} else {

request.clear();

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

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

}

pendingRequests.add(request);

}

}

SingleRequest类:

由上面我们知道了,产生的request是SingleRequest.我们来看下SingleRequest是干什么的。

64fab1a85487

image.png

每个SingleRequest都有这6中状态。SingleRequest类有一个Status变量,用来标记当前SingleRequst的状态。

SingleRequest#begin方法如下:

@Override

public void begin() {

synchronized (requestLock) {

//加载完成从内存中去取

if (status == Status.COMPLETE) {

onResourceReady(

resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false); return;

}

// Restarts for requests that are neither complete nor running can be treated as new requests

// and can run again from the beginning.

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

}

if (IS_VERBOSE_LOGGABLE) {

logV("finished run method in " + LogTime.getElapsedMillis(startTime));

}

}

}

onSizeReady() 调用了Engine.load方法正式开始加载。是加载的入口

loadStatus =

engine.load(

glideContext,

model,

requestOptions.getSignature(),

this.width,

this.height,

requestOptions.getResourceClass(),

transcodeClass,

priority,

requestOptions.getDiskCacheStrategy(),

requestOptions.getTransformations(),

requestOptions.isTransformationRequired(),

requestOptions.isScaleOnlyOrNoTransform(),

requestOptions.getOptions(),

requestOptions.isMemoryCacheable(),

requestOptions.getUseUnlimitedSourceGeneratorsPool(),

requestOptions.getUseAnimationPool(),

requestOptions.getOnlyRetrieveFromCache(),

this,

callbackExecutor);

Engine类:

engine是整个Glide的灵魂所在,著名的缓存机制都是在Engine中进行的。和我们之前学习一样。Engine肯定又是一个“大家长”。其主要功能是负责开启load Request 且管理资源的缓存。这些功能都是由其内部一个又一个类构成。往往在这个类中就会声明实现各个模块的“小部件”

public class Engine

implements EngineJobListener,

MemoryCache.ResourceRemovedListener,

EngineResource.ResourceListener {

private static final String TAG = "Engine";

private static final int JOB_POOL_SIZE = 150;

private static final boolean VERBOSE_IS_LOGGABLE = Log.isLoggable(TAG, Log.VERBOSE);

private final Jobs jobs;//通过HashMap记录产生的EngineJob

private final EngineKeyFactory keyFactory;

private final MemoryCache cache;//内存缓存机制第二层

private final EngineJobFactory engineJobFactory;//生产EngineJob

private final ResourceRecycler resourceRecycler;

private final LazyDiskCacheProvider diskCacheProvider;

private final DecodeJobFactory decodeJobFactory;

private final ActiveResources activeResources;//内存缓存机制第一层

Engine#load:

public LoadStatus load(

GlideContext glideContext,

Object model,

Key signature,

int width,

int height,

Class> resourceClass,

Class transcodeClass,

Priority priority,

DiskCacheStrategy diskCacheStrategy,

Map, Transformation>> transformations,

boolean isTransformationRequired,

boolean isScaleOnlyOrNoTransform,

Options options,

boolean isMemoryCacheable,

boolean useUnlimitedSourceExecutorPool,

boolean useAnimationPool,

boolean onlyRetrieveFromCache,

ResourceCallback cb,

Executor callbackExecutor) {

long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;

//标记EngineResource的独一无二的key

EngineKey key =

keyFactory.buildKey(

model,

signature,

width,

height,

transformations,

resourceClass,

transcodeClass,

options);

EngineResource> memoryResource;

synchronized (this) {

memoryResource = loadFromMemory(key, isMemoryCacheable, startTime); //缓存机制1:从内存加载

if (memoryResource == null) {

return waitForExistingOrStartNewJob(

glideContext,

model,

signature,

width,

height,

resourceClass,

transcodeClass,

priority,

diskCacheStrategy,

transformations,

isTransformationRequired,

isScaleOnlyOrNoTransform,

options,

isMemoryCacheable,

useUnlimitedSourceExecutorPool,

useAnimationPool,

onlyRetrieveFromCache,

cb,

callbackExecutor,

key,

startTime);

}

}

loadFromMemory方法:就是Glide框架中的第一层缓存机制---内存缓存。

@Nullable

private EngineResource> loadFromMemory(

EngineKey key, boolean isMemoryCacheable, long startTime) {

if (!isMemoryCacheable) {

return null;

}

EngineResource> active = loadFromActiveResources(key);

if (active != null) {

if (VERBOSE_IS_LOGGABLE) {

logWithTimeAndKey("Loaded resource from active resources", startTime, key);

}

return active;

}

EngineResource> cached = loadFromCache(key);

if (cached != null) {

if (VERBOSE_IS_LOGGABLE) {

logWithTimeAndKey("Loaded resource from cache", startTime, key);

}

return cached;

}

return null;

}

我们首先看看第一层内存缓存 loadFromActiveResources:

@Nullable

private EngineResource> loadFromActiveResources(Key key) {

EngineResource> active = activeResources.get(key);

if (active != null) {

active.acquire();

}

return active;

}

ActiveResources类:

final class ActiveResources {

private final boolean isActiveResourceRetentionAllowed;

private final Executor monitorClearedResourcesExecutor;

@VisibleForTesting final Map activeEngineResources = new HashMap<>();//缓存EngineResource。

private final ReferenceQueue> resourceReferenceQueue = new ReferenceQueue<>();

private ResourceListener listener;

ResourceWeakReference 对象可以理解成是对EngineResource包装。本身是弱引用。当前程序中被使用的EngineResource都会放在这个map中。这样当在其他地方再次使用,可以直接从内存中查找出来,根据EngineKey。

active.acquire();采用了引用计数机制(类似jvm内存管理中的),被引用一次就+1,当为0的时候,就会退居二线内存容器中,也就是会被添加到MemoryCache中。

我们首先看看第二层内存缓存loadFromCache:

private EngineResource> loadFromCache(Key key) {

EngineResource> cached = getEngineResourceFromCache(key);

if (cached != null) {

cached.acquire();

activeResources.activate(key, cached);

}

return cached;

}

private EngineResource> getEngineResourceFromCache(Key key) {

Resource> cached = cache.remove(key);

final EngineResource> result;

if (cached == null) {

result = null;

} else if (cached instanceof EngineResource) {

// Save an object allocation if we've cached an EngineResource (the typical case).

result = (EngineResource>) cached;

} else {

result =

new EngineResource<>(

cached, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);

}

return result;

}

cache指的就是MemoryCache.其实现类是:LruResourceCache 类继承自 LruCache。内部主要是维护了一个LinkedHashMap。

关于Lru算法可以参考这个文章:Lru算法

LinkHashMap 参考这个:LinkHashMap介绍

GetEngineResourceFromCache 就是从第二层LruCache中查找EngineResource。上面是调用remove方法。意味着当命中EngineKey。LruCache会删除这个EngineKey。同时activeResources调用put方法。放进一级缓存中。同时计数+1

流程图:

64fab1a85487

image.png

内存缓存的机制就讲到这里。因为第一次加载网络图片,内存中肯定是找不到EngineResource的。 找不到怎么办呢? 会调用waitForExistingOrStartNewJob 方法进而产生一个EngineJob和DecodeJob

EngineJob engineJob =

engineJobFactory.build(

key,

isMemoryCacheable,

useUnlimitedSourceExecutorPool,

useAnimationPool,

onlyRetrieveFromCache);

DecodeJob decodeJob =

decodeJobFactory.build(

glideContext,

model,

key,

signature,

width,

height,

resourceClass,

transcodeClass,

priority,

diskCacheStrategy,

transformations,

isTransformationRequired,

isScaleOnlyOrNoTransform,

onlyRetrieveFromCache,

options,

engineJob);

jobs.put(key, engineJob);

engineJob.addCallback(cb, callbackExecutor);

engineJob.start(decodeJob);

EngineJob类:

EngineJob是Engine处理每个请求的最小单元,里面有load success/fail 等回调;也有开启load,取消load等操作

64fab1a85487

image.png

DecodeJob类:

DecodeJob 负责从diskCache中或者服务器上解码得到图片的数据。实现了Runnable接口。是真正进行请求的核心类;也是文件缓存的入口。

EngineJob调用Start方法治好,会执行DecodeJob run方法。run代码如下:

@SuppressWarnings("PMD.AvoidRethrowingException")

@Override

public void run() {

GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);

DataFetcher> localFetcher = currentFetcher;

try {

if (isCancelled) {

notifyFailed();

return;

}

runWrapped();//关键

} catch (CallbackException e) {

throw e;

} catch (Throwable t) {

}

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

}

}

private DataFetcherGenerator getNextGenerator() {

switch (stage) {

case RESOURCE_CACHE:

return new ResourceCacheGenerator(decodeHelper, this);

case DATA_CACHE:

return new DataCacheGenerator(decodeHelper, this);

case SOURCE:

return new SourceGenerator(decodeHelper, this);

case FINISHED:

return null;

default:

throw new IllegalStateException("Unrecognized stage: " + stage);

}

}

DataFetcherGenerator是一个接口,实现类如下:

64fab1a85487

image.png

万众瞩目的文件缓存来!!!

runWrapped() 调用顺序

调用ResourceCacheGenerator.startNext方法 从文件中加载数据

找不到数据,调用DataCacheGenerator的startNext 从另外的文件中查询数据;

依然查询不到就调用SourceGenerator.startNext从网络上加载数据。

ResourceCacheGenerator.startNext方法如下:

currentKey =

new ResourceCacheKey( // NOPMD AvoidInstantiatingObjectsInLoops

helper.getArrayPool(),

sourceId,

helper.getSignature(),

helper.getWidth(),

helper.getHeight(),

transformation,

resourceClass,

helper.getOptions());

cacheFile = helper.getDiskCache().get(currentKey);

if (cacheFile != null) {

sourceKey = sourceId;

modelLoaders = helper.getModelLoaders(cacheFile);

modelLoaderIndex = 0;

}

}

loadData = null;

boolean started = false;

while (!started && hasNextModelLoader()) {

ModelLoader modelLoader = modelLoaders.get(modelLoaderIndex++);

loadData =

modelLoader.buildLoadData(

cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());

if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {

started = true;

loadData.fetcher.startNext(helper.getPriority(), this);

}

}

cacheFile = helper.getDiskCache().get(currentKey);

从文件去寻找图片资源

找到之后,调用去读取数据

loadData.fetcher.startNext(helper.getPriority(), this);

DataCacheGenerator的流程和ResourceCacheGenerator流程几乎一样。都是从文件中加载图片区别在于:

ResourceCacheGenerator文件中保存的是经过转换的图片,例如转换成圆角。那张圆角就是保存在ResourceCacheGenerator 中

DataCacheGenerator 是保存最原始的,也就是从服务器拉下来的那张图片。

当然这也与Glide 缓存策略有关:

ALL:既缓存原始图片,也缓存转换过后的图片;对于远程图片,缓存 DATA 和 RESOURCE;对于本地图片,只缓存 RESOURCE。

AUTOMATIC (默认策略):尝试对本地和远程图片使用最佳的策略。当你加载远程数据(比如,从 URL 下载)时,AUTOMATIC 策略仅会存储未被你的加载过程修改过 (比如,变换、裁剪等) 的原始数据(DATA),因为下载远程数据相比调整磁盘上已经存在的数据要昂贵得多。对于本地数据,AUTOMATIC 策略则会仅存储变换过的缩略图(RESOURCE),因为即使你需要再次生成另一个尺寸或类型的图片,取回原始数据也很容易。

DATA:只缓存未被处理的文件。我的理解就是我们获得的 stream。它是不会被展示出来的,需要经过装载 decode,对图片进行压缩和转换,等等操作,得到最终的图片才能被展示。

NONE:表示不缓存任何内容。

RESOURCE:表示只缓存转换过后的图片(也就是经过decode,转化裁剪的图片)。

完整的内存缓存图:

64fab1a85487

image.png

DataFetcherGenerator 加载数据最终都是靠DataFetcher来实现的。DataFetcher接口。常用的DataFetch实现由如下三种:

64fab1a85487

image.png

HttpUrlFetcher:默认的从服务器拉取数据的实现。SourceGenerator 持有的DataFetcher

FileFetcher:从文件中读取数据。ResourceCacheGenerator持有的。

接下来看看 HttpUrlFetcher#loadData

@Override

public void loadData(

@NonNull Priority priority, @NonNull DataCallback super InputStream> callback) {

long startTime = LogTime.getLogTime();

try {

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

callback.onDataReady(result);

} catch (IOException e) {

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

Log.d(TAG, "Failed to load data for url", e);

}

callback.onLoadFailed(e);

} finally {

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

Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));

}

}

}

loadDataWithRedirects方法就不展开讲了,内部就是通过网络请求最终返回一个InputStream.

到此整个请求的数据就拿到了。Glide的缓存机制也分析了一遍。接下来就是各种回调。

2.1.3 请求回调处理

HttpUrlFetcher#loadData中的 callback.onDataReady(result);经过层层的回调一直到Engine类中。流程图如下:

64fab1a85487

image.png

以上就是整个Glide加载流程的主脉络。很多细节问题没有去深究。等以后遇到问题再去弄懂细节问题。

这篇文章整整花了4个小时时间抒写。回调章节讲的也是很简单。可以打印下堆栈快速弄懂回调逻辑。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值