Android中图片加载框架Glide解析2----从源码的角度理解Glide的执行流程

志不强者智不达,言不信者行不果。这篇文章主要讲述Android中图片加载框架Glide解析2----从源码的角度理解Glide的执行流程相关的知识,希望能为你提供帮助。
转载地址: http://blog.csdn.net/guolin_blog/article/details/53939176

在本系列的上一篇文章中, 我们学习了Glide的基本用法, 体验了这个图片加载框架的强大功能, 以及它非常简便的API。还没有看过上一篇文章的朋友, 建议先去阅读 Android图片加载框架最全解析( 一) , Glide的基本用法 。
在多数情况下, 我们想要在界面上加载并展示一张图片只需要一行代码就能实现, 如下所示:

Glide.with(this).load(url).into(imageView);
  • 1
  • 1

虽说只有这简简单单的一行代码, 但大家可能不知道的是, Glide在背后帮我们默默执行了成吨的工作。这个形容词我想了很久, 因为我觉得用非常多这个形容词不足以描述Glide背后的工作量, 我查到的英文资料是用tons of work来进行形容的, 因此我觉得这里使用成吨来形容更加贴切一些。
虽说我们在平时使用Glide的时候格外地简单和方便, 但是知其然也要知其所以然。那么今天我们就来解析一下Glide的源码, 看看它在这些简单用法的背后, 到底执行了多么复杂的工作。
如何阅读源码 在开始解析Glide源码之前, 我想先和大家谈一下该如何阅读源码, 这个问题也是我平时被问得比较多的, 因为很多人都觉得阅读源码是一件比较困难的事情。
那么阅读源码到底困难吗? 这个当然主要还是要视具体的源码而定。比如同样是图片加载框架, 我读Volley的源码时就感觉酣畅淋漓, 并且对Volley的架构设计和代码质量深感佩服。读Glide的源码时却让我相当痛苦, 代码极其难懂。当然这里我并不是说Glide的代码写得不好, 只是因为Glide和复杂程度和Volley完全不是在一个量级上的。
那么, 虽然源码的复杂程度是外在的不可变条件, 但我们却可以通过一些技巧来提升自己阅读源码的能力。这里我和大家分享一下我平时阅读源码时所使用的技巧, 简单概括就是八个字: 抽丝剥茧、点到即止。应该认准一个功能点, 然后去分析这个功能点是如何实现的。但只要去追寻主体的实现逻辑即可, 千万不要试图去搞懂每一行代码都是什么意思, 那样很容易会陷入到思维黑洞当中, 而且越陷越深。因为这些庞大的系统都不是由一个人写出来的, 每一行代码都想搞明白, 就会感觉自己是在盲人摸象, 永远也研究不透。如果只是去分析主体的实现逻辑, 那么就有比较明确的目的性, 这样阅读源码会更加轻松, 也更加有成效。
而今天带大家阅读的Glide源码就非常适合使用这个技巧, 因为Glide的源码太复杂了, 千万不要试图去搞明白它每行代码的作用, 而是应该只分析它的主体实现逻辑。那么我们本篇文章就先确立好一个目标, 就是要通过阅读源码搞明白下面这行代码:
Glide.with(this).load(url).into(imageView);
  • 1
  • 1

到底是如何实现将一张网络图片展示到ImageView上面的。先将Glide的一整套图片加载机制的基本流程梳理清楚, 然后我们再通过后面的几篇文章具体去了解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()看起。
1. 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); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

