目录
  1. 1. 前言
  2. 2. 开始阅读
    1. 2.1. with()
    2. 2.2. load()
重拾Android-【必知必会SDK】之Glide

前言

既然是要阅读 Glide 的源码,那么需要先将 Glide 的源码下载下来。其实如果你是使用在 build.gradle 中添加依赖的方式将 Glide 引入到项目中的,那么 源码自动就已经下载下来了,在 Android Studio 中就可以直接进行查看。不过,使用添加依赖的方式引入的 Glide,我们只能看到它的源码,但不能做任 何的修改,如果你还需要修改它的源码的话,可以到 GitHub 上面将它的完整源 码下载下来。

Glide 的 GitHub 主页的地址是:https://github.com/bumptech/glide

不过在这个地址下载到的永远都是最新的源码,有可能还正在处于开发当中。而我们整个系列都是使用 Glide 3.7.0 这个版本来进行讲解的,因此如果你需要专门去下载 3.7.0 版本的源码,可以到这个地址进行下载:

https://github.com/bumptech/glide/tree/v3.7.0

开始阅读

我们知道,Glide 最基本的用法就是三步走:先 with(), 再 load(),最后 into()。那么我们开始一步步阅读这三步走的源码,先从 with()看 起。

with()

with()方法是 Glide 类中的一组静态方法,它有好几个方法重载,我们来看一下 Glide 类中所有 with()方法的方法重载:

public class Glide {
...

public static RequestManager with(Context context) {
RequestManagerRetriever retriever = RequestManagerRetriever.get ();
return retriever.get(context);
}

public static RequestManager with(Activity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get ();
return retriever.get(activity);
}

public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get ();
return retriever.get(activity);
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static RequestManager with(android.app.Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get ();
return retriever.get(fragment);
}

public static RequestManager with(Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get ();
return retriever.get(fragment);
}
}

可以看到,with()方法的重载种类非常多,既可以传入 Activity,也可以传入 Fragment 或者是 Context。每一个 with()方法重载的代码都非常简单,都是先调 用 RequestManagerRetriever 的静态 get()方法得到一个 RequestManagerRetriever 对象,这个静态 get()方法就是一个单例实现,没什么需要解释的。然后再调用 RequestManagerRetriever 的实例 get()方法,去获取 RequestManager 对象。

而 RequestManagerRetriever 的实例 get()方法中的逻辑是什么样的呢?我们一起来看一看:

public class RequestManagerRetriever implements Handler.Callback { 

private static final RequestManagerRetriever INSTANCE = new RequestM anagerRetriever();
private volatile RequestManager applicationManager;

...
/**
* Retrieves and returns the RequestManagerRetriever singleton.
*/

public static RequestManagerRetriever get() {
return INSTANCE;
}

private RequestManager getApplicationManager(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 fragm ent 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
applicationManager = new RequestManager(context.getApp licationContext(), new ApplicationLifecycle(), new EmptyRequestMa nagerTreeNode());
}
}
}
return applicationManager;
}

public RequestManager get(Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load o n a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Applicat ion)) {
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);
}

public RequestManager get(FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm);
}
}

public RequestManager get(Fragment fragment) {
if (fragment.getActivity() == null) {
throw new IllegalArgumentException("You cannot start a load o n a fragment before it is attached");
}
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm);
}
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public RequestManager get(Activity activity) {
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build. VERSION_CODES.HONEYCOMB) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager ();
return fragmentGet(activity, fm);
}
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private static void assertNotDestroyed(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 & & activity.isDestroyed()) {
throw new IllegalArgumentException("You cannot start a load f or a destroyed activity");
}
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public RequestManager get(android.app.Fragment fragment) {
if (fragment.getActivity() == null) {
throw new IllegalArgumentException("You cannot start a load o n a fragment before it is attached");
}if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build. VERSION_CODES.JELLY_BEAN_MR1) {
return get(fragment.getActivity().getApplicationContext());
} else {
android.app.FragmentManager fm = fragment.getChildFragmentMan ager();
return fragmentGet(fragment.getActivity(), fm);
}
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
RequestManagerFragment getRequestManagerFragment(final android.app.F ragmentManager fm) {
RequestManagerFragment current = (RequestManagerFragment) fm.find FragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);

if (current == null) {
current = new RequestManagerFragment();
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAl lowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, android.app.FragmentMana ger fm) {
RequestManagerFragment current = getRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifec ycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}

SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm) {
SupportRequestManagerFragment current = (SupportRequestManagerFr agment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = new SupportRequestManagerFragment();
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}

RequestManager supportFragmentGet(Context context, FragmentManager f m) {
SupportRequestManagerFragment current = getSupportRequestManager Fragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifec ycle(), current.getRequestManagerTreeNode()); current.setRequestManager(requestManager);
}
return requestManager;
}

...
}
}

上述代码虽然看上去逻辑有点复杂,但是将它们梳理清楚后还是很简单的。 RequestManagerRetriever 类中看似有很多个 get()方法的重载,什么 Context 参数, Activity 参数,Fragment 参数等等,实际上只有两种情况而已,即传入 Application 类型的参数,和传入非 Application 类型的参数。

