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

Android进阶之彻底理解LruCache缓存机制原理

时间:2023-03-13 21:24:57 科技观察

深入理解Android进阶LruCache缓存机制原理将这两种缓存机制的实现应用到LruCache算法中。今天我们将从使用到源码分析,彻底了解Android中的缓存机制;一、LruCache的概念介绍1、什么是LruCache?LruCache是??Android3.1提供的一个Cache类,所以在Android中可以直接使用LruCache来实现内存缓存。DisLruCache目前在Android中不属于AndroidSDK的一部分,但Android官方文档推荐使用该算法实现硬盘缓存;LruCache是??一个泛型类,主要算法原理是将最近使用过的带有强引用的对象(也就是我们平时使用Reference方法的对象)存储在LinkedHashMap中。当缓存满时,将最近最少使用的对象从内存中移除,并提供get和put方法完成缓存的获取和添加;2.LruCache的使用LruCache的使用很简单,我们已经缓存图片了ExampleintmaxMemory=(int)(Runtime.getRuntime().totalMemory()/1024);intcacheSize=maxMemory/8;mMemoryCache=newLruCache(cacheSize){@OverrideprotectedintsizeOf(Stringkey,Bitmapvalue){returnvalue.getRowBytes()*value.getHeight()/1024;}};①设置LruCache缓存的大小,一般为可用容量的1/8当前进程的大小;②重写sizeOf方法,计算每张图片要缓存的大小;注意:缓存的总容量和每个缓存对象的大小要在同一个单位;2、LruCache的实现原理LruCache的核心思想很容易理解,就是维护一个缓存对象列表,对象列表的排列是按照访问顺序来实现的,即没有被访问过的对象会被放在队列的末尾,很快就会被淘汰。最近访问的对象会被放在队列的头部,最后被淘汰;1.构造函数publicLruCache(intmaxSize){if(maxSize<=0){thrownewIllegalArgumentException("maxSize<=0");}this.maxSize=maxSize;this.map=newLinkedHashMap(0,0.75f,true);}创建LruCache对象时,其内部工作如下:记录指定的最大容量maxSize;创建一个初始容量为0的LinkedHashMap用于保存对象;因此,在创建LruCache对象时,我们需要指定一个最大容量值maxSize,可以是具体的值,也可以是内存空间。对应sizeOf方法:protectedintsizeOf(Kkey,Vvalue){return1;}如果我们想从数量上控制缓存,可以看到默认的sizeOf方法返回值为1,表示每次取一块添加数据的时候,它占用的空间为1,直到添加内容的个数达到maxSize,也就是添加数据时,系统会选择使用频率最低的值,将其剔除;如果使用内存空间作为缓存限制,则需要重写size返回对象占用的空间:intmaxMemory=(int)(Runtime.getRuntime().maxMemory())/1024;//获取可用的内存,转换为单位KBintmaxCache=maxMemory/10;//缓存大小为可用内存的1/10LruCachemMemoryCache=newLruCache(maxCache){@Override//重写sizeOf方法返回占用内存通过每条数据protectedintsizeOf(Stringkey,Bitmapvalue){returnvalue.getRowBytes()*value.getHeight()/1024;//returnvalue.getByteCount()/1024;}};2.读取内容读取内容对应get方法,源码如下:publicfinalVget(Kkey){if(key==null){//ifkey如果为null则直接抛出异常thrownewNullPointerException("key==null");}VmapValue;synchronized(this){mapValue=map.get(key);//读取key对应的valueif(mapValue!=null){hitCount++;returnmapValue;//如果获取到内容,返回}missCount++;//如果没有读取到内容,missCount计数会增加,继续向下执行}/*尝试创建值可能会消耗一定的多少时间。创建完成后,地图可能已经改变。如果发现用于创建的key已经存在,导致冲突,则丢弃create*Attempttocreateavalue创建的value。这可能需要很长时间,并且map*maybedifferentwhencreate()returns.Ifaconflictingvaluewas*addedtothemapwhilecreate()wasworking,weleavethatvaluein*themapandreleasethecreatedvalue.*///如果取不到值,调用create方法尝试创建对象//create方法默认返回null,可以重写该方法,取不到值时决定添加该方法VcreatedValue=create(key);if(createdValue==null){returnnull;}//以下部分是地图创建后的修改动作,包括存储对象、调整大小等。synchronized(this){createCount++;mapValue=map.put(key,createdValue);if(mapValue!=null){//存在冲突soundothatlastputmap.put(key,mapValue);}else{size+=safeSizeOf(key,createdValue);}}if(mapValue!=null){entryRemoved(false,key,createdValue,mapValue);returnmapValue;}else{trimToSize(maxSize);returncreatedValue;}}get方法尝试根据传入的key读取内容,如果读到否,您可以选择是否创建一个对象。如果选择创建对象,我们需要重写create方法返回要创建的对象。使用起来也非常简单;3、存储内容使用put方法:publicfinalVput(Kkey,Vvalue){if(key==null||value==null){thrownewNullPointerException("key==null||value==null");}Vprevious;synchronized(this){putCount++;size+=safeSizeOf(key,value);previous=map.put(key,value);if(previous!=null){size-=safeSizeOf(key,previous);}}if(previous!=null){entryRemoved(false,key,previous,value);}trimToSize(maxSize);returnprevious;}在存储内容的时候,可以看到如何控制缓存大小:存储完内容后,会执行size+=safeSizeOf(key,value);,safeSizeOf的默认实现是sizeOf方法。每存储一个对象,其大小就会增加相应的值。如果存储的key已经有数据,则大小不变;同时提供了一个entryRemoved方法,当数据被remove时调用(remove调用remove,覆盖新值,删除缓存)。默认实现是空的;在put的最后调用了trimToSize,这是一个控制缓存大小的方法。每当存储新数据时都会调用此方法。当前大小超过缓存最大值后,该方法将删除最近最少使用的数据;除了正常的存储和读取,LruCache还提供了一次性读取所有缓存对象的方法:publicsynchronizedfinalMapsnapshot(){returnnewLinkedHashMap(map);}4.trimToSize()方法publicvoidtrimToSize(intmaxSize){//无限循环while(true){Kkey;Vvalue;synchronized(this){//如果map为空并且缓存大小不等于0或者缓存大小小于0,则异常thrownif(size<0||(map.isEmpty()&&size!=0)){thrownewIllegalStateException(getClass().getName()+".sizeOf()isreportinginconsistentresults!");}//如果缓存大小更小超过最大缓存,或者map为空,不需要删除缓存对象跳出循环if(size<=maxSize||map.isEmpty()){break;}//迭代器获取到first对象,也就是队列尾部的元素,最近最少访问的元素Map.EntrytoEvict=map.entrySet().iterator().next();key=toEvict.getKey();value=toEvict.getValue();//删除对象并更新缓存大小map.remove(key);size-=safeSizeOf(key,value);evictionCount++;}entryRemoved(true,key,value,null);}}小结LruCache维护了一个集合LinkedHashMap,按照访问的先后顺序进行排序。当调用put()方法时,元素将被添加到组合中,并调用trimToSize()来判断缓存是否已满。如果已满,将使用LinkedHashMap迭代器删除队列末尾的元素,即最近最少访问的元素。当调用get()方法访问缓存对象时,会调用LinkedHashMap的get()方法获取对应的集合元素,并将该元素更新到队列头部;