攻略大全
1. 粘贴攻略
1.1 简单对比
1.2 简单使用
// 流式接口的调用方式
Glide
// 绑定Context
.with(Context context)
// 绑定Activity
.with(Activity activity)
// 绑定FragmentActivity
.with(FragmentActivity activity)
// 绑定Fragment
.with(Fragment fragment)
// 清理磁盘缓存 需要在子线程中执行
.clearDiskCache()
// 清理内存缓存 可以在UI主线程中进行
.clearMemory()
// 要加载的资源
.load("source")
// 设置加载尺寸
.override(800, 800)
// 设置加载中图片
.placeholder(R.mipmap.ic_launcher)
// 设置加载失败图片
.error(R.mipmap.ic_launcher)
// 设置加载动画
.animate(R.anim.item_alpha_in)
// 设置图片裁剪方式
.centerCrop()
// 设置缩略图支持:先加载缩略图 然后在加载全图
// 传了一个 0.1f 作为参数,Glide 将会显示原始图像的10%的大小。
// 如果原始图像有 1000x1000 像素,那么缩略图将会有 100x100 像素。
.thumbnail(0.1f)
//显示gif静态图片
.asBitmap()
//显示gif动态图片
.asGif()
// 缓存参数说明
// DiskCacheStrategy.NONE:不缓存任何图片,即禁用磁盘缓存
// DiskCacheStrategy.ALL :缓存原始图片 & 转换后的图片(默认)
// DiskCacheStrategy.SOURCE:只缓存原始图片(原来的全分辨率的图像,即不缓存转换后的图片)
// DiskCacheStrategy.RESULT:只缓存转换后的图片(即最终的图像:降低分辨率后或者转换后 ,不缓存原始图片
.diskCacheStrategy(DiskCacheStrategy.ALL)
// 设置跳过内存缓存
//这意味着 Glide 将不会把这张图片放到内存缓存中去
//这里需要明白的是,这只是会影响内存缓存!Glide 将会仍然利用磁盘缓存来避免重复的网络请求。
.skipMemoryCache(true)
// 设置下载优先级
.priority(Priority.NORMAL)
// 图片最终要展示的地方
.into(imageView);
.into(new SimpleTarget<GlideDrawable>() {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
imageView.setImageDrawable(resource);
}
});
2. 造火箭攻略
3. 拧螺丝攻略
3.1 with()
with()
是为得到一个RequestManager
对象,从而将Glide
的加载图片周期与Activity
和Fragment
的生命周期进行绑定。
3.1.1 获取RequestManagerRetriever
源码上,都会调用getRetriever()获得RequestManagerRetriever实例对象,进而调用RequestManagerRetriever实例对象的get()方法去构建RequestManager。
而getRetriever()的调用,涉及Gilde单例的初始化。
@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.getActivity()).get(fragment);
}
@NonNull
public static RequestManager with(@NonNull androidx.fragment.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
@NonNull
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// Context could be null for other reasons (ie the user passes in null), but in practice it will
// only occur due to errors with the Fragment lifecycle.
Preconditions.checkNotNull(
context,
"You cannot start a load on a not yet attached View or a Fragment where getActivity() "
+ "returns null (which usually occurs when getActivity() is called before the Fragment "
+ "is attached or after the Fragment is destroyed).");
return Glide.get(context).getRequestManagerRetriever();
}
/**
* Get the singleton.
*
* @return the singleton
*/
@NonNull
public static Glide get(@NonNull Context context) {
if (glide == null) {
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
3.1.2 构建RequestManager
通过context的不同类型构建出不同的RequestManager对象,即不同生命周期的Gilde。
@NonNull
public RequestManager get(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, null /*parentHint*/);
}
}
@NonNull
public RequestManager get(@NonNull Fragment fragment) {
Preconditions.checkNotNull(fragment.getActivity(),
"You cannot start a load on a fragment before it is attached or after it is destroyed");
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm, fragment);
}
}
@NonNull
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, null /*parentHint*/);
}
}
@NonNull
public RequestManager get(@NonNull View view) {
if (Util.isOnBackgroundThread()) {
return get(view.getContext().getApplicationContext());
}
Preconditions.checkNotNull(view);
Preconditions.checkNotNull(view.getContext(),
"Unable to obtain a request manager for a view without a Context");
Activity activity = findActivity(view.getContext());
// The view might be somewhere else, like a service.
if (activity == null) {
return get(view.getContext().getApplicationContext());
}
// Support Fragments.
// Although the user might have non-support Fragments attached to FragmentActivity, searching
// for non-support Fragments is so expensive pre O and that should be rare enough that we
// prefer to just fall back to the Activity directly.
if (activity instanceof FragmentActivity) {
Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
return fragment != null ? get(fragment) : get(activity);
}
// Standard Fragments.
android.app.Fragment fragment = findFragment(view, activity);
if (fragment == null) {
return get(activity);
}
return get(fragment);
}
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) {
// Normally pause/resume is taken care of by the fragment we add to the fragment or
// activity. However, in this case since the manager attached to the application will not
// receive lifecycle events, we must force the manager to start resumed using
// ApplicationLifecycle.
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context.getApplicationContext());
applicationManager =
factory.build(
glide,
new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
}
}
}
return applicationManager;
}
3.1.3 揭开绑定生命周期的神秘面纱
3.1.3.1 RequestManger:监听生命周期的回调进而管理图片的加载
RequestManger实现了LifecycleListener,在构造函数中将自己加入了lifecycle的监听器列表中,进而根据所回调的生命周期方法去管理图片的加载。
3.1.3.2 RequestManagerFragment:连接生命周期方法并扩散传递
RequestManagerFragment在收到生命周期的回调时,会遍历lifecycle中所添加的生命周期监听器,从而扩散生命周期的回调。
3.1.3.2 RequestManagerRetriever:将RequestManagerFragment与RequestManger绑定
RequestManagerRetriever会创建出一个无UI界面的RequestManagerFragment依附到Activity中,借此感知到当前界面的生命周期,进而回调Lifecycle中的监听器,达到生命周期回调的传递。
3.1.3.4 RequestTracker:图片请求管理的执行者
/**
* Starts any not yet completed or failed requests.
*/
public void resumeRequests() {
// 将界面状态isPaused置为false
isPaused = false;
// 遍历图片请求集合
for (Request request : Util.getSnapshot(requests)) {
// 如果这个图片请求没有完成&&没有被关闭&&没有正在执行
// 则启动异步请求
if (!request.isComplete() && !request.isCancelled() && !request.isRunning()) {
request.begin();
}
}
// 清空掉等待请求的集合
pendingRequests.clear();
}
RequestManager监听到生命周期的回调后,会回调到RequestTracker中,进行对应的管理。
3.2 load()
load方法得到了一个RequestBuilder
图片请求构建器,即创建图片请求。
3.2.1 跟进as()方法
构建不同类型的RequestBuilder。
// We only override the method to change the return type, not the functionality.
@SuppressLint("CheckResult")
@SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
protected RequestBuilder(
@NonNull Glide glide,
RequestManager requestManager,
Class<TranscodeType> transcodeClass,
Context context) {
this.glide = glide;
this.requestManager = requestManager;
this.transcodeClass = transcodeClass;
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.glideContext = glide.getGlideContext();
initRequestListeners(requestManager.getDefaultRequestListeners());
apply(requestManager.getDefaultRequestOptions());
}
3.2.2 跟进load()方法
简单地进行了一些赋值配置操作,而后将RequestBuilder实例对象返回。
3.3 into()
3.3.1 阶段一
3.3.1.1 查看into()
/**
* Sets the {@link ImageView} the resource will be loaded into, cancels any existing loads into
* the view, and frees any resources Glide may have previously loaded into the view so they may be
* reused.
*
* @see RequestManager#clear(Target)
*
* @param view The view to cancel previous loads for and load the new resource into.
* @return The
* {@link com.bumptech.glide.request.target.Target} used to wrap the given {@link ImageView}.
*/
@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
RequestOptions requestOptions = this.requestOptions;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don't retain the transformation applied based on the previous
// View's scale type.
// 根据我们设置的裁剪方式构建不同的requestOptions
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:
// Do nothing.
}
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions);
}
3.3.1.2 继续跟进into()的重载方法
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
@NonNull RequestOptions options) {
Util.assertMainThread();
Preconditions.checkNotNull(target);
if (!isModelSet) {
// 调用链检测
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
options = options.autoClone();
// 构建图片请求
Request request = buildRequest(target, targetListener, options);
// 获取Target载体已有的请求
Request previous = target.getRequest();
// 如果两个请求是等效的 && 之前的请求是忽略内存缓存的而且该请求还没有完成的话
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
// 那就释放掉新构建的请求
request.recycle();
// 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();
}
return target;
}
// 清除target旧的request,并释放掉旧的request
requestManager.clear(target);
// 绑定新的图片请求
target.setRequest(request);
// 启动异步请求
requestManager.track(target, request);
return target;
}
3.3.1.3 跟进 requestManager.track()
/**
* Starts tracking the given request.
*/
public void runRequest(Request request) {
// 加入请求集合中
requests.add(request);
// 如果当前不是暂停状态
// RequestManger收到生命周期的回调时,会修改该状态
// 这也是为什么Glide能根据生命周期的变化来管理图片的加载周期
if (!isPaused) {
// 开始请求图片
request.begin();
} else {
// 否则加入待请求的集合中
pendingRequests.add(request);
}
}
3.3.2 阶段二
3.3.2.1 查看SingleRequest中的begin()
@Override
public void begin() {
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 we're restarted after we're complete (usually via something like a notifyDataSetChanged
// that starts an identical request into the same Target or View), we can simply use the
// resource and size we retrieved the last time around and skip obtaining a new size, starting a
// new load etc. This does mean that users who want to restart a load because they expect that
// the view size has changed will need to explicitly clear the View or Target before starting
// the new load.
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
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 {
// 否则去获取宽高,最后还是会走onSizeReady()
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));
}
}
3.3.2.2 跟进onSizeReady()
3.3.2.2 跟进engine.load()
public <R> LoadStatus load(...) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
// 构建缓存key
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
// 根据缓存的配置规则通过key去获取缓存
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
// 如果获取到了缓存
if (active != null) {
// 回调返回
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
// 创建EngineJob对象
// 作用:开启线程(作异步加载图片)
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
// 创建DecodeJob对象
// 作用:对图片解码
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.addCallback(cb);
// DecodeJob实现了Runable
engineJob.start(decodeJob);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
3.3.2.3 跟进DecodeJob的run()
DecodeJob
是一个Runable,看看他的run方法,调用链如下:
DecodeJob.run -> DecodeJob.runWrapped -> DecodeJob.runGenerators ->
SourceGenerator.startNext -> SourceGenerator.startNextLoad ->
MultiModelLoader#MultiFetcher.loadData ->
HttpUrlFetcher.loadData
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
// 获取输入流,没有引入okhttp,则使用HttpURLConnection
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
// 回调回去
// callBack
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));
}
}
}
最终回调至View,将图片资源设置给了View。
3.3.3 两个阶段的流程
3.4 总结
整个图片加载过程,就是with得到RequestManager
,load得到RequestBuilder
,然后into开启加载:
创建Request
、开启Engine
、运行DecodeJob
线程、HttpUrlFetcher
加载网络数据、回调给载体Target
、载体为ImageView
设置展示图片。