我们先来看传入 Application 参数的情况。如果在 Glide.with()方法中传入的是一 个 Application 对象,那么这里就会调用带有 Context 参数的 get()方法重载,然后会调用 getApplicationManager()方法来获取一个 RequestManager 对象。 其实这是最简单的一种情况,因为 Application 对象的生命周期即应用程序的生命周期,因此 Glide 并不需要做什么特殊的处理,它自动就是和应用程序的生命周 期是同步的,如果应用程序关闭的话,Glide 的加载也会同时终止。

接下来我们看传入非 Application 参数的情况。不管你在 Glide.with()方法中传入 的是 Activity、FragmentActivity、v4 包下的 Fragment、还是 app 包下的 Fragment, 最终的流程都是一样的,那就是会向当前的 Activity 当中添加一个隐藏的 Fragment。具体添加的逻辑在上述代码中,分别对应的 app 包和 v4 包下的两种 Fragment 的情况。那么这里为什么要添加一个隐藏的 Fragment 呢?因为 Glide 需要知道加载的生命周期。很简单的一个道理,如果你在某个 Activity 上正在加载着一张图片,结果图片还没加载出来,Activity 就被用户关掉了,那么图片还应该继续加载吗?当然不应该。可是 Glide 并没有办法知道 Activity 的生命周期,于是 Glide 就使用了添加隐藏 Fragment的这种小技巧, 因为 Fragment 的生命周期和 Activity 是同步的,如果 Activity 被销毁了,Fragment 是可以监听到的,这样 Glide 就可以捕获这个事件并停止图片加载了。

这里额外再提一句,从上面代码可以看出,如果我们是在非主线程当中使用的 Glide,那么不管你是传入的 Activity 还是 Fragment,都会被强制当成 Application 来处理。不过其实这就属于是在分析代码的细节了,本篇文章我们将会把目光主 要放在 Glide 的主线工作流程上面,后面不会过多去分析这些细节方面的内容。

总体来说,第一个 with()方法的源码还是比较好理解的。其实就是为了得到一个 RequestManager 对象而已,然后 Glide 会根据我们传入 with()方法的参数来确定图片加载的生命周期,并没有什么特别复杂的逻辑。不过复杂的逻辑还在后面等 着我们呢,接下来我们开始分析第二步 load() 方法。

load()

由于 with()方法返回的是一个 RequestManager 对象,那么很容易就能想到,load() 方法是在 RequestManager 类当中的,所以说我们首先要看的就是 RequestManager 这个类。不过我们用过就知道,Glide 是支持图片 URL 字符串、图片本地路径等等加载形式的,因此 RequestManager 中也有很多个 load() 方法的重载。但是这里我们不可能把每个 load()方法的重载都看一遍,因此我们 就只选其中一个加载图片 URL 字符串的 load()方法来进行研究吧。

RequestManager 类的简化代码如下所示:

public class RequestManager implements LifecycleListener {

...

/**
* Returns a request builder to load the given {@link String}.
* signature.
*
* @see #fromString()
* @see #load(Object)
*
* @param string A file path, or a uri or url handled by {@link com.b umptech.glide.load.model.UriLoader}.
*/
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}

/*** Returns a request builder that loads data from {@link String}s using an empty signature.
*
* <p>
* Note - this method caches data using only the given String as the cache key. If the data is a Uri outside of
* your control, or you otherwise expect the data represented by the given String to change without the String
* identifier changing, Consider using
* {@link GenericRequestBuilder#signature(Key)} to mixin a signature
* you create that identifies the data currently at the given String that will invalidate the cache if that data
* changes. Alternatively, using {@link DiskCacheStrategy#NONE} a nd/or
* {@link DrawableRequestBuilder#skipMemoryCache(boolean)} may be appropriate.
* </p>
*
* @see #from(Class)
* @see #load(String)
*/
public DrawableTypeRequest<String> fromString() {
return loadGeneric(String.class);
}

private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
ModelLoader<T, InputStream> streamModelLoader = Glide.buildStream ModelLoader(modelClass, context);
ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader = Glide.buildFileDescriptorModelLoader(modelClass, contex t);
if (modelClass != null && streamModelLoader == null && fileDescri ptorModelLoader == null) {
throw new IllegalArgumentException("Unknown type " + modelCla ss + ". You must provide a Model of a type for"
+ " which there is a registered ModelLoader, if you ar e using a custom model, you must first call" + " Glide#register with a ModelLoaderFactory for your custom model class");
}

return optionsApplier.apply(new DrawableTypeRequest<T>(modelClass, streamModelLoader,
fileDescriptorModelLoader, context, glide, requestTracker, lifecycle, optionsApplie r));
}

...
}

RequestManager 类的代码是非常多的,经过简化,我们只探究加载图片 URL 字符串这一个 load()方法的情况下,那么比较重要的方法就只剩下上述代码中的这三个方法。

