Glide 是现在Android开发常用的一个图片加载工具,可以根据资源文件、网络URL请求图片,并设置到控件当中。并且有一套完整的缓存重用机制,可以很大程度上地节约内存。
传统的Android 图片请求
我们首先需要通过网络请求工具请求图片,以Stream的形式将图片存储于一个Bitmap对象中,然后再通过setBitmap(bitmap)将图片设置进去。
Thread(Runnable {
val bitmap = getBitmap(sampleWallPaper)//开启Http连接,加载Stream,转成Bitmap
runOnUiThread {
glide_out.setImageBitmap(bitmap)
}
}).start()
这样一来,就涉及到了在代码中分散的网络请求、网络连接等等,还涉及到生命周期对加载的影响。
Glide 的使用
不带任何配置的Glide图片请求很简单:
Glide.with(context)
.load(url)
.into(imageView)
Glide 的使用步骤中,有三个主要步骤,其中with是绑定生命周期,load是加载图片,into是设置图片。接下来我们会从这三个方面来进行分析。
With部分解析
With(Context context)方法
在Glide.java的定义中,With方法有如下几种重载方法,对象包括Context、Activity、Fragment等等,但是溯源到最后,都是调用的Fragment内部的fragment.getContext()或者是View.getContext()方法。
我们打开最基本的with(context)方法:
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
这个静态方法传入了一个运行时的上下文对象Context,返回了一个RequestManager,而RequestManager的部分定义如下:
//RequestManager.java
public class RequestManager implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>> {
// 省略
protected final Glide glide;
protected final Context context;
@SuppressWarnings("WeakerAccess")
@Synthetic
final Lifecycle lifecycle;
//省略
}
RequestManagerc持有着Glide、Context、LifeCycle的引用,前面二者我们比较熟悉,但是LifeCycle可能比较陌生:
public interface Lifecycle {
void addListener(@NonNull LifecycleListener listener);
void removeListener(@NonNull LifecycleListener listener);
}
其本身是一个接口,一共只有两个接口方法:1.添加接听者,2.移除监听者。而这个LifeCycleListener的定义就比较有意思了:
//package com.bumptech.glide.manager;
public interface LifecycleListener {
void onStart();
void onStop();
void onDestroy();
}
这三个接口方法和Activity或者是Fragment中的三个生命周期方法同名。我们看看其中之一onStart()方法的调用处:
我们又回到了RequestManager.java中,显然,RequestManager通过这三个方法来控制生命周期和请求的停等关系。
@Override
public synchronized void onStart() {
resumeRequests();
targetTracker.onStart();
}
@Override
public synchronized void onStop() {
pauseRequests();
targetTracker.onStop();
}
@Override
public synchronized void onDestroy() {
targetTracker.onDestroy();
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
targetTracker.clear();
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
Util.removeCallbacksOnUiThread(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}
其中有一个变量targetTracker,它本身也是实现了LifecycleListener。
private final TargetTracker targetTracker = new TargetTracker();
回到with方法:
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
我们来看看getRetriever()方法,方法定义:
@NonNull
private 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();
}
该CheckNull的意思是:我们不能把一个还未attach的View或者是一个Fragment中,调用getActivity还返回NULL的时候的Fragment进行调用(通常是Fragement的isAttached的状态之前或者是被Destroy之后)。
换句话来说,在Fragment中,我们只能在onAttach()和onDetach()状态之间调用。
在Glide.get方法主要功能是将我们之前构建的LifeCycle去创建一个RequestManager,但是构建RequestManager时,采用不同的context会有不同的构建方法和思路,这里不做展开,文章末尾有做解析与比较。下面的流程都是采用Activity作为Context进行的,而不是ApplicationContext。
- ApplicationContext和普通的Activity.Context有什么区别? Context本意是“上下文”,就是当前的代码的一个运行环境。ApplicationContext就是整个Application下的一个Context,但是并没有ApplicationContext单独的一个类存在。通常情况下来说,和UI相关的Context相关操作都应该使用Activity做为Context来处理;其他的一些操作,Service,Activity,Application等实例都可以,当然了,注意Context引用的持有,防止内存泄漏。
get方法中,根据Context的类型判断,做了分支处理。我们选择Activity分支进入:
@NonNull
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else if (activity instanceof FragmentActivity) {
return get((FragmentActivity) activity);
} else {
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
android.app.FragmentManager fm = activity.getFragmentManager();//****
return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));//****
}
}
一开始也是一个分支判断,不同的情况交由不同分支的重载函数进行处理。
- 具体的处理方式可以看StackOverFlow上的这个问题:glide-image-loading-with-application-context 大意是:如果我们在Fragment上使用Glide,并且采用的是ApplicationContext,当我们的Fragment被移除后。Glide仍然会加载图片,甚至设置到ImageView当中,最后会被GC算法回收,就不会根据Fragment的生命周期来进行处理了。
注意加注释的这两行,这就是整个Glide与宿主生命周期绑定的关键,第一行获取了Activity的Fragment的FragmentManager。 在getRequestManagerFragment(Fragment:fragment)的实现方法中,这两行如下:
android.app.FragmentManager fm = fragment.getChildFragmentManager();
return fragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
第二行的fragmentGet方法的方法体如下:
private RequestManager fragmentGet(
@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
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 =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
// This is a bit of hack, we're going to start the RequestManager, but not the
// corresponding Lifecycle. It's safe to start the RequestManager, but starting the
// Lifecycle might trigger memory leaks. See b/154405040
if (isParentVisible) {
requestManager.onStart();
}
current.setRequestManager(requestManager);
}
return requestManager;
}
在其中构建了一个RequestManagerFragment,这个RequestManagerFragment继承自Fragment就是我们日常使用的Fragment:
public class RequestManagerFragment extends Fragment {
//省略
}
然后 通过builder模式创建RequestManager,并且将fragment的lifecycle传入,这样Fragment和RequestManager就建立了联系。RequestManagerFragment会持有一个ActivityFragmentLifecycle类型的,名为LifeCycle的变量,该变量可以回调RequestManager中的方法。当一个Fragment被贴在Activity上,这样一来,空白UI的Fragment中的三个生命周期的回调方法就能得到回调了:
With部分总结
接下来我们来总结一下Glide是如何与生命周期绑定的。首先明确三个基本问题:
1.Glide要如何感知绑定的Activity的生命周期?
2.Glide如何传递生命周期的钩子函数?
3.Glide绑定的流程是什么?
-
对于第一个问题,答案是Glide构建了一个无UI的Fragment,并将它贴在需要监听的Activity上,通过Activity和Fragment的生命周期来进行感知。
-
第二个问题:Glide的无UI的Fragment中,持有lifecycle的引用,ActivityFragmentLifecycle是Glide的类,内部包含了一个数据结构:
private final Set<LifecycleListener> lifecycleListeners = Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());
当我们在不同的生命周期的阶段会做不同的事情:
void onStart() {
isStarted = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStart();
}
}
void onStop() {
isStarted = false;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStop();
}
}
void onDestroy() {
isDestroyed = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onDestroy();
}
}
这里其实采用的是观察者模式,三个onXXX方法就是事件,一旦事件发生,那么就会通知所有已经订阅的消费者出来消费事件。lifecycleListeners在addListener方法被添加值:
@Override
public void addListener(@NonNull LifecycleListener listener) {
lifecycleListeners.add(listener);
if (isDestroyed) {
listener.onDestroy();
} else if (isStarted) {
listener.onStart();
} else {
listener.onStop();
}
}
而这个addListener正是在RequestManager中的构造函数中被调用的:
- 对于第三个问题,绑定的流程,如图:
附:Lifecycle的创建问题。
如果我们在get方法传入的Context是ApplicationContext,那么会像这样新建一个:new ApplicationLifecycle():
factory.build(
glide,
new ApplicationLifecycle(),//新建一个ApplicationContext
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
如果我们传入的是其他的Context,那么则会直接调用current.GetGlideLifeCycle():
factory.build(
glide,
current.getGlideLifecycle(),
current.getRequestManagerTreeNode(),
context);
这种情况会返回current的LifeCycle,这个current是在getSupportRequestManagerFragment()中将空白UI的Fragment贴在宿主上之前,通过构造函数创建的:
public SupportRequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}
@NonNull
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint) {
SupportRequestManagerFragment current =
(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = new SupportRequestManagerFragment(); //如果没有此处创建的
current.setParentFragmentHint(parentHint);
pendingSupportRequestManagerFragments.put(fm, current);//存入pendingSupportRequestManagerFragments
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
两个并不是同一个LifeCycle,这和前文中提到的StackOverflow上的那篇回答【glide-image-loading-with-application-context】说的一样,ApplicationLifeCycle中只对添加、移除事件进行了处理:
class ApplicationLifecycle implements Lifecycle {
@Override
public void addListener(@NonNull LifecycleListener listener) {
listener.onStart();
}
@Override
public void removeListener(@NonNull LifecycleListener listener) {
// Do nothing.
}
}
由于Application的生命周期是App本身,所以自然而然地也就不需要处理那么多的onStart、onStop等等的方法了。一旦App退出或者被回收,那么自然这些对象也会被回收。
所以,传入不同的Context,实现的方式也有不同,在BackGroundThread执行或者是传入ApplicationContext是不会生成空白Fragment的。(前者的主要原因是如果在子线程执行,那么默认就是传入applicationContext)。使用ApplicationContext会使得Glide的生命周期和App一样长。