今天开始更Glide4.0源码解析系列。
博主在面试过程中,经常遇到对使用过的开源项目源码解读的相关问题,之前对开源项目仅重视使用,没能做到知其然也知其所以然,所以从现在开始,准备出一些关于知名的开源项目的源码解读。感兴趣的同学可以一起交流学习,共同进步。下面开始今天的内容。
首先,让我们从Glide经常调用的方法看起,由于项目的源码量比较大,这样可以有助于我们找到切入点。加载网络图片时,一般调用Glide三步走操作如下:
Glide
.with(context)
.load(url)
.into(myImageView);
我们分步来详细看一下这三步走的过程中,Glide帮助我们做了哪些事情。
注意:本文阅读时间较长,且涉及代码量较大,请阅读前做好准备并合理选择阅读时间。
一、Glide.with()方法
通过查看Glide类的代码可知 ,Glide.with()共有6个重载方法,分别是:
public static RequestManager with(Context context) {
return getRetriever(context).get(context);
}
public static RequestManager with(Activity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(android.support.v4.app.FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(android.support.v4.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
public static RequestManager with(android.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
public static RequestManager with(View view) {
return getRetriever(view.getContext()).get(view);
}
注意:
这里由于Glide加入了对android.support.v4.app.Fragment和android.support.v4.app.FragmentActivity的支持,所以我们在引入Glide的同时,也必须引入android.support.v4包。
这六个方法都调用了getRetriever(...)方法,返回一个RequestManagerRetriever对象,通过这个对象调用get(...)方法,最终调用fragmentGet()方法,通过RequestManagerFactory,创建Glide网络请求所需要的RequestManager对象,代码如下:
@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, isParentVisible);
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);
current.setRequestManager(requestManager);
}
return requestManager;
}
上述代码涉及到一个类,就是RequestManagerFragment,Glide源码注释解释为
/**
* A view-less {@link android.app.Fragment} used to safely store an {@link
* com.bumptech.glide.RequestManager} that can be used to start, stop and manage Glide
* requests started for targets the fragment or activity this fragment is a child of.
*/
翻译过来大概就是说这个Fragment是用来存储对应的Fragment或Activity所使用的Glide的RequestManager,并用来控制Glide请求的start、stop和合并。那么它是怎么控制的呢?参见代码如下:
RequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
this.lifecycle = lifecycle;
}
@Override
public void onStart() {
super.onStart();
lifecycle.onStart();
}
@Override
public void onStop() {
super.onStop();
lifecycle.onStop();
}
@Override
public void onDestroy() {
super.onDestroy();
lifecycle.onDestroy();
unregisterFragmentWithRoot();
}
RequestManagerFragment在其构造函数中传入了ActivityFragmentLifecycle,这个类回调了由RequestManager注册的LifecycleListener监听,这样,RequestManagerFragment根据自己生命周期的变化,就可以去对应修改RequestManager的状态。可以说,RequestManagerFragment就是Glide根据app页面情况管理自身请求状态的关键点。
至此,第一步结束,返回了RequestManager对象,为请求做准备。
二、load(url)方法
获得了对应页面的RequestManager,我们就可以进行下一步操作了。
当然,众所周知,Glide不止能加载网络图片,load(...)的重载方法多达9个,可传对象从Bitmap、Drawable、String到Uri、Url、File等等,具体方法如下:
public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
return asDrawable().load(bitmap);
}
public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
return asDrawable().load(drawable);
}
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
public RequestBuilder<Drawable> load(@Nullable Uri uri) {
return asDrawable().load(uri);
}
public RequestBuilder<Drawable> load(@Nullable File file) {
return asDrawable().load(file);
}
public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
return asDrawable().load(resourceId);
}
public RequestBuilder<Drawable> load(@Nullable URL url) {
return asDrawable().load(url);
}
public RequestBuilder<Drawable> load(@Nullable byte[] model) {
return asDrawable().load(model);
}
public RequestBuilder<Drawable> load(@Nullable Object model) {
return asDrawable().load(model);
}
load(url)中调用了asDrawable()方法,asDrawable()调用as()方法创建一个新的RequestBuilder,并将资源类型传递给它:
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
RequestBuilder在构造方法中添加用户在GlideBuilder创建Glide时通过addGlobalRequestListener(...)方法添加的本地监听,并apply当前Glide定制请求的options:
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());
}
asDrawable()方法返回RequestBuilder后继续调用load(url),并调用RequestBuilder的loadGeneric(url)方法。
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
第二步结束,返回了携带TranscodeType的RequestBuilder。
三、into(view)方法
我们调用的into(view)方法源码如下:
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions<?> requestOptions = this;
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.
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),
/*ta