那么我们先来看 load()方法,这个方法中的逻辑是非常简单的,只有一行代码, 就是先调用了fromString()方法,再调用 load()方法,然后把传入的图片 URL 地址 传进去。而 fromString()方法也极为简单,就是调用了 loadGeneric()方法,并且指 定参数为 String.class,因为 load()方法传入的是一个字符串参数。那么看上去,好像主要的工作都是在 loadGeneric()方法中进行的了。

其实 loadGeneric()方法也没几行代码,这里分别调用了 Glide.buildStreamModelLoader()方法和Glide.buildFileDescriptorModelLoader()方法 来获得 ModelLoader 对象。ModelLoader 对象是用于加载图片的,而我们给 load() 方法传入不同类型的参数,这里也会得到不同的 ModelLoader 对象。由于我们刚才传入的参数 是 String.class,因此最终得到的是 StreamStringLoader 对象,它是实现了 ModelLoader 接口的最后我们可以看到,loadGeneric()方法是要返回一个 DrawableTypeRequest 对象 的,因此在 loadGeneric()方法的最后又去 new 了一个 DrawableTypeRequest 对象,然后把刚才获得的 ModelLoader 对象,还有一大堆杂七杂八的东西都传了进去。 具体每个参数的含义和作用先跳过,只看主线流程。

那么这个 DrawableTypeRequest 的作用是什么呢?我们来看下它的源码,如下所示:

public class DrawableTypeRequest<ModelType> extends DrawableRequestBuild er<ModelType> implements DownloadOptions { 
private final ModelLoader<ModelType, InputStream> streamModelLoader;
private final ModelLoader<ModelType, ParcelFileDescriptor> fileDescr iptorModelLoader;
private final RequestManager.OptionsApplier optionsApplier;

private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide, ModelLoader<A, InputStream> streamModelLoader, ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoade r, Class<Z> resourceClass, Class<R> transcodedClass, ResourceTranscoder<Z, R> transcoder) {
if (streamModelLoader == null && fileDescriptorModelLoader == nul l) {
return null;
}
if (transcoder == null) {
transcoder = glide.buildTranscoder(resourceClass, transcodedC lass);
}

DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.b uildDataProvider(ImageVideoWrapper.class, resourceClass);
ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader< A>(streamModelLoader, fileDescriptorModelLoader);

return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoa der, transcoder, dataLoadProvider);
}

DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelTy pe, InputStream> streamModelLoader, ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorMo delLoader, Context context, Glide glide, RequestTracker requestTracker, Lifecycle lifecycle, RequestMa nager.OptionsApplier optionsApplier) {
super(context, modelClass, buildProvider(glide, streamModelLoader, fileDescriptorMod elLoader, GifBitmapWrapper.class, GlideDrawable.class, null), glide, requestTracker, lifecycle);
this.streamModelLoader = streamModelLoader;
this.fileDescriptorModelLoader = fileDescriptorModelLoader;
this.optionsApplier = optionsApplier;
}

/**
* Attempts to always load the resource as a {@link android.graphics. Bitmap}, even if it could actually be animated.
*
* @return A new request builder for loading a {@link android.graphic s.Bitmap}
*/
public BitmapTypeRequest<ModelType> asBitmap() {
return optionsApplier.apply(new BitmapTypeRequest<ModelType>(this, streamModelLoader, fileDescriptorModelLoader, optionsApplier));
}

/**
* Attempts to always load the resource as a {@link com.bumptech.glide.load.resource.gif.GifDrawable}.
* <p>
* If the underlying data is not a GIF, this will fail. As a result, this should only be used if the model
* represents an animated GIF and the caller wants to interact with the GIfDrawable directly. Normally using
* just an {@link DrawableTypeRequest} is sufficient because it will determine whether or
* not the given data represents an animated GIF and return the appropriate animated or not animated
* {@link android.graphics.drawable.Drawable} automatically.
* </p>
*
* @return A new request builder for loading a {@link com.bumptech.glide.load.resource.gif.GifDrawable}.
*/
public GifTypeRequest<ModelType> asGif() {
return optionsApplier.apply(new GifTypeRequest<ModelType>(this, streamModelLoader, optionsApplier));
}

...

}

这个类中的代码本身就不多,我只是稍微做了一点简化。可以看到,最主要的就是它提供了 asBitmap()和 asGif()这两个方法。这两个方法我们在上一篇文章当中都是学过的,分别是用于强制指定加载静态图片和动态图片。而从源码中可以看出,它们分别又创建了一个 BitmapTypeRequest 和 GifTypeRequest,如果没有进行强制指定的话,那默认就是使用 DrawableTypeRequest。

好的,那么我们再回到 RequestManager 的 load()方法中。刚才已经分析过了, fromString()方法会返回一个 DrawableTypeRequest 对象,接下来会调用这个对象的 load()方法,把图片的 URL 地址传进去。但是我们刚才看到了, DrawableTypeRequest 中并没有 load()方法,那么很容易就能猜想到,load()方法是在父类当中的。

DrawableTypeRequest 的父类是 DrawableRequestBuilder,我们来看下这个类的源码:

打赏
  • 微信
  • 支付宝

评论