Glide框架是一款非常经典的图片加载框架,在使用方法上显示出了简单便捷、自动绑定页面生命周期、自动管理内存和图片缓存等诸多优秀的地方。
这里我们透过Glide的使用方式,来逐一分析Glide的设计思想和实现方式。本文基于Glide 4.8.0版本
。
简洁的初始化:Glide.with()
标题的代码大伙儿都不会陌生,因为所有使用Glide的场景都首先要调用with
方法。而Glide 的初始化所需要做的一切工作,也就这一句足矣。with()是Glide的静态方法。调用非常简单只需要传入Activity、或是Fragment,或者干脆就是Context,但深入进去会发现它在初始化Glide的过程中做了非常多的事情。
/**
* @param context Any context, will not be retained.
* @return A RequestManager for the top level application that can be used to start a load.
* @see #with(android.app.Activity)
* @see #with(android.app.Fragment)
* @see #with(androidx.fragment.app.Fragment)
* @see #with(androidx.fragment.app.FragmentActivity)
*/
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
加载之前的准备,得到RequestManager的“寻回者”
getRetriver()
中一共做了两件大事:获取Glide单例对象(如果没有就从头初始化Glide单例);组装合适的RequestManagerRetriver
对象,以备后面拿到RequestManager
做准备。
先说Glide单例的初始化过程:1. 检查传入Context是否可用;2. 调用Glide.get(context)
得到Glide单例;3.使用单例获取RequestManagerRetriver()
。
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.
// 确保Context非空,不过是在想不通,api中允许Context可空,进来又得检查空,是不是多此一举。。没想通为何这么设计。
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();
}
1. 真正的Glide单例初始化流程 Glide.get(context)
/**
* Get the singleton.
*
* @return the singleton
*/
@NonNull
public static Glide get(@NonNull Context context) {
if (glide == null) {
//annotationGeneratedModule先忽略,不影响我们分析初始化流程
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
这个写法一看就是静态类方式实现单例。进入checkAndInitializeGlide()
在夸过中间两层校验之后,终于来到initializeGlide(congtext,builder,annotationGeneratedModule)
,代码如下:
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder,
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
Context applicationContext = context.getApplicationContext();
。。。省略。。。
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory()
: null;
builder.setRequestManagerFactory(factory);
。。。省略。。。
Glide glide = builder.build(applicationContext);
。。。省略。。。
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
主要干了5件事:
- 得到applicationContext 对象。
- 获得
RequestManagerFactory
对象,后续用来组装出合适的RequestManager
,进行图的加载 - 将上述两个对象设置到GlideBuilder中去,并得到Glide 的实际对象。
- 通过
ComponentCallbacks2
协议(接口)将Glide对象与ApplicationContext绑定,用来监听系统内存状况。 - 单例变量赋值,完成单例的创建。
这5步中可以看下builder.build()的内容,能够更好的帮我们理解初始化都设置了哪些组件。后续内容我们会围绕着下面这些构成Glide实例的组件进行展开讲解。
Glide build(@NonNull Context context) {
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();
}
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
if (connectivityMonitorFactory == null) {
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
}
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
engine =
new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
animationExecutor,
isActiveResourceRetentionAllowed);
}
if (defaultRequestListeners == null) {
defaultRequestListeners = Collections.emptyList();
} else {
defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
}
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
isLoggingRequestOriginsEnabled,
isImageDecoderEnabledForBitmaps);
}
2. 使用单例获取RequestManagerRetriver()
早在第一步的时候,requestManagerRetriever就已经被实例化传入Glide构造器中。因此这里我们要分析的是,RequestManagerRetriever中传入工厂对象,又做了什么。
其实,RetquestManagerRetriever就只干了一件事儿,就是如何获取最合适的RequestManager
。这时不禁会想,RequestManager就是一个请求管理的对象,何来的最合适?
这里就体现出了Glide设计中的一个亮点:生命周期绑定
。我们来看关键代码:
public RequestManagerRetriever(@Nullable RequestManagerFactory factory) {
this.factory = factory != null ? factory : DEFAULT_FACTORY;
handler = new Handler(Looper.getMainLooper(), this /* Callback */);
}
首先是构造器的元素构成:分别是用到RequestManagerRetriever和Handler。由此可见,factory负责生产RequestManager,handler负责主线程发送消息。
下面我们重点看 factory是如何生成RequestManager和生命周期是如何做绑定的
。
RequestManagerRetriever类中有诸多get()方法,其中有一个是其余所有get方法的入口:
public 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);
}
在通过了context判空校验后,主题逻辑做了一下5件事儿:
- 判断当前是否在主线程以及传入context是否是Application实例。如果在子线程或者传入的是个Application实例,则直接返回
getApplicationManager()
,也就是返回一个绑定了ApplicationLifecycle
生命周期的RequestManager
。 - 如果1中条件不满足,既在主线程,又不是Application实例的context,则分别判断context属于哪个实例范畴。至于为什么要根据context判断是哪个实例,以及了解Context到底是个什么东西,有什么作用,可以参考之前的一篇文章:《详解 Context》。
- 如果context是FragmnetActivity,则调用get(FragmentActivity context)
- 如果context是Activity,则调用get(Activity context)
- 如果context是ContextWrapper,则调用get(contextWrapper.getBaseContext()),入参的真实类型其实就是ContextImpl。这在《详解 Context》中也做了说明。
当然,这个方法并不是每次都必须调用。其实Glide.with()
中 的入参会直接作为这里的入参get(),因此传入的参数直接对应到上述第3或4条内容.
但是细心观察会发现,这里似乎少了两个get()方法。Glide.with().get(android.app.Fragment) 和Glide.with().get(android.support.v4.app.Fragment)哪去了?
别急,这两个方法在Retriver中肯定也有。如下:
@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());
}
}
public RequestManager get(@NonNull android.support.v4.app.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());
}
}
只不过,Glide在迭代的过程中,逐渐使用v4包代替原生包,因此get(android.app.Fragment fragment)方法已被废弃。但其实他们的核心逻辑都是一样的。
甚至是,整个RequestManagerRetriever围绕着get方法展开的核心逻辑都是一样的,用一句话就能够概括:非主线程或全局请求则绑定ApplicationLifeCircle;主线程且页面级别请求,则通过获取对应页面类型的FragmentManager,组装并绑定一个无展示Fragment,进而能够进行与生命周期相关的各种图片操作
。这也是整个Glide库的设计亮点之一。
怀着这个思想,我们的目光就聚焦在了:通过fragmentManager,要管理的无展示Fragment是什么样的?它是如何被创建的,又是如何运作的呢。
使用“寻回者”RequestManagerRetriever,获取RequestManager
之前不是介绍到了获取传入组件的fragmentManager了吗,自然而然就想到了,要去管理和操控的,只能使Fragment了。带着这个思路我们去看supportFragmentGet()
方法(fragmentGet()方法类似,只不过已经被废弃,就不以它举例了)。
private 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;
}
这里面就做了两件事儿:
- 获取一个与当前fragmentManager对应的Fragment,如果没有则创建。
- 从1中的fragment中获取RequestManager,如果没有,则使用factory工厂创建,并置入刚才的fragment中,然后返回requestManager。
通过这种方式,把RequestManager与Fragment生命周期相联系,从而能够获取页面的各种回调状态。确实是一个很巧妙的方式。
接下来详细了解下getSupportRequestManagerFragment()
方法中是如何获取Fragment的吧。
private 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;
}
方式也很简单,分为4步执行:
- 通过fragmentManager查找是否已经存在关联状态下的SupportRequestManagerFragment,如果有,则直接返回;没有则执行第二步。
- 从
pendingSupportRequestManagerFragments
这个Map中寻找以fragmentManager为Key的SupportRequestManagerFragment,如果没有,则创建新的,并加入到Map中进行存储。并通过setParentFragmentHint(parentFragment)
维护从Activity开始的一个Fragment树。如果parent此时是可见的,那么当前Fragment也就可以执行生命周期的onStart()
,这个方法会触发生命周期的监听,通知Request状态的恢复等行为。 - 通过fragmentManager,添加当前Fragment。
- 使用handler进行消息通知,在Fragment完成添加的时候,从
pendingSupportRequestManagerFragments
中删除。
最后,执行requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
通过工厂方式实例化RequestManager实例。
最后说下handler,刚才在获取SupportRequestManagerFragment的时候,最后一步,通过handler进行消息通知,从MessageID上能够看出,是需要从fragmentManager中remove掉这个fragment。这里之所以使用handler去传递事件消息,就是因为它需要在fragmentManager完成添加事务之后,再进行调用。这两件事情之间需要保证消息的顺序执行。
@Override
public boolean handleMessage(Message message) {
boolean handled = true;
Object removed = null;
Object key = null;
switch (message.what) {
case ID_REMOVE_FRAGMENT_MANAGER:
android.app.FragmentManager fm = (android.app.FragmentManager) message.obj;
key = fm;
removed = pendingRequestManagerFragments.remove(fm);
break;
case ID_REMOVE_SUPPORT_FRAGMENT_MANAGER:
FragmentManager supportFm = (FragmentManager) message.obj;
key = supportFm;
removed = pendingSupportRequestManagerFragments.remove(supportFm);
break;
default:
handled = false;
break;
}
if (handled && removed == null && Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Failed to remove expected request manager fragment, manager: " + key);
}
return handled;
}
RequestManagerRetriever自身只会发送并处理两种Message,即ID_REMOVE_FRAGMENT_MANAGER
和ID_REMOVE_SUPPORT_FRAGMENT_MANAGER
。也是根据传入Fragment的不同(v4和非v4)进行的不同处理。
到此,Glide的准备阶段完成。
总结
Glide初始化准备过程中总共做了三件大事:
- Glide单例实例化
- 通过传入的不同组件,构造RequestManagerRetriever对象,组装出对应于不同页面层级的RequestManagerFragment,绑定页面生命周期。
- 组装好RequestManager,完成准备工作。
下一篇,我们会着重分析RequestManager是如何加载各种形式的图片资源,以及它的缓存机制。