介绍
常规方式:Glide.with(this).load(URL).into(imageView)
虽然with方法重载了很多个,我们可以传入不同的对象,但是终归会分为两种。
后面我们还可以设置各种存储方式,加载的图片也可以设置各种设置,基本的源码学习还是分为三部分。
with、load、into
with(生命周期)
1、分为两种情况,一种会创建空白Fragment,一种不会。
子线程调用的Glide.with和传入Application的不会,其他的会。
创建一个空白的Fragment贴上去
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
Util.assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm);
}
}
@NonNull
public RequestManager get(@NonNull Fragment fragment) { // androidx
if (Util.isOnBackgroundThread()) {
return get(fragment.getContext().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getContext(), fm);
}
}
将准备好的空白Fragment添加上去
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm) {
// 1、从 FragmentManager 中获取 SupportRequestManagerFragment(空白)
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
// 2、从该 空白Fragment 中获取 RequestManager
RequestManager requestManager = current.getRequestManager();
// 3、首次获取,则实例化 RequestManager
if (requestManager == null) { //这样做的目的是为了 一个Activity或Fragment 只能有一个 RequestManager
// 3.1 实例化
Glide glide = Glide.get(context);
requestManager = new RequestManager(glide, current.getGlideLifecycle(), context);
// 3.2 设置 Fragment 对应的 RequestManager 空白的Fragment<--->requestManager
current.setRequestManager(requestManager);
}
return requestManager;
}
如何保证一个Activity或者Fragment只会有一个空白的Fragment呢?
// 1、从 FragmentManager 中获取 SupportRequestManagerFragment
@NonNull
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm) {
SupportRequestManagerFragment current =
(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
// 1.2 尝试从【记录保存】中获取 Fragment
current = pendingSupportRequestManagerFragments.get(fm);
// 1.3 实例化 Fragment
if (current == null) {
// 1.3.1 创建对象 空白的Fragment
current = new SupportRequestManagerFragment();
// 1.3.2 【记录保存】映射关系 进行保存 第一个保障
// 一个MainActivity == 一个空白的SupportRequestManagerFragment == 一个RequestManager
pendingSupportRequestManagerFragments.put(fm, current);
// 1.3.3 提交 Fragment 事务
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
// 1.3.4 post 一个消息
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
创建了空白Fragment之后存储到对应的集合里面
@VisibleForTesting
final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments =
new HashMap<>();
在通过handler指令移除
switch (message.what) {
case ID_REMOVE_FRAGMENT_MANAGER: // 移除 【记录保存】 1.3.5 post 一个消息
android.app.FragmentManager fm = (android.app.FragmentManager) message.obj;
pendingRequestManagerFragments.remove(fm); // 1.3.6 移除临时记录中的映射关系
break;
1、上面几个方法的流程说明:首先从FragmentManager 查找对应的tag的空白Fragment,第一次肯定找不到。
2、尝试从缓存数组当中找一个,找不到,创建之前再判断一次,防止刚才遍历数组的时候已经有另外的代码创建了一次。子线程是不会创建空白Fragment,按照道理只会在主线程才会执行的代码,应该从上到下执行的代码不可能出现多线程情况,下面的代码会解释。
3、创建一个空白Fragment,添加到缓存数组,添加到FragmentManager 里面,通过事物提交。
事物提交内部的机制是一个handler,那么就说明它不是立即执行,有一定的延迟。
4、通过handler主线程发送一个消息,删除刚添加到缓存数组的空白Fragment。
5、后面进来的代码一检查可以从tag中拿到已经贴上去的空白Fragment,就不会再进入这个方法了。
这个是基本逻辑。
将空白的Fragment贴上去这个操作是事务提交,底层还是通过一个handler指令操作,那么会有一定的延迟,如果这个时候第二个创建操作的代码执行过来,从Tag中肯定是找不到的,从缓存数组中再判断一次是因为如果上一个创建指令还没完全执行完贴上空白的Fragment,那么数组里面的临时数据也还没删除,那么如果数组里有数据我们直接就不需要再执行一次了,这也是双重保障。
考虑得非常详细,UI线程理论上不用考虑多线程的情况,但是这里连发送一个handler这种极限非常少的时间也考虑到了。
这就是没有加锁对象的时候创建空白Fragment包装只会一对一的关键代码逻辑。
2、为什么创建一个空白Fragment贴上去?
这个问题其实有点傻,现在jpt的lifecycle监听生命周期方法就是来自Glide,看它的生命周期方法就知道了。
// andrid x 的 空白的Fragment 监听生命周期变化的
public class SupportRequestManagerFragment extends Fragment {
private static final String TAG = "SupportRMFragment";
private final ActivityFragmentLifecycle lifecycle;
@Nullable private RequestManager requestManager;
public SupportRequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}
@VisibleForTesting
@SuppressLint("ValidFragment")
public SupportRequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
this.lifecycle = lifecycle;
}
public void setRequestManager(@Nullable RequestManager requestManager) {
this.requestManager = requestManager;
}
@NonNull
public ActivityFragmentLifecycle getGlideLifecycle() {
return lifecycle;
}
@Nullable
public RequestManager getRequestManager() {
return requestManager;
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// this.lifecycle.addListener(requestManager);
}
@Override
public void onDetach() {
super.onDetach();
}
@Override
public void onStart() {
super.onStart();
lifecycle.onStart();
}
@Override
public void onStop() {
super.onStop();
lifecycle.onStop();
}
@Override
public void onDestroy() {
super.onDestroy();
lifecycle.onDestroy();
}
}
into
这个环节不准备深入讲解,我还没有深入分析,给大家准备一张神图。
缓存
LRU缓存
Glide里面的内存缓存和磁盘缓存都是使用了LRU。
1、指定大小。
2、最近、最少使用原则。
内部是链表结构,最近使用的添加到链头,当满了的时候链尾且是使用次数最少的先移除。
三级缓存
Glide有三级缓存,分别是磁盘缓存,和两种内存缓存,一种强引用持有的LRU缓存,一种若引用持有的活动缓存。
为什么要有两种内存缓存
如果我只有一种LRU缓存,那么我正在使用当中的资源就有可能被移除当前LRU的缓存池且释放掉,那不就GameOver了。
如果只有活动缓存一种,这是弱引用创建的,都活不过下一次垃圾回收,那么这个缓存的效果就太弱了。
如果活动缓存类似LRU是强引用持有,那么就会太庞大了,达到一定程度还是要移除一些资源。
加载顺序
1、正在使用当中的存储于活动缓存,页面销毁则移动LRU缓存。
2、加载顺序:先从活动缓存找、再从LRU缓存找、最后找磁盘缓存、从网络加载
3、有runing队列和等待队列,因为存在页面加载了一半就被关闭进入其他页面,等待队列全部清空,加载当前界面要显示的队列到runing队列。
4、Glide判断重复的标准是很严格的,一种完全相同内容的图片,宽高、名字等等有一点点差距都认为是不同的图片。
5、加载完成存储到磁盘缓存同时加载到活动缓存。
活动缓存为什么使用弱引用
我们都知道弱引用持有的对象活不过下一次垃圾回收。
使用弱引用是防止发生内存泄露,即防止出现对象的内存申请了以后就一直释放不了的情况,如果出现太多就会产生内存不够用的情况。一旦使用强引用,该对象就很难被垃圾回收器进行回收了。
弱引用队列ReferenceQueue
activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
场景:我一个页面recyclerView加载了很多的图片,那么滑动很多次,加载了很多,那些已经加载过的图片资源全部都在活动缓存了,如果我滑得比较多,recyclerView本身缓存的ViewHolder已经满了。
1、也就是看不见的ViewHolder有一些不持有活动缓存里面的图片资源了,强引用断开,这个时候这些是弱引用创建的活不过下一个垃圾回收。
2、那些ViewHolder被缓存的还会持有活动缓存的图片资源,有强引用,他们就不会被回收。
在页面销毁的时候活动缓存的图片会被移除,被LRU缓存强引用持有。活动缓存使用弱引用确保一个界面类似滑动列表这种加载非常多的图片,也不会因为强引用导致垃圾回收器无法回收。