文章目录
- 一、前言
- 二、如何阅读源码
- 三、源码解析
- 1、with()
Android Glide图片加载框架系列文章
Android Glide图片加载框架(一)基本用法
Android Glide图片加载框架(二)源码解析之with()
Android Glide图片加载框架(二)源码解析之load()
Android Glide图片加载框架(二)源码解析之into()
Android Glide图片加载框架(三)缓存机制
一、前言
在本系列的上一篇文章中,我们学习了Glide的基本用法,体验了这个图片加载框架的强大功能,以及它非常简便的API。还没有看过上一篇文章的朋友,建议先去阅读 Android Glide图片加载框架(一)基本用法。
在多数情况下,我们想要在界面上加载并展示一张图片只需要一行代码就能实现,如下所示:
Glide.with(this).load(url).into(img);
虽说只有这简简单单的一行代码,但大家可能不知道的是,Glide在背后帮我们默默执行了成吨的工作。这个形容词我想了很久,因为我觉得用非常多这个形容词不足以描述Glide背后的工作量,我查到的英文资料是用tons of work来进行形容的,因此我觉得这里使用成吨来形容更加贴切一些。
虽说我们在平时使用Glide的时候格外地简单和方便,但是知其然也要知其所以然。那么今天我们就来解析一下Glide的源码,看看它在这些简单用法的背后,到底执行了多么复杂的工作。
二、如何阅读源码
在开始解析Glide源码之前,我想先和大家谈一下该如何阅读源码,这个问题也是我平时被问得比较多的,因为很多人都觉得阅读源码是一件比较困难的事情。
那么阅读源码到底困难吗?
这个当然主要还是要视具体的源码而定。比如同样是图片加载框架,我读Volley的源码时就感觉酣畅淋漓,并且对Volley的架构设计和代码质量深感佩服。读Glide的源码时却让我相当痛苦,代码极其难懂。当然这里我并不是说Glide的代码写得不好,只是因为Glide和复杂程度和Volley完全不是在一个量级上的。
那么,虽然源码的复杂程度是外在的不可变条件,但我们却可以通过一些技巧来提升自己阅读源码的能力。这里我和大家分享一下我平时阅读源码时所使用的技巧,简单概括就是八个字:抽丝剥茧、点到即止
。
应该认准一个功能点,然后去分析这个功能点是如何实现的。但只要去追寻主体的实现逻辑即可,千万不要试图去搞懂每一行代码都是什么意思,那样很容易会陷入到思维黑洞当中,而且越陷越深。因为这些庞大的系统都不是由一个人写出来的,每一行代码都想搞明白,就会感觉自己是在盲人摸象,永远也研究不透。如果只是去分析主体的实现逻辑,那么就有比较明确的目的性,这样阅读源码会更加轻松,也更加有成效。
而今天带大家阅读的Glide源码就非常适合使用这个技巧,因为Glide的源码太复杂了,千万不要试图去搞明白它每行代码的作用,而是应该只分析它的主体实现逻辑。
那么我们本篇文章就先确立好一个目标,就是要通过阅读源码搞明白下面这行代码:
Glide.with(this).load(url).into(img);
到底是如何实现将一张网络图片展示到ImageView上面的。先将Glide的一整套图片加载机制的基本流程梳理清楚,然后我们再通过后面的几篇文章具体去了解Glide源码方方面面的细节。
准备好了吗?那么我们现在开始。
既然是要阅读Glide的源码,那么我们自然需要先将Glide的源码下载下来。其实如果你是使用在 build.gradle
中添加依赖的方式将Glide引入到项目中的,那么源码自动就已经下载下来了,在Android Studio中就可以直接进行查看。
不过,使用添加依赖的方式引入的Glide,我们只能看到它的源码,但不能做任何的修改,如果你还需要修改它的源码的话,可以到GitHub上面将它的完整源码下载下来。
Glide的GitHub主页的地址是:https://github.com/bumptech/glide
不过在这个地址下载到的永远都是最新的源码,有可能还正在处于开发当中。而我们整个系列都是使用Glide 4.8.0这个版本来进行讲解的,因此如果你需要专门去下载4.8.0版本的源码,可以到这个地址进行下载:https://github.com/bumptech/glide/tree/v4.8.0
三、源码解析
1、with()
with()
方法是Glide类中的一组静态方法,它有好几个方法重载,我们来看一下Glide类中所有 with()
方法的方法重载:
public class Glide{...@NonNullpublic static RequestManager with(@NonNull Context context) {return getRetriever(context).get(context);}@NonNullpublic static RequestManager with(@NonNull Activity activity) {return getRetriever(activity).get(activity);}@NonNullpublic static RequestManager with(@NonNull FragmentActivity activity) {return getRetriever(activity).get(activity);}@NonNullpublic static RequestManager with(@NonNull Fragment fragment) {return getRetriever(fragment.getActivity()).get(fragment);}@SuppressWarnings("deprecation")@Deprecated@NonNullpublic static RequestManager with(@NonNull android.app.Fragment fragment) {return getRetriever(fragment.getActivity()).get(fragment);}@NonNullpublic static RequestManager with(@NonNull View view) {return getRetriever(view.getContext()).get(view);}@NonNullprivate 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();}...
}
可以看到,with()
方法的重载种类非常多,既可以传入 Activity
,也可以传入 Fragment
或者是 Context
。每一个 with()
方法重载的代码都非常简单,都是先调用 getRetriever()
方法获取 RequestManagerRetriever
对象,然后再调用 RequestManagerRetriever
的实例 get()
方法,去获取 RequestManager
对象。
而 RequestManagerRetriever
的实例 get()
方法中的逻辑是什么样的呢?我们一起来看一看:
public class RequestManagerRetriever implements Handler.Callback {/*** The top application level RequestManager.*/private volatile RequestManager applicationManager;@NonNullprivate 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;}@NonNullpublic 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);}@NonNullpublic RequestManager get(@NonNull FragmentActivity activity) {if (Util.isOnBackgroundThread()) {return get(activity.getApplicationContext());} else {assertNotDestroyed(activity);FragmentManager fm = activity.getSupportFragmentManager();return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));}}@NonNullpublic 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, fragment.isVisible());}}@SuppressWarnings("deprecation")@NonNullpublic 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, /*parentHint=*/ null, isActivityVisible(activity));}}@SuppressWarnings("deprecation")@NonNullpublic 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);}@SuppressWarnings("deprecation")@Deprecated@NonNull@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)public RequestManager get(@NonNull android.app.Fragment fragment) {if (fragment.getActivity() == null) {throw new IllegalArgumentException("You cannot start a load on 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.getChildFragmentManager();return fragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());}}@SuppressWarnings("deprecation")@Deprecated@NonNullRequestManagerFragment getRequestManagerFragment(Activity activity) {return getRequestManagerFragment(activity.getFragmentManager(), /*parentHint=*/ null, isActivityVisible(activity));}@SuppressWarnings("deprecation")@NonNullprivate RequestManagerFragment getRequestManagerFragment(@NonNull final android.app.FragmentManager fm,@Nullable android.app.Fragment parentHint,boolean isParentVisible) {RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);if (current == null) {current = pendingRequestManagerFragments.get(fm);if (current == null) {current = new RequestManagerFragment();current.setParentFragmentHint(parentHint);if (isParentVisible) {current.getGlideLifecycle().onStart();}pendingRequestManagerFragments.put(fm, current);fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();}}return current;}@SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"})@Deprecated@NonNullprivate 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;}@NonNullprivate SupportRequestManagerFragment getSupportRequestManagerFragment(@NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {SupportRequestManagerFragment current =(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);if (current == null) {current = pendingSupportRequestManagerFragments.get(fm);if (current == null) {current = new SupportRequestManagerFragment();current.setParentFragmentHint(parentHint);if (isParentVisible) {current.getGlideLifecycle().onStart();}pendingSupportRequestManagerFragments.put(fm, current);fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();}}return current;}@NonNullprivate RequestManager supportFragmentGet(@NonNull Context context,@NonNull FragmentManager fm,@Nullable Fragment parentHint,boolean isParentVisible) {SupportRequestManagerFragment current =getSupportRequestManagerFragment(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;}
}
上述代码虽然看上去逻辑有点复杂,但是将它们梳理清楚后还是很简单的。 RequestManagerRetriever
类中看似有很多个 get()
方法的重载,什么Context参数,Activity参数,Fragment参数等等,实际上只有两种情况而已,即传入Application类型的参数,和传入非Application类型的参数
。
-
Application类型参数:
如果在Glide.with()
方法中传入的是一个Application对象
,那么这里就会调用带有Context参数的get()方法重载,然后会在第48行调用getApplicationManager()
方法来获取一个RequestManager
对象。其实这是最简单的一种情况,因为Application对象的生命周期即应用程序的生命周期,因此Glide并不需要做什么特殊的处理,它自动就是和应用程序的生命周期是同步的,如果应用程序关闭的话,Glide的加载也会同时终止。 -
非Application类型参数:
不管你在Glide.with()
方法中传入的是Activity、FragmentActivity、v4包下的Fragment、还是app包下的Fragment,最终的流程都是一样的,那就是会向当前的Activity当中添加一个隐藏的Fragment
。具体添加的逻辑是在上述代码的第176行和第215行,分别对应的app包和v4包下的两种Fragment的情况。
这里为什么要添加一个隐藏的Fragment呢?
因为Glide需要知道加载的生命周期。很简单的一个道理,如果你在某个Activity上正在加载着一张图片,结果图片还没加载出来,Activity就被用户关掉了,那么图片还应该继续加载吗?当然不应该。可是 Glide并没有办法知道Activity的生命周期,于是Glide就使用了添加隐藏Fragment的这种小技巧,因为Fragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以监听到的,这样Glide就可以捕获这个事件并停止图片加载了
。
这里额外再提一句,从第54行代码可以看出,如果我们是在非主线程当中使用的Glide,那么不管你是传入的Activity还是Fragment,都会被强制当成Application来处理
。不过其实这就属于是在分析代码的细节了,本篇文章我们将会把目光主要放在Glide的主线工作流程上面,后面不会过多去分析这些细节方面的内容。
总体来说,第一个with()方法的源码还是比较好理解的。其实就是为了得到一个RequestManager对象而已,然后 Glide会根据我们传入with()方法的参数来确定图片加载的生命周期
,并没有什么特别复杂的逻辑。不过复杂的逻辑还在后面等着我们呢,接下来我们开始分析第二步,load()方法。