简介
Glide由Google推出的图片加载框架,支持多种图片格式,同类的还有picasso,fresco。picasso功能没有Glide强大,也不支持gif加载,fresco是Facebook公司推出的框架。
添加glide
// glide
implementation 'com.github.bumptech.glide:glide:4.10.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
实例
Glide.with(targetImageView.getContext())
.load(headerUrl)
.into(targetImageView);
Glide初始化
在调用with方法的时候,如果Glide没有初始化,那么会先进行初始化,创建Glide实例。
Glide编译
在编译过程中,Glide注解程序会生成GeneratedAppGlideModuleImpl和GeneratedRequestManagerFactory两个类来帮助Glide的初始化。
创建GeneratedAppGlideModuleImpl对象
通过创建GeneratedAppGlideModuleImpl对象来获取初始化和配置信息实例,并组装到glide实例中。
创建Glide初始化配置实例
自定义ConfigGlideModule,并继承于AppGlideModule抽象类,通过“GlideModule”注解或者在AndroidManifest配置来指定。
- 注解方式
@com.bumptech.glide.annotation.GlideModule public class ConfigGlideModule extends AppGlideModule { @Override public boolean isManifestParsingEnabled() {return false;} @Override public void applyOptions(Context context, GlideBuilder builder) {super.applyOptions(context, builder);} }
- 清单文件配置
with过程
创建初始化Glide实例,调用with方法对glide进行初始化,with()方法可以接收Context、Activity,Fragment类型参数。如果调用的地方不在Activity,Fragment中,可以获取当前应用程序的ApplicationContext,传入到with()方法中。如果传入的是Activity或者Fragment的实例,那么当这个Activity或Fragment被销毁的时候,图片加载也会停止。如果传入的是ApplicationContext,那么只有当应用程序被杀掉的时候,图片加载才会停止。
Glide类中存在下面两个变量。
private static volatile Glide glide;
private static volatile boolean isInitializing;
如果glide是null,才会去创建glide对象,如果isInitializing是true,代表glide正在初始化,这时候如果再次初始化,是直接会抛异常的,所以在多线程使用环境中一定要注意。
- 全局初始化配置
- 调用自定义的初始化配置类的applyOptions和registerComponents方法
// 通过manifest配置 for (com.bumptech.glide.module.GlideModule module : manifestModules) { module.applyOptions(applicationContext, builder); module.registerComponents(applicationContext, glide, glide.registry); } // 通过“GlideModule”注解配置 if (annotationGeneratedModule != null) { annotationGeneratedModule.applyOptions(applicationContext, builder); annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry); }
- 调用自定义的初始化配置类的applyOptions和registerComponents方法
- GlideBuilder(组装Glide实例)
- 调用GeneratedAppGlideModuleImpl的getRequestManagerFactory获取GeneratedRequestManagerFactory实例并设置到glide中。
builder.setRequestManagerFactory(factory);
- 注册ComponentCallbacks
applicationContext.registerComponentCallbacks(glide);
load过程
- 创建RequestBuilder
resourceClass默认是Drawable.class,可以通过调用 asGif,.asBitmap等方法来改变public
RequestBuilder as( @NonNull Class resourceClass) { return new RequestBuilder<>(glide, this, resourceClass, context); }
保存load传入的参数Glide.with(targetImageView.getContext()) .asBitmap() .load(internetUrl) .apply(getRoundedCornersOptions(dip2px(targetImageView.getContext(), cornerRadios))) .into(targetImageView);
model的值就是load传入的参数private RequestBuilder
loadGeneric(@Nullable Object model) { this.model = model; isModelSet = true; return this; }
into过程
glide需要工作在主线程,否则直接抛异常。
-
请求配置(BaseRequestOptions)
if (!requestOptions.isTransformationSet() && requestOptions.isTransformationAllowed() && view.getScaleType() != null) { switch (view.getScaleType()) { case CENTER_CROP: requestOptions = requestOptions.clone().optionalCenterCrop(); break; case CENTER_INSIDE: requestOptions = requestOptions.clone().optionalCenterInside(); break; case FIT_CENTER: case FIT_START: case FIT_END: requestOptions = requestOptions.clone().optionalFitCenter(); break; case FIT_XY: requestOptions = requestOptions.clone().optionalCenterInside(); break; case CENTER: case MATRIX: default: } }
- 构建请求
Request request = buildRequest(target, targetListener, options, callbackExecutor);
- 构建主请求实例mainRequest
里面重点就是创建请求实例,创建请求实例分两种情况。- 需求请求缩略图
如果有缩略图将创建全图fullRequest(SingleRequest类型)和缩略图thumbnailRequest(SingleRequest类型)请求实例,并且会创建一个持有这两个请求实例的协同请求实例coordinator(ThumbnailRequestCoordinator类型), - 不需要缩略图
直接创建全图fullRequest(SingleRequest类型)请求实例。
- 需求请求缩略图
- 创建错误请求实例
如果错误请求协同实例errorRequestCoordinator是为null,那么会创建主请求实例和错误请求实例,并且设置到errorRequestCoordinator(ErrorRequestCoordinator类型)中。
- 构建主请求实例mainRequest
- 构建请求
-
获取缓存Request
Request previous = target.getRequest();
如果首次加载图片,previous是null,在正式加载前,调用目标View的setTag方法将请求实例request保存到View中,如果获得到的实例不是Request类型,那么将直接抛异常
"You must not call setTag() on a view Glide is targeting"
所以用glide是不能在给View设置tag的,否则会报异常。
判断是否是同一个任务逻辑如下:
return localOverrideWidth == otherLocalOverrideWidth && localOverrideHeight == otherLocalOverrideHeight // 需要加载的图片路径。比如url && Util.bothModelsNullEquivalentOrEquals(localModel, otherLocalModel) // 转换类型 && localTransocdeClass.equals(otherLocalTransocdeClass) && localRequestOptions.equals(otherLocalRequestOptions) && localPriority == otherLocalPriority && localListenerCount == otherLocalListenerCount;
大概意思就是,请求的图片路径,应用到目标View的基本参数,任务优先级等相同就任务是相同任务请求。
如果是同一任务,有下面几种情况
- 开启缓存
- 如果请求已完成,则重新开始重新传递请求结果,从而触发RequestListeners和Targets
- 如果请求失败,则重新启动请求,完成请求。
- 如果请求已经正在运行,我们可以让它继续运行而不会中断
- 没有开启缓存
- 如果请求失败,则重新启动请求,完成请求。
- 如果请求已经正在运行,我们可以让它继续运行而不会中断
- 开启缓存
-
缓存request
target.setRequest(request);
-
运行任务
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); } }
如果请求已经暂停,者清理请求,释放此请求所拥有的任何资源,显示当前占位图(如果已提供),并将该请求标记为已取消,然后将该请求添加到等待队列里。否则,加载占位图,开始运行任务。
-
请求已经完成
获取结果,改变请求状态,通知。try { boolean anyListenerHandledUpdatingTarget = false; if (requestListeners != null) { for (RequestListener
listener : requestListeners) { anyListenerHandledUpdatingTarget |= listener.onResourceReady(result, model, target, dataSource, isFirstResource); } } anyListenerHandledUpdatingTarget |= targetListener != null && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource); if (!anyListenerHandledUpdatingTarget) { Transition super R> animation = animationFactory.build(dataSource, isFirstResource); target.onResourceReady(result, animation); } } finally { isCallingCallbacks = false; } notifyLoadSuccess(); 后面继续分析,这些回调里面都做了什么操作。
-
计算目标View尺寸
一种是你使用了override() API为图片指定了固定的宽高,一种是没有指定。如果指定了就调用onSizeReady()方法。如果没指定就调用target.getSize()方法,并根据ImageView的layout_width和layout_height值来算出图片应该的宽高if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight); } else { // 通过目标view计算 target.getSize(this); }
-
通知请求开始导入
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE) && canNotifyStatusChanged()) { target.onLoadStarted(getPlaceholderDrawable()); }
-
请求执行引擎
这部分讲解真正的从网络端获取数据,并处理的逻辑
获取缓存数据
如果缓存存在,者直接返回缓存数据,不在执行请求,否则运行请求。
- 构建缓存key
缓存key和下面的参数有关EngineKey key = keyFactory.buildKey( model, signature, width, height, transformations, resourceClass, transcodeClass, options);
创建请求任务
请求任务也进行了缓存,如果缓存里面存在该请求任务,这不会创建新的请求任务,只添加执行回调。具体,请自己看源码。
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);
}
如果缓存没有该任务,者会创建EngineJob和DecodeJob两个任务实例,然后缓存,执行任务
jobs.put(key, engineJob);
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);
执行请求任务
engineJob.start(decodeJob);
调用这个方法后会执行DecodeJob中的run方法,然后执行 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); } }
-
执行数据提取器生成器
while (!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())) { stage = getNextStage(stage); currentGenerator = getNextGenerator(); if (stage == Stage.SOURCE) { reschedule(); return; } }
-
获取数据
loadData.fetcher.loadData(helper.getPriority(), this);
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)); } } }
-
读数据
public void onDataReady(Object data) { DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy(); if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) { dataToCache = data; // We might be being called back on someone else's thread. Before doing anything, we should // reschedule to get back onto Glide's thread. cb.reschedule(); } else { cb.onDataFetcherReady( loadData.sourceKey, data, loadData.fetcher, loadData.fetcher.getDataSource(), originalKey); } }
-
解析数据
解析数据的逻辑可以自己去分析,比如需要的是bitmap,拿到数据流后,就会解析成bitmap,再进行一些转换,然后通知数据获取完成public Resource
decode( DataRewinder rewinder, int width, int height, @NonNull Options options, DecodeCallback callback) throws GlideException { Resource decoded = decodeResource(rewinder, width, height, options); Resource transformed = callback.onResourceDecoded(decoded); return transcoder.transcode(transformed, options); } private void notifyComplete(Resource
resource, DataSource dataSource) { setNotifiedOrThrow(); callback.onResourceReady(resource, dataSource); } -
设置数据
在设置数据之前,会根据参数配置处理好数据,然后设置给View- 比如BitmapContainerTransitionFactory转换工厂
@Override public boolean transition(R current, ViewAdapter adapter) { Resources resources = adapter.getView().getResources(); Drawable currentBitmap = new BitmapDrawable(resources, getBitmap(current)); return transition.transition(currentBitmap, adapter); }
- ImageViewTarget
public void onResourceReady(@NonNull Z resource, @Nullable Transition super Z> transition) { if (transition == null || !transition.transition(resource, this)) { setResourceInternal(resource); } else { maybeUpdateAnimatable(resource); } }
- 比如BitmapContainerTransitionFactory转换工厂
总结
glide加载大体流程就这样,最后面数据解析和回调写得有点模糊,自己看了一下代码,有点多,自己可以去跟踪一下,看一下大体处理逻辑。下一篇将写一篇用法总结,如果对里面的原理不关心的,可以阅读我下一篇文章。