#Glide学习记录
###1.What
Glide是一款Google官方推出的图片加载库。它主要用来加载网络图片和处理图片大小、图片内存管理等问题。它的外部接口、使用方式和Picasso非常相似。它和Picasso相比既有优点也有缺点。Picasso加载出来的图片质量更高,但是Picasso会耗费更多的内存。Glide可以加载gif动图但是Picasso不能。
这是最基本的使用方式:1
2
3Glide.with(context)
.load("http://inthecheesefactory.com/uploads/source/glidepicasso/cover.jpg")
.into(ivImg);
可以看到,Glide的和Picasso一样使用链式调用的方式,整个流程看起来很清晰。
注意,这里的context和Picasso不同,它可以接受Activity或是Fragment,并从从找到相应的Context,如果这样做,同时也可以把图片加载的过程和Activity、Fragment的生命周期绑定,可以更好的控制图片。
另外一点需要注意的是两者的磁盘缓存策略不同,当图片缓存到磁盘时,Picasso保存原图,整个图像的所有像素点,而Glide只保存加载到ImageView里的图像,图像的大小和ImageView相同。
总结:在不考虑图片质量的情况下,Glide相当于Picasso的强化版本,Picasso能做的所有事情Glide都能做。
###2.How
###3.Why
首先从Glide这个类开始分析。
Glide.class1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/**
* Get the singleton.
*
* @return the singleton
*/
public static Glide get(Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
Context applicationContext = context.getApplicationContext();
List<GlideModule> modules = new ManifestParser(applicationContext).parse();
GlideBuilder builder = new GlideBuilder(applicationContext);
for (GlideModule module : modules) {
module.applyOptions(applicationContext, builder);
}
glide = builder.createGlide();
for (GlideModule module : modules) {
module.registerComponents(applicationContext, glide.registry);
}
}
}
}
get方法是Glide创建实例的过程,很明显,Glide采用了单例模式。
1 | /** |
接下来,是几个重载的with方法。仔细数一下,一共有5个,这5个with方法的参数分别是:Context、Activity、FragmentActivity、Fragment、V4Fragment。可以看到,Glide对FragmentActivity进行了判断,那么显然会在之后进行特殊处理,具体接下来再说。Glide的一个细节,它同时兼容Fragment和V4Fragment。
这几个方法完全类似,首先调用
RequestManagerRetriever.get()
方法拿到一个RequestManagerRetriever的实例,深入这个类可以发现这个实例也是一个单例实例。接下来调用
retriever.get(xxx)
让我们来看看这个方法具体的内部实现,首先来看参数为Context时的情况:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public 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);
}
可以看到,这个方法实际上是个空的调度中间方法,它没有真正的实际逻辑,检测context的种类并由相应的方法去执行,从这里可以看出两点:
- 如果当前线程不是主线程,那么最后只会走进getApplicationManager,也就是说,如果不在UI线程执行Gilde,那么Activity的生命周期绑定就不能实现。
- 实际使用时Glide.with(Context)和Glide.with(Acitivty)的逻辑其实是一样的。
接下来让我们看看当实际参数为Activity时的逻辑1
2
3
4
5
6
7
8
9
10@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, null);
}
}
可以看到,这里和刚才一样,做了一个是否在主线程的检查。接下来调用assertNotDestroyed方法,之后调用fragmentGet并返回一个RequestManager。1
2
3
4
5
6@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");
}
}
可以看到这个方法正如其名,只是做了一个简单的检查,判断这个Activity是否已经被销毁,这里我们可以看到Glide不能在Activity中的OnDestroy中调用。
1 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) |
这里是得到RequestManager的具体实现,可以看到要得到RequestManager首先要创建一个RequestManagerFragment,然后去调用requestManagerFragment的getRequestManager方法。那么现在我们的问题就是怎么创建RequestManagerFragment,以及这个东西是做什么的。
那么接下来深入getRequestManagerFragment方法来了解怎么样得到它。
1 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) |
首先调用findFragmentByTag,因为这个时候走的是一个Acitvity,所以显然这里拿不到framgnet,那么走到current==null的情况。因为是第一次执行,因此在pendingRequestManagerFragments里也拿不到fragment,注意pendingRequestManagerFragments是一个简单的map。接下来可以看到直接new出了一个RequestManagerFragment,并把它放进了pendingRequestManagerFragments里缓存留至以后再取用。可以看到这个Fragment通过Acitivity的FragmentManager被添加进了Activity里。
那么到了这里,怎么取得RequestManagerFragment这个问题已经解决了。
下面让我们来看看这个Fragment到底有什么用,它和RequestManager有什么关系。1
2
3
4
5* A view-less {@link android.support.v4.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 within the fragment or activity this fragment is a child of.
*
public class SupportRequestManagerFragment extends Fragment
来看一看这个类的注释。RequestManagerFragment是一个没有view的空白Fragment,Glide通过它来安全的储存RequetManager,并且可以用它来实现request和声明周期的绑定。这个Fragment作为目标Activity或者Fragment的子Fragment来实现对声明周期的绑定!
Nice,到了这里我们已经发现了Glide一个重要特性:“将图片加载和UI的生命周期绑定”的实现方法。
那么具体是如何做的呢?通过观察源码可以发现
private final ActivityFragmentLifecycle lifecycle;
这样一个成员变量,这个东西其实就是一个listener的控制器,它把所有监听生命周期的listener封装在一个set里,然后在fragment生命周期发生变化时去回调它。代码比较简单,我就不贴了。
你可能会问,为什么能通过这样一个child fragment绑定activity或fragment的生命周期呢?
让我们来看一看Google对Fragment生命周期的描述:
与 Activity 生命周期协调一致 片段所在的 Activity 的生命周期会影响片段的生命周期,其表现为,Activity 的每次生命周期回调都会引发每个片段的类似回调。 例如,当 Activity 收到 onPause() 时,Activity
中的每个片段也会收到 onPause()。不过,片段还有几个额外的生命周期回调,用于处理与 Activity 的唯一交互,以执行构建和销毁片段 UI 等操作。这些额外的回调方法是:
onAttach() 在片段已与 Activity 关联时调用(Activity 传递到此方法内)。 onCreateView()
调用它可创建与片段关联的视图层次结构。 onActivityCreated() 在 Activity 的 onCreate()
方法已返回时调用。 onDestroyView() 在删除与片段关联的视图层次结构时调用。 onDetach() 在取消片段与
Activity 的关联时调用。 图 3 图示说明了受其宿主 Activity 影响的片段生命周期流。在该图中,您可以看到 Activity
的每个连续状态如何决定片段可以收到的回调方法。 例如,当 Activity 收到其 onCreate() 回调时,Activity
中的片段只会收到 onActivityCreated() 回调。一旦 Activity 达到恢复状态,您就可以意向 Activity 添加片段和删除其中的片段。 因此,只有当 Activity
处于恢复状态时,片段的生命周期才能独立变化。不过,当 Activity 离开恢复状态时,片段会在 Activity 的推动下再次经历其生命周期。
现在你明白了吧?
解决了刚才的问题,现在回头来继续看RequestManager的部分1
2
3
4
5
6
7
8
9
10
11
12
13
14@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, android.app.FragmentManager fm,
android.app.Fragment parentHint) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
new RequestManager(glide, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
可以看到,RequestManager采用了和RequestManagerFragment相同的创建策略,第一次创建新对象,然后缓存,以后调用时直接取用。
到了这一步,RequestManager的创建过程搞清楚了。Glide.with(context)方法就没有问题了,我们仅就Activity的情况进行了分析,但是可以推测,其它情况下的执行流程也是类似的。注意,如果传过去的是一个application的实例,是不能和声明周期绑定的,这是因为Application和Activity与Fragment不同,不能创建child fragment,因此刚才说的那一套方法在Application上不能生效。
Glide:
https://github.com/bumptech/glide
Picasso:
https://github.com/square/picasso
参考:
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0327/2650.html