当前位置: 首页 > 科技观察

Glide使用的LruCache,你知道多少?

时间:2023-03-15 22:32:21 科技观察

前言说到Glide,有点尴尬。本来想到一篇文章《手撕Glide》,可惜源码太多了。写了三千多字,还没写完。实在不妥,因为我的写作原则是短小精悍,所以暂时不发这篇文章。这一次,我将谈谈Glide的使用。LruCache有什么神奇之处。另外,我的抖音采访在即,不知道有没有达到水平,所以现在发的算第一。思维导图的使用方法和结果在项目中直接导入Glide库,调用内部LruCache查看效果。LruCachelruCache=newLruCache(2);lruCache.put("1",1);lruCache.put("2",2);lruCache.put("1",1);lruCache.put("3",3);System.out.println(lruCache.get("1"));System.out.println(lruCache.get("2"));System.out.println(lruCache.get("3"));简单说明一下代码内容,创建一个空间为2的存储空间(内部结构这里就不说了),使用put()方法存储数据,然后通过get()对每条数据进行一次获取操作,然后我们再看一下结果。我的上帝!!2没了?怎么了??要想知道答案,我们还得进入Glide库看看原因。LruCache源码攻略先看LruCache变量家族中的小家伙们。publicclassLruCache{//双向链表,容量为100privatefinalMapcache=newLinkedHashMap<>(100,0.75f,true);privatefinallonginitialMaxSize;//初始化最大容量privatelongmaxSize;//最大容量privatelongcurrentSize;//现有容量}同样,对于LruCache,和HashMap一样只有三步走,那我就从这三步开始探索LruCache,但是我们要从一个问题开始,initialMaxSize的作用是什么?newLruCachepublicLruCache(longsize){this.initialMaxSize=size;this.maxSize=size;}这里的套路读者已经知道了,初始化最大容量和最大容量,那么进入下一步。put(key,value)publicsynchronizedYput(@NonNullTkey,@NullableYitem){//返回值为一个1finalinitemSize=getSize(item);//如果1大于等于最大值,则没有操作//它表示整个初始化不能Setsizeto1if(itemSize>=maxSize){//重写的保留方法onItemEvicted(key,item);returnnull;}//对当前已有数据容量加一if(item!=null){currentSize+=itemSize;}@NullablefinalYold=cache.put(key,item);if(old!=null){currentSize-=getSize(old);if(!old.equals(item)){onItemEvicted(key,old);}}evict();//1-->returnold;}//Note1直接调用的方法privatevoidevict(){trimToSize(maxSize);//2-->}//Note直接调用的方法2protectedsynchronizedvoidtrimToSize(longsize){Map.Entrylast;Iterator>cacheIterator;//表示当前容量大于最大容量//需要清理最后一个数据while(currentSize>size){cacheIterator=cache.entrySet().iterator();last=cacheIterator.next();finalYtoRemove=last.getValue();currentSize-=getSize(toRemove);finalTkey=last.getKey();cacheIterator.remove();onItemEvicted(key,toRemove);}}这是一个方法一个锁定机制。通过判断当前容量和最大容量,我们可以决定是否删除我们的数据。但是问题依然存在,initialMaxSize的作用是什么?我们可以知道的是,maxSize是一个用来控制容量大小的值。get()publicsynchronizedYget(@NonNullTkey){returncache.get(key);}那么这就是调用LinkedHashMap中的数据了,不过毕竟还是没有提到initialMaxSize的作用。关于initialMaxSize,我这里不买账,因为其实在我看来,这个initialMaxSize真的没什么用。哈哈哈哈哈!!!但是,它被用在另一个地方。publicsynchronizedvoidsetSizeMultiplier(floatmultiplier){if(multiplier<0){thrownewIllegalArgumentException("Multipliermustbe>=0");}maxSize=Math.round(initialMaxSize*multiplier);evict();}是用来调节我们的最大容量的,不过我觉得还是没用,不过我太牛了,这个方法没有其他方法可以调用,使用过程中直接使用,可能和多次使用Link,场景的一个保存数据的问题有关类似于Glide的图片缓存加载。也希望知道的读者能给我一个答案。LinkedHashMap就不赘述了,因为操作方式和HashMap是一致的,看它的节点的样子就知道了。staticclassLinkedHashMapEntryextendsHashMap.Node{//有前后节点,也就是我们所说的双向链表LinkedHashMapEntrybefore,after;LinkedHashMapEntry(inthash,Kkey,Vvalue,Nodenext){super(hash,key,value,next);}}但是在这里,我还有一个问题,为什么我看不到整个数据的移动呢?即最近使用的数据应该交换到end的起始位置,他到底在哪里处理呢?猜一猜,既然使用put()会引起双向链表中的数据变换,那么我们应该需要进入LinkedHashMap.put()方法查询。当然,对于有兴趣探索的读者,我需要提醒你的是,这个调用不能直接查询put(),它只会调用接口函数或者抽象类函数。最合适的方法是使用我们的断点进行探索性查询。但是经过一段时间的努力,他不断调用,深入探索,发现了这样的问题,最终还是会调用这样的问题。//CallbackstoallowLinkedHashMappost-actionsvoidafterNodeAccess(Nodep){}//移动数据到最后一个位置voidafterNodeInsertion(booleanevict){}voidafterNodeRemoval(Nodep){}这就是我们学习的HashMapbefore但是没找到几个方法,明明写着是给LinkedHashMap保留的。哇!!那么我们的操作就必须在这些。//-->HashMap源码656行附近调用了下面的方法//在putVal()方法afterNodeAccess(e)里面出现了这个;//-->LinkedHashMap的具体实现//是直接push当前数据to最后一个位置//是最近刚用过的数据p=(LinkedHashMapEntry)e,b=p.before,a=p.after;p.after=null;if(b==null)head=a;elseb.after=a;if(a!=null)a.before=b;elselast=b;if(last==null)head=p;else{p.before=last;last.after=p;}tail=p;++modCount;}}好了,至此我们就明白了整个链表的转换过程。实战:手动LruCache是??非常精彩和精彩的部分。在滚动代码之前,让我们寻找想法。(1)储存容器是做什么用的?由于LinkedHashMap的思路过于冗长,我们使用数组重新完成整个代码的构建(2)关键调用方法put()、get()、put()涉及到已有的变量shift。哇!好像没有那么多事情要做,先看看第一次搭建的框架吧。publicclassLruCache{privateObjectobjects[];privateintmaxSize;privateintcurrentSize;publicLruCache(intsize){objects=newObject[size];maxSize=size;}/***Insertitem*@paramitem*/publicvoidput(Objectitem){}/***getitem*@paramitem*/publicObjectget(Objectitem){returnnull;}/***根据下标对应,将后面的数组移位*@paramindex*/publicvoidmove(intindex){}}因为只要有移位就有移位数组转换,所以Shift操作是必不可少的。那么我们当前的工作就是填写数据,对应的shift是什么操作。publicclassLruCache{publicObjectobjects[];privateintmaxSize;publicintcurrentSize;publicLruCache(intsize){objects=newObject[size];maxSize=size;}/***insertitem**@paramitem*/publicvoidput(Objectitem){//当容量为notfull分为两种情况//1。//2存在于容器内部。intindex=search(item);if(index==-1){if(currentSize