Java资源缓存 之 LruCache
例如对网络加载图片进行缓存:
//得到应用程序被分配的最大的内存 intmaxMemory=(int)Runtime.getRuntime().maxMemory(); //取处内存的1/5用来当缓存大小 intcachSize=maxMemory/5; //实例化LruCache lruCache=newlruCache<String,Bitmap>(cachSize){ //内部方法sizeOf设置每一张图片的缓存大小 protectedintsizeOf(Stringkey,Bitmapvalue){ //在每次存入缓存时调用,告诉系统这张缓存图片有多大 //相当于为每次要缓存的资源分配大小空间 returnvalue.getByteCount(); } };
上面的代码一般放在初始化的方法里面
其实可以将LurCache类理解为Map类map有put和get方法
接下去就调用put和get方法进行需要缓存资源的存取
LurCache的add:
publicvoidputBitmapToCache(Stringurl,Bitmapbitmap){ if(getBitmapfromCache(url)==null){//判断当前缓存是否存在 lruCache.put(url,bitmap); } } LurCache的get: publicBitmapgetBitmapfromCache(Stringurl){ returnlruCache.get(url);//可将lruCache看成map }
调用上面的add和get方法就可以对资源进行缓存的,还是挺简单的,
但要注意一点LruCachelruCache=newLruCache<String,Bitmap>(cachSize)只能被new一次,不然不同对象就不同的缓存了
附上Android的Lrucache类
packageandroid.util; importjava.util.LinkedHashMap; importjava.util.Map; /** *Acachethatholdsstrongreferencestoalimitednumberofvalues.Eachtime *avalueisaccessed,itismovedtotheheadofaqueue.Whenavalueis *addedtoafullcache,thevalueattheendofthatqueueisevictedandmay *becomeeligibleforgarbagecollection. *Cache保存一个强引用来限制内容数量,每当Item被访问的时候,此Item就会移动到队列的头部。 *当cache已满的时候加入新的item时,在队列尾部的item会被回收。 *<p>Ifyourcachedvaluesholdresourcesthatneedtobeexplicitlyreleased, *override{@link#entryRemoved}. *如果你cache的某个值需要明确释放,重写entryRemoved() *<p>Ifacachemissshouldbecomputedondemandforthecorrespondingkeys, *override{@link#create}.Thissimplifiesthecallingcode,allowingitto *assumeavaluewillalwaysbereturned,evenwhenthere'sacachemiss. *如果key相对应的item丢掉啦,重写create().这简化了调用代码,即使丢失了也总会返回。 *<p>Bydefault,thecachesizeismeasuredinthenumberofentries.Override *{@link#sizeOf}tosizethecacheindifferentunits.Forexample,thiscache *islimitedto4MiBofbitmaps:默认cache大小是测量的item的数量,重写sizeof计算不同item的 *大小。 *<pre>{@code *intcacheSize=4*1024*1024;//4MiB *LruCache<String,Bitmap>bitmapCache=newLruCache<String,Bitmap>(cacheSize){ *protectedintsizeOf(Stringkey,Bitmapvalue){ *returnvalue.getByteCount(); *} *}}</pre> * *<p>Thisclassisthread-safe.Performmultiplecacheoperationsatomicallyby *synchronizingonthecache:<pre>{@code *synchronized(cache){ *if(cache.get(key)==null){ *cache.put(key,value); *} *}}</pre> * *<p>Thisclassdoesnotallownulltobeusedasakeyorvalue.Areturn *valueofnullfrom{@link#get},{@link#put}or{@link#remove}is *unambiguous:thekeywasnotinthecache. *不允许key或者value为null *当get(),put(),remove()返回值为null时,key相应的项不在cache中 */ publicclassLruCache<K,V>{ privatefinalLinkedHashMap<K,V>map; /**Sizeofthiscacheinunits.Notnecessarilythenumberofelements.*/ privateintsize;//已经存储的大小 privateintmaxSize;//规定的最大存储空间 privateintputCount;//put的次数 privateintcreateCount;//create的次数 privateintevictionCount;//回收的次数 privateinthitCount;//命中的次数 privateintmissCount;//丢失的次数 /** *@parammaxSizeforcachesthatdonotoverride{@link#sizeOf},thisis *themaximumnumberofentriesinthecache.Forallothercaches, *thisisthemaximumsumofthesizesoftheentriesinthiscache. */ publicLruCache(intmaxSize){ if(maxSize<=0){ thrownewIllegalArgumentException("maxSize<=0"); } this.maxSize=maxSize; this.map=newLinkedHashMap<K,V>(0,0.75f,true); } /** *Returnsthevaluefor{@codekey}ifitexistsinthecacheorcanbe *createdby{@code#create}.Ifavaluewasreturned,itismovedtothe *headofthequeue.Thisreturnsnullifavalueisnotcachedandcannot *becreated.通过key返回相应的item,或者创建返回相应的item。相应的item会移动到队列的头部, *如果item的value没有被cache或者不能被创建,则返回null。 */ publicfinalVget(Kkey){ if(key==null){ thrownewNullPointerException("key==null"); } VmapValue; synchronized(this){ mapValue=map.get(key); if(mapValue!=null){ hitCount++;//命中 returnmapValue; } missCount++;//丢失 } /* *Attempttocreateavalue.Thismaytakealongtime,andthemap *maybedifferentwhencreate()returns.Ifaconflictingvaluewas *addedtothemapwhilecreate()wasworking,weleavethatvaluein *themapandreleasethecreatedvalue. *如果丢失了就试图创建一个item */ VcreatedValue=create(key); if(createdValue==null){ returnnull; } synchronized(this){ createCount++;//创建++ mapValue=map.put(key,createdValue); if(mapValue!=null){ //Therewasaconflictsoundothatlastput //如果前面存在oldValue,那么撤销put() map.put(key,mapValue); }else{ size+=safeSizeOf(key,createdValue); } } if(mapValue!=null){ entryRemoved(false,key,createdValue,mapValue); returnmapValue; }else{ trimToSize(maxSize); returncreatedValue; } } /** *Caches{@codevalue}for{@codekey}.Thevalueismovedtotheheadof *thequeue. * *@returnthepreviousvaluemappedby{@codekey}. */ 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){//返回的先前的value值 size-=safeSizeOf(key,previous); } } if(previous!=null){ entryRemoved(false,key,previous,value); } trimToSize(maxSize); returnprevious; } /** *@parammaxSizethemaximumsizeofthecachebeforereturning.Maybe-1 *toevicteven0-sizedelements. *清空cache空间 */ privatevoidtrimToSize(intmaxSize){ while(true){ Kkey; Vvalue; synchronized(this){ if(size<0||(map.isEmpty()&&size!=0)){ thrownewIllegalStateException(getClass().getName() +".sizeOf()isreportinginconsistentresults!"); } if(size<=maxSize){ break; } Map.Entry<K,V>toEvict=map.eldest(); if(toEvict==null){ break; } key=toEvict.getKey(); value=toEvict.getValue(); map.remove(key); size-=safeSizeOf(key,value); evictionCount++; } entryRemoved(true,key,value,null); } } /** *Removestheentryfor{@codekey}ifitexists. *删除key相应的cache项,返回相应的value *@returnthepreviousvaluemappedby{@codekey}. */ publicfinalVremove(Kkey){ if(key==null){ thrownewNullPointerException("key==null"); } Vprevious; synchronized(this){ previous=map.remove(key); if(previous!=null){ size-=safeSizeOf(key,previous); } } if(previous!=null){ entryRemoved(false,key,previous,null); } returnprevious; } /** *Calledforentriesthathavebeenevictedorremoved.Thismethodis *invokedwhenavalueisevictedtomakespace,removedbyacallto *{@link#remove},orreplacedbyacallto{@link#put}.Thedefault *implementationdoesnothing. *当item被回收或者删掉时调用。改方法当value被回收释放存储空间时被remove调用, *或者替换item值时put调用,默认实现什么都没做。 *<p>Themethodiscalledwithoutsynchronization:otherthreadsmay *accessthecachewhilethismethodisexecuting. * *@paramevictedtrueiftheentryisbeingremovedtomakespace,false *iftheremovalwascausedbya{@link#put}or{@link#remove}. *true---为释放空间被删除;false---put或remove导致 *@paramnewValuethenewvaluefor{@codekey},ifitexists.Ifnon-null, *thisremovalwascausedbya{@link#put}.Otherwiseitwascausedby *anevictionora{@link#remove}. */ protectedvoidentryRemoved(booleanevicted,Kkey,VoldValue,VnewValue){} /** *Calledafteracachemisstocomputeavalueforthecorrespondingkey. *Returnsthecomputedvalueornullifnovaluecanbecomputed.The *defaultimplementationreturnsnull. *当某Item丢失时会调用到,返回计算的相应的value或者null *<p>Themethodiscalledwithoutsynchronization:otherthreadsmay *accessthecachewhilethismethodisexecuting. * *<p>Ifavaluefor{@codekey}existsinthecachewhenthismethod *returns,thecreatedvaluewillbereleasedwith{@link#entryRemoved} *anddiscarded.Thiscanoccurwhenmultiplethreadsrequestthesamekey *atthesametime(causingmultiplevaluestobecreated),orwhenone *threadcalls{@link#put}whileanotheriscreatingavalueforthesame *key. */ protectedVcreate(Kkey){ returnnull; } privateintsafeSizeOf(Kkey,Vvalue){ intresult=sizeOf(key,value); if(result<0){ thrownewIllegalStateException("Negativesize:"+key+"="+value); } returnresult; } /** *Returnsthesizeoftheentryfor{@codekey}and{@codevalue}in *user-definedunits.Thedefaultimplementationreturns1sothatsize *isthenumberofentriesandmaxsizeisthemaximumnumberofentries. *返回用户定义的item的大小,默认返回1代表item的数量,最大size就是最大item值 *<p>Anentry'ssizemustnotchangewhileitisinthecache. */ protectedintsizeOf(Kkey,Vvalue){ return1; } /** *Clearthecache,calling{@link#entryRemoved}oneachremovedentry. *清空cacke */ publicfinalvoidevictAll(){ trimToSize(-1);//-1willevict0-sizedelements } /** *Forcachesthatdonotoverride{@link#sizeOf},thisreturnsthenumber *ofentriesinthecache.Forallothercaches,thisreturnsthesumof *thesizesoftheentriesinthiscache. */ publicsynchronizedfinalintsize(){ returnsize; } /** *Forcachesthatdonotoverride{@link#sizeOf},thisreturnsthemaximum *numberofentriesinthecache.Forallothercaches,thisreturnsthe *maximumsumofthesizesoftheentriesinthiscache. */ publicsynchronizedfinalintmaxSize(){ returnmaxSize; } /** *Returnsthenumberoftimes{@link#get}returnedavaluethatwas *alreadypresentinthecache. */ publicsynchronizedfinalinthitCount(){ returnhitCount; } /** *Returnsthenumberoftimes{@link#get}returnednullorrequiredanew *valuetobecreated. */ publicsynchronizedfinalintmissCount(){ returnmissCount; } /** *Returnsthenumberoftimes{@link#create(Object)}returnedavalue. */ publicsynchronizedfinalintcreateCount(){ returncreateCount; } /** *Returnsthenumberoftimes{@link#put}wascalled. */ publicsynchronizedfinalintputCount(){ returnputCount; } /** *Returnsthenumberofvaluesthathavebeenevicted. *返回被回收的数量 */ publicsynchronizedfinalintevictionCount(){ returnevictionCount; } /** *Returnsacopyofthecurrentcontentsofthecache,orderedfromleast *recentlyaccessedtomostrecentlyaccessed.返回当前cache的副本,从最近最少访问到最多访问 */ publicsynchronizedfinalMap<K,V>snapshot(){ returnnewLinkedHashMap<K,V>(map); } @OverridepublicsynchronizedfinalStringtoString(){ intaccesses=hitCount+missCount; inthitPercent=accesses!=0?(100*hitCount/accesses):0; returnString.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]", maxSize,hitCount,missCount,hitPercent); } }