可以看到, 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 RequestManagerRetriever(); 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 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. applicationManager = new RequestManager(context.getApplicationContext(), new ApplicationLifecycle(), new EmptyRequestManagerTreeNode()); } } } return applicationManager; }public RequestManager get(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); }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 on 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 for 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 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); } }@ TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) { RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(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).commitAllowingStateLoss(); handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget(); } } return current; }@ TargetApi(Build.VERSION_CODES.HONEYCOMB) RequestManager fragmentGet(Context context, android.app.FragmentManager fm) { RequestManagerFragment current = getRequestManagerFragment(fm); RequestManager requestManager = current.getRequestManager(); if (requestManager = = null) { requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode()); current.setRequestManager(requestManager); } return requestManager; }SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm) { SupportRequestManagerFragment current = (SupportRequestManagerFragment) 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 fm) { SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm); RequestManager requestManager = current.getRequestManager(); if (requestManager = = null) { requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode()); current.setRequestManager(requestManager); } return requestManager; }... }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152

上述代码虽然看上去逻辑有点复杂, 但是将它们梳理清楚后还是很简单的。RequestManagerRetriever类中看似有很多个get()方法的重载, 什么Context参数, Activity参数, Fragment参数等等, 实际上只有两种情况而已, 即传入Application类型的参数, 和传入非Application类型的参数。
我们先来看传入Application参数的情况。如果在Glide.with()方法中传入的是一个Application对象, 那么这里就会调用带有Context参数的get()方法重载, 然后会在第44行调用getApplicationManager()方法来获取一个RequestManager对象。其实这是最简单的一种情况, 因为Application对象的生命周期即应用程序的生命周期, 因此Glide并不需要做什么特殊的处理, 它自动就是和应用程序的生命周期是同步的, 如果应用程序关闭的话, Glide的加载也会同时终止。
接下来我们看传入非Application参数的情况。不管你在Glide.with()方法中传入的是Activity、FragmentActivity、v4包下的Fragment、还是app包下的Fragment, 最终的流程都是一样的, 那就是会向当前的Activity当中添加一个隐藏的Fragment。具体添加的逻辑是在上述代码的第117行和第141行, 分别对应的app包和v4包下的两种Fragment的情况。那么这里为什么要添加一个隐藏的Fragment呢? 因为Glide需要知道加载的生命周期。很简单的一个道理, 如果你在某个Activity上正在加载着一张图片, 结果图片还没加载出来, Activity就被用户关掉了, 那么图片还应该继续加载吗? 当然不应该。可是Glide并没有办法知道Activity的生命周期, 于是Glide就使用了添加隐藏Fragment的这种小技巧, 因为Fragment的生命周期和Activity是同步的, 如果Activity被销毁了, Fragment是可以监听到的, 这样Glide就可以捕获这个事件并停止图片加载了。
这里额外再提一句, 从第48行代码可以看出, 如果我们是在非主线程当中使用的Glide, 那么不管你是传入的Activity还是Fragment, 都会被强制当成Application来处理。不过其实这就属于是在分析代码的细节了, 本篇文章我们将会把目光主要放在Glide的主线工作流程上面, 后面不会过多去分析这些细节方面的内容。
总体来说, 第一个with()方法的源码还是比较好理解的。其实就是为了得到一个RequestManager对象而已, 然后Glide会根据我们传入with()方法的参数来确定图片加载的生命周期, 并没有什么特别复杂的逻辑。不过复杂的逻辑还在后面等着我们呢, 接下来我们开始分析第二步, load()方法。
2. load() 由于with()方法返回的是一个RequestManager对象, 那么很容易就能想到, load()方法是在RequestManager类当中的, 所以说我们首先要看的就是RequestManager这个类。不过在上一篇文章中我们学过, Glide是支持图片URL字符串、图片本地路径等等加载形式的, 因此RequestManager中也有很多个load()方法的重载。但是这里我们不可能把每个load()方法的重载都看一遍, 因此我们就只选其中一个加载图片URL字符串的load()方法来进行研究吧。
【Android中图片加载框架Glide解析2----从源码的角度理解Glide的执行流程】 RequestManager类的简化代码如下所示:
到此这篇关于Android中图片加载框架Glide解析2----从源码的角度理解Glide的执行流程内容已经讲完,如果未能解决您的问题,请参考下面的文章:

    推荐阅读