志不强者智不达,言不信者行不果。这篇文章主要讲述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的执行流程内容已经讲完,如果未能解决您的问题,请参考下面的文章:
推荐阅读
- Android--开发:由模块化到组件化
- Android应用开发-从设计到实现 3-9 Origami动态原型设计
- android 消息传递机制EventBus的深入探究
- Android开发系列之UI开发
- android 注解
- Android 解决RecyclerView删除Item导致位置错乱的问题
- Android Studio常用快捷键Android Studio快捷键大全
- Xamarin.Forms开发APP
- Android Studio常见问题