「Glide源码分析」
一、主体模块
二、从基本使用说起
Glide的使用很简单核心代码只有一行:
String url = "https://pic.netbian.com/uploads/allimg/211013/232957-16341389973a59.jpg";
Glide.with(this).load(url).into(imageView);
Glide#with();
with(context)
方法属于Glide
中一组静态的重载方法,排除已经过时的方法主要有:
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
@NonNull
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getContext()).get(fragment);
}
@NonNull
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
可以发现with(context)
其中context
可选的参数有Activity
、FragmentActivity
、Fragment
、View
,
无论传入的参数是什么最终都是通过getRetriever(context)
获得RequestManagerRetriever
来构造RequestManager
的实例。简单一点,可以分为两大类即Application类型参数
与非Application参数
。具体在RequestManagerRetriever
中。
RequestManagerRetriever#RequestManager#with()
@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) { #1
return get(activity.getApplicationContext());
} else if (activity instanceof FragmentActivity) { #2
return get((FragmentActivity) activity);
} else { #3
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
//以传入参数为Activity具体分析,其他情况类似。
思考一个场景,当在一个Activity
中加载一张图片时,图片并未加载完成但此时Activity
却被销毁(Destroy)
,那么比较合理的情况应该是:Glide
取消正在下载的任务。实际上Glide
也确实是这样处理的,那么问题来了,Glide
是如何感知Activity
的生命周期的呢?Glide
的做法很巧妙,向当前Activity
中添加了一个隐藏的Fragment
,我们知道依附于Activity
上的Fragment
的生命周期就与此Activity
的生命周期绑定了。通过Fragment
的生命周期函数的回调情况即可反映出宿主Activity
生命周期的变化,以此来合理控制加载的状态。
如上#3
的情况:
...
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
#1
return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
....
#1(具体实现)
@SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"})
@Deprecated
@NonNull
private RequestManager fragmentGet(
@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
// This is a bit of hack, we're going to start the RequestManager, but not the
// corresponding Lifecycle. It's safe to start the RequestManager, but starting the
// Lifecycle might trigger memory leaks. See b/154405040
if (isParentVisible) {
requestManager.onStart();
}
current.setRequestManager(requestManager);
}
return requestManager;
}
@SuppressWarnings("deprecation")
@NonNull
private RequestManagerFragment getRequestManagerFragment(
@NonNull final android.app.FragmentManager fm, @Nullable android.app.Fragment parentHint) {
//通过Tag查找是否已经存在空fragment绑定了生命周期
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
//首次会实例化一个空fragment
current = new RequestManagerFragment();
current.setParentFragmentHint(parentHint);
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
当传入的context为Activity时,如果不是首次加载,内部会通过Tag
查找已经存在的Fragment
,否则会实例化一个空的Fragment
添加到RequestManager
中。(通过RequestManagerRetriever#RequestManagerFactory#build),因为空Fragment
被添加到当前传入的Activity
之上,而Fragment
与Activity
生命周期是同步的,也即完成了对生命周期的监听。对于context
为Fragment
的情况,由Activity
的情况可以推断出,同样是实例化一个Fragment
将context(Fragment)
作为ParentFragment
,添加之上。
三、子线程中调用的特殊处理
@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else if (activity instanceof FragmentActivity) {
return get((FragmentActivity) activity);
} else {
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
注意到这里对是否是主线程作了判断(其他context同样),if (Util.isOnBackgroundThread())
,代码比较简单,当在子线程中调用时,会被强制转换为ApplicationContext
,那么此时就对应程序本身的生命周期,程序退出Glide终止,也不必在做特使处理。但在使用过程中,这种情况是需要注意的,考虑给Glide
一个合理的生命周期。
RequestManager#load()
在RequestManager中的load()同样有很多重载方法,返回的是RequestBuilder
实例,这个类主要对一些请求参数的构建,如默认的RequestOptions
:
protected static final RequestOptions DOWNLOAD_ONLY_OPTIONS =
new RequestOptions()
//磁盘缓存原始数据
.diskCacheStrategy(DiskCacheStrategy.DATA)
//图片质量
.priority(Priority.LOW)
//是否开启内存缓存
.skipMemoryCache(true);
重点在load()之后的into()方法,着重分析into()方法做了那些操作。
RequestBuilder#into()
#1
//Set the target the resource will be loaded into.
@NonNull
public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
return into(target, /*targetListener=*/ null, Executors.mainThreadExecutor());
}
#2
@NonNull
@Synthetic
<Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
Executor callbackExecutor) {
return into(target, targetListener, /*options=*/ this, callbackExecutor);
}
#3
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
//包装后的请求,是一个接口,定义了请求的开始、清除、暂停、完成等,主要有三个实现类
//SingleRequest,ErrorRequestCoordinator,ThumbnailRequestCoordinator
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
Request
//包装请求的接口
Public interface Request {
void begin();
void clear();
void pasue();
boolean isRunning();
......
}
ThumbnailRequestCoordinator (Request实现类之一)
官方解释,这个类会协同两个独立的Request,分别下载图片的原图与缩略图。(根据配置的参数来决定)
/** * A coordinator that coordinates two individual {@link Request}s that load a small thumbnail * version of an image and the full size version of the image at the same time. */
对Request接口具体实现,包括请求的开始、结束、暂停、清除等,在RequestBuilder#into()的具体实现中,其中核心步骤就是创建了请求Request也即ThumbnailRequestCoordinator。
/** Starts first the thumb request and then the full request. */
@Override
public void begin() {
synchronized (requestLock) {
isRunningDuringBegin = true;
try {
// If the request has completed previously, there's no need to restart both the full //and the
// thumb, we can just restart the full.
if (fullState != RequestState.SUCCESS && thumbState != RequestState.RUNNING) {
thumbState = RequestState.RUNNING;
thumb.begin();
}
if (isRunningDuringBegin && fullState != RequestState.RUNNING) {
fullState = RequestState.RUNNING;
full.begin();
}
} finally {
isRunningDuringBegin = false;
}
}
}
@Override
public void clear() {
synchronized (requestLock) {
isRunningDuringBegin = false;
fullState = RequestState.CLEARED;
thumbState = RequestState.CLEARED;
thumb.clear();
full.clear();
}
}
那么Request请求是何时开启的呢?在into()方法中注意到:
......
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
//省略
requestManager.clear(target);
target.setRequest(request);
//这里的target为目标控件,而request是经过构造包装后ThumbnailRequestCoordinator的实例。
requestManager.track(target, request);
return target;
}
RequestManager#track(),
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
//RequestTracker的实例,这个类主要对请求Request的生命周期进行管理。
requestTracker.runRequest(request);
}
RequestTracker#runRequest()
/** Starts tracking the given request. */
//private final Set<Request> requests =
//Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
//private final Set<Request> pendingRequests = new HashSet<>();
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
分析可得出,对新的Request做了如下操作:
1.将Request加入到Set集合当中(requests),这个集合采用的是弱引用的方式持有,避免内存泄露。
2.判断请求Request是否是非暂停的状态,如果不是,则直接开启加载任务。request.begin()
如果是暂停状态,则调用request.clear()
,这个地方需要特别注意。如果当从直观的感觉像是对request进行了清除操作?然后在加入到待加载的队列
(HashSet)。显然是不符合逻辑的。
3.回到RequestBuilder中对Request的构造中分析,得到的是ThumbnailRequestCoordinator
的实例,也即这里的request.clear()
,其实就是ThumbnailRequestCoordinator
对Request
接口的具体实现:
@Override
public void clear() {
synchronized (requestLock) {
//这里的“清除”操作,其实是对request中生命周期的初始化,并且对request中包含的两个独立Request(原图、缩略图的初始化),这样是比较合理的。
isRunningDuringBegin = false;
fullState = RequestState.CLEARED;
thumbState = RequestState.CLEARED;
//缩略图的request
thumb.clear();
//原图的request
full.clear();
}
}
SingleRequest(Request实现之二)
上述分析中得知ThumbnailRequestCoordinator
是对请求(原图、缩略图)的协同整合,分别对应full
,thumb
。那它们的具体实现是什么呢?回到RequestBuilder#obtainRequest()
方法中:
private Request obtainRequest(
Object requestLock,
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
Executor callbackExecutor) {
//full,thumb的真正实现类
return SingleRequest.obtain(
context,
glideContext,
requestLock,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory(),
callbackExecutor);
}
//默认的构造方法,此时的 status = Status.PENDING;
@SuppressWarnings("GuardedBy")
private SingleRequest(
Context context,
GlideContext glideContext,
@NonNull Object requestLock,
@Nullable Object model,
Class<R> transcodeClass,
BaseRequestOptions<?> requestOptions,
int overrideWidth,
int overrideHeight,
Priority priority,
Target<R> target,
@Nullable RequestListener<R> targetListener,
@Nullable List<RequestListener<R>> requestListeners,
RequestCoordinator requestCoordinator,
Engine engine,
TransitionFactory<? super R> animationFactory,
Executor callbackExecutor) {
this.requestLock = requestLock;
this.context = context;
this.glideContext = glideContext;
this.model = model;
this.transcodeClass = transcodeClass;
this.requestOptions = requestOptions;
this.overrideWidth = overrideWidth;
this.overrideHeight = overrideHeight;
this.priority = priority;
this.target = target;
this.targetListener = targetListener;
this.requestListeners = requestListeners;
this.requestCoordinator = requestCoordinator;
this.engine = engine;
this.animationFactory = animationFactory;
this.callbackExecutor = callbackExecutor;
status = Status.PENDING;
if (requestOrigin == null &&
glideContext.getExperiments().isEnabled(LogRequestOrigins.class)) {
requestOrigin = new RuntimeException("Glide request origin trace");
}
}
可以发现full
,thumb
的真正实现类是SingleRequest
。
SingleRequest#begin()(在ThumbnailRequestCoordinator#begin中被开启)
@Override
public void begin() {
synchronized (requestLock) {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
// Only log at more verbose log levels if the user has set a fallback drawable, // because
// fallback Drawables indicate the user expects null models occasionally.
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
if (status == Status.COMPLETE) {
onResourceReady(
resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return;
}
cookie = GlideTrace.beginSectionAsync(TAG);
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());
}
......
}
}
按照begin()代码执行顺序,一步步分析:
1.经过之前的分析可以知道的是,初始化的SingleRequest中status的值为Status.PENDING
,并且在调用into()
时并没有指定target的宽高(width,height),对基础参数的校验,包括控件的宽高,status如果为Status.RUNNING
,直接抛出异常,这个很好理解请求中的状态是不允许重新开启的。
2.判断status = Status.COMPLETE
的值,表示资源已经加载完成,调用内部方法SingleRequest#onResourceReady
并返回。
3.资源未加载完成则将status
的值赋为Status.WAITING_FOR_SIZE
。
4.还是对目标控件的大小进行校验,目标大小已经确定即执行SingleRequest#onSizeReady()
,否则执行target.getSize(this)
,这里的this
显然表示的是SingleRequest
本身的实例,而SingleRequest
实现了SizeReadyCallback
接口,这里其实是需要一个SizeReadyCallback
的实例。
public final class SingleRequest<R> implements Request, SizeReadyCallback, ResourceCallback {.....}
public interface Target<R> extends LifecycleListener {
....
void getSize(@NonNull SizeReadyCallback cb);
....
}
public interface SizeReadyCallback {
void onSizeReady(int width, int height);
}
通过target.getSize(this),最终回调还是会走到内部方法SingleRequest#onSizeReady()。
5.对于是否需要先加载PlaceholderDrawable
进行判断并设置。
总结:从begin()
方法分析可以得出,begin()
还不是真正开启加载资源的入口。
SingleRequest#onSizeReady(int width, int height);
//仅保留核心代码
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) {
//.....
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
//真正开启加载资源任务
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);
if (status != Status.RUNNING) {
loadStatus = null;
}
//.....
}
}
Engine#load(),加载任务
可以理解Engine
是Glide加载任务的执行引擎,包括资源的获取,无论是从活动缓存中、内存缓存中还是磁盘中,其中load()
主要做了以下操作,也是重点需要关注的核心逻辑。核心代码:
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, 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;
//构建请求资源的key,包括获取已经被缓存的资源都会用到这个key。
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
//直接从缓存中获取资源,这里会分为两个步骤,首先是从活动缓存中去查找资源,没有则去内存缓存中查找。
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
//缓存中都没有待加载的资源,则会构建EngineJob,DecodeJob并行获取资源。
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);
}
}
// Avoid calling back while holding the engine lock, doing so makes it easier for callers to
// deadlock.
cb.onResourceReady(
memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return null;
}
Engine
这个类中主要关注load()
方法,总结这个方法做了几件事情:
1.第一步构建请求资源的key
,有特定的构造规则,同时这个key
也是从缓存中获取资源的凭证
。
EngineKey key = keyFactory.buildKey(model,signature,width,height,transformations,resourceClass,
transcodeClass,options);
2.从活动缓存(activities cache)中获取资源,获取到直接返回,没有进行下一步。
3.从内存缓存中(memory cache)中获取资源,获取到直接返回,没有执行下一步。
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
@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;
}
4.构建LoadStatus
对象(这个对象的本质是为了协同Engine与ResourceCallback),通过第一步创建的key
判断当前请求是不是正在执行,是则直接返回。
//Engine#waitForExistingOrStartNewJob()
private <R> LoadStatus waitForExistingOrStartNewJob(...) {
.......
//判断当前请求是否正在执行,是则直接返回
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
.....
}
5.构建EngineJob、DecodeJob
并行,并构建LoadStatus
对象返回。
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
//分别构建了EngineJob与DecodeJob,
//对onResourceReady接口进行设置(监听),而最终的回调还是回到了SingleRequest#onResourceReady
//或者SingleRequest#onLoadFailed()
engineJob.addCallback(cb, callbackExecutor);
//以第一次加载为例,DecodeJob实现了Runnable接口,这里直接开启了DecodeJob任务,因此关注点即
//DecodeJob中run()方法的具体实现即可 DecodeJob#run(),包括网络资源的获取,Cache中资源的获取等
//涉及到重要的加载接口-> DataFetcher
engineJob.start(decodeJob);
DecodeJob#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);
}
}
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);
}
}
/** Why we're being executed again. */
private enum RunReason {
/** The first time we've been submitted. */
INITIALIZE,
/** We want to switch from the disk cache service to the source executor. */
SWITCH_TO_SOURCE_SERVICE,
/**
* We retrieved some data on a thread we don't own and want to switch back to our thread * to
* process the data.
*/
DECODE_DATA,
}
分别对应着不同加载任务,第一次加载、从缓存cache中获取资源、解码资源。
1.以初次从网络加载资源为例(Stage.SOURCE)
,在DecodeJob#runGenerators()
中当stage ==Stage.SOURCE
时直接调用DecodeJob#reschedule()
,回到之前分析的EngineJob
类中就实现了DecodeJob.Callback<R>
接口,绕了一圈还是回到了资源加载引擎EngineJob
中,如下:
@Override public void reschedule(DecodeJob<?> job) { // Even if the job is cancelled here, it still needs to be scheduled so that it can // clean itself // up. getActiveSourceExecutor().execute(job); }
总结:DecodeJob
负责从缓存、或者源资源中获取数据,通过解码等转化为对应的Resource
通过回调的方式抛出到Target
,并且DecodeJob
实现了Runnable
接口,而EngineJob
则是执行DecodeJob
的入口,将DecodeJob
运行到指定的线程池中。而图片的最终加载是通过实现DataFetcher
接口来完成的,考虑到缓存的存在,并不是以直接加载的方式,根据接口DataFetcherGenerator
的三个实现类,分别隔离了从源资源,未转换的资源、转换后的资源中获取数据。获取完成后使用Encoder
将数据存入磁盘缓存文件中,同时使用对应的解码器将原始数据转换为相应的资源文件,这样整个流程就差不多结束了。
四、缓存模块流程图
五、加载流程图
六、扩展,自定义加载进度显示Model
Glide在使用过程对于图片的加载是没有进度显示的,但是根据文档,我们完全可以实现自己的 Model 。基于Glide 4.12.0
最新的Glide
自定义扩展不再需要在AndroidManifest
中注册。只需要加上注解@GlideModule
并实现AppGlideModule
即可。要实现带进度条,只需要通过使用okHttp3
中的拦截器对下载内容进行拦截,将进度抛出至外层展示则可满足。
自定义进度条Module
具体细节如下:
//此注解必须加上
@GlideModule
public class ProgressModule extends AppGlideModule {
@Override
public boolean isManifestParsingEnabled() {
return true;
}
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addInterceptor(new ProgressInterceptor());
OkHttpClient okHttpClient = builder.build();
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(okHttpClient));
}
}
对进度进行回调的接口OnProgressListener
:
public interface OnProgressListener {
void onProgress(int progress);
}
okHttp3
自定义拦截器拦截请求:
public class ProgressInterceptor implements Interceptor {
static final Map<String, OnProgressListener> LISTENER_MAP = new HashMap<>();
public static void addListener(String url, OnProgressListener listener) {
LISTENER_MAP.put(url, listener);
}
public static void removeListener(String url) {
LISTENER_MAP.remove(url);
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
String url = request.url().toString();
ResponseBody responseBody = response.body();
return response.newBuilder().body(new ProgressResponseBody(url,responseBody)).build();
}
}
拦截器Interceptor
:
public class ProgressInterceptor implements Interceptor {
//对请求地址相同的url作缓存,避免重复请求
static final Map<String, OnProgressListener> LISTENER_MAP = new HashMap<>();
public static void addListener(String url, OnProgressListener listener) {
LISTENER_MAP.put(url, listener);
}
public static void removeListener(String url) {
LISTENER_MAP.remove(url);
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
String url = request.url().toString();
ResponseBody responseBody = response.body();
return response.newBuilder().body(new ProgressResponseBody(url, responseBody)).build();
}
}
请求返回的ResponseBody
:
public class ProgressResponseBody extends ResponseBody {
private static final String TAG = ProgressResponseBody.class.getCanonicalName();
private final ResponseBody responseBody;
private BufferedSource bufferedSource;
private OnProgressListener listener;
public ProgressResponseBody(String url, ResponseBody responseBody) {
this.responseBody = responseBody;
listener = ProgressInterceptor.LISTENER_MAP.get(url);
}
@Nullable
@Override
public MediaType contentType() {
return responseBody.contentType();
}
@Override
public long contentLength() {
return responseBody.contentLength();
}
@Override
public BufferedSource source() {
if (null == bufferedSource) {
bufferedSource = Okio.buffer(new ProgressSource(responseBody.source()));
}
return bufferedSource;
}
private class ProgressSource extends ForwardingSource {
private int currentProgress;
private long totalCount = 0;
public ProgressSource(Source delegate) {
super(delegate);
}
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
long totalByteCount = responseBody.contentLength();
if (-1 == bytesRead) {
totalCount = totalByteCount;
} else {
totalCount += bytesRead;
}
int progress = (int) (100f * totalCount / totalByteCount);
Log.d(TAG, "progress is = " + progress);
if (null != listener && progress != currentProgress) {
listener.onProgress(progress);
}
if (null != listener && totalCount == totalByteCount) {
listener = null;
}
currentProgress = progress;
return bytesRead;
}
}
}
下载图片的地方使用:
private void initProgressDialog() {
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("下载中...");
}
private void loadImg() {
String url = "https://pic.netbian.com/uploads/allimg/211013/232957-16341389973a59.jpg";
ProgressInterceptor.addListener(url, progress -> progressDialog.setProgress(progress));
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.DATA)
.skipMemoryCache(false)
.into(new CustomTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
binding.ivImg.setImageDrawable(resource);
progressDialog.dismiss();
ProgressInterceptor.removeListener(url);
}
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
progressDialog.show();
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
}
});
}
七、结语
Glide作为优秀的图片加载框架,源码虽然很多,但是按照模块分出来很清晰,根据加载的流程往下走。如果试图理解每一行代码,过程是极其痛苦的,对于这种类似的库,最好的办法是去分析主体的实现逻辑,从整体上把握。效率更高,反过来如果对于某一块深入研究,如Glide中编解码的具体实现则可以一步步理解每一行代码的实现。本次的Glide主要是从使用Glide.with(context).load(url).into(target)
逐渐分析,理解各个模块的作用与相互配合,反复琢磨还是能收获很多的。当然Glide
作为优秀的图片加载框架,个人认为常看常新,每次都会有不小的收获。