这次就是专门学习内存储存了。
从内部储存这块要学习的内容有
1. 简单了解类的继承关系
2. 关注里面的一些熟悉的语法但是陌生的用法 --嵌入到了代码部分用红色标注了
3. 关注函数的命名规律;
4. 关注里面出现的比较常用的算法
5、 接口作为参数
/*******************************************************************************
package com.nostra13.universalimageloader.cache.memory;
import android.graphics.Bitmap;
import java.util.Collection;
/**
* Interface for memory cache
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.9.2
*/
public interface MemoryCache {
/**
* Puts value into cache by key
*
* @return <b>true</b> - if value was put into cache successfully, <b>false</b> - if value was <b>not</b> put into
* cache
*/
boolean put(String key, Bitmap value);
/** Returns value by key. If there is no value for key then null will be returned. */
Bitmap get(String key);
/** Removes item by key */
Bitmap remove(String key);
/** Returns all keys of cache */
Collection<String> keys();
/** Remove all items from cache */
void clear();
}
1.继承关系
首先,最重要的就是要了解为什么MemoryCache 要有这些方法呢?
我们从缓存里面存、取数据那么就要有一个key对应去索引;
接下来,知道了key,那么怎么去得到缓存数据、放数据到缓存呢?
在BaseMemoryCache里面就有这么一个:
/** Stores not strong references to objects */
private final Map<String, Reference<Bitmap>> softMap = Collections.synchronizedMap(new HashMap<String, Reference<Bitmap>>());
Collections是集合的接口,里面提供了一个synchronizedMap的方法;这个方法的特点是函数返回的线程安全的HashMap,所以保证多线程访问数据的一致性;
比如我们如果要存数据
@Override
public boolean put(String key, Bitmap value) {
softMap.put(key, createReference(value));
return true;
}
对应的createReference是一个抽象方法,在FIFOLimitMemoryCache中的具体实现就是一个弱引用。
注意--毕竟不是所有的子类都是需要这个弱引用,所以不在基类里面去实现而是放到子类去实现比较适合
@Override
protected Reference<Bitmap> createReference(Bitmap value) {
return new WeakReference<Bitmap>(value);
}
如果要取出缓存图片
public Bitmap get(String key) {
Bitmap result = null;
Reference<Bitmap> reference = softMap.get(key);
if (reference != null) {
result = reference.get();
}
return result;
}
这里就涉及到了内存的储存方式,引用下人家的话:
【
回顾:
(1)StrongReference(强引用)
强引用就是平时经常使用的,如常规new Object()。如果一个对象具有强引用,那垃圾回收器绝不会回收。内存不足,甚至出现OOM时,也不会随意回收强引用的对象。
(2)SoftReference(软引用)
在内存空间足够,垃圾回收器不会回收它;如果内存空间不足,垃圾回收器就会回收软引用的对象。
(3)WeakReference(弱引用)
弱引用相对软引用,具有更短暂的生命周期。常规的GC,只要被扫描到,都会直接被回收。
(4)PhantomReference(虚引用)
虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。没用过。。。
选其一FIFOLimitedMemoryCache进行分析,构造时,必须要指定大小(单位:字节)。当设置的大小超过16MB(Android默认分配的大小好像也是这个)时,会有警告。
】
扩展下:
基本的存储关系搞清楚了,再简单了解下LimitMemoryCache,其他的类就不分析了,道理差不多;
这个类依然还是抽象类,还有一些类是要继承于它的,自然我们在使用的时候不会直接用这个类(抽象类不能实例化),但是了解它是必要的;
首先,如果我们要做一个限制大小的缓存类,那么首先我们要得到:要设置的缓存最大是多少,要缓存的内容是多大,
eg:在放入缓存的时候,肯定有个判断
@Override
public boolean put(String key, Bitmap value) {
boolean putSuccessfully = false;
// Try to add value to hard cache
int valueSize = getSize(value); //得到需要缓存的数据的大小
int sizeLimit = getSizeLimit(); //得到要设置的最大缓存是多少
int curCacheSize = cacheSize.get();
if (valueSize < sizeLimit) {
while (curCacheSize + valueSize > sizeLimit) {
Bitmap removedValue = removeNext();
if (hardCache.remove(removedValue)) {
curCacheSize = cacheSize.addAndGet(-getSize(removedValue));
}
}
hardCache.add(value);
cacheSize.addAndGet(valueSize);
putSuccessfully = true;
}
另外,注意的是LimitMemoryCache虽然继承的是BaseMemoryCache,但是内存所有数据结构变了, 用的是Collections.synchronizedList(new LinkedList<Bitmap>()) ,
/**
* Contains strong references to stored objects. Each next object is added last. If hard cache size will exceed
* limit then first object is deleted (but it continue exist at {@link #softMap} and can be collected by GC at any
* time)
*/
private final List<Bitmap> hardCache = Collections.synchronizedList(new LinkedList<Bitmap>());
道理和上面的synchronizedHashMap一样,Collections.synchronizedList的话参考这个:写的比较详细 http://my.oschina.net/infiniteSpace/blog/305425
所以说参考的这篇文章说BaseMemoryCache的子类都是弱引用,这个地方值得商榷~
2、命名规范问题
在类的命名上比较规范,包的命名都很好,一看就知道那个包里面是接口,哪里是实现接口的类;
有一点个人觉得不舒服就是,memory这个包里面有几个抽象类,所以我觉得要么把这几个类移到memory.impl这个包,要么在接口类的名字后面加个interface的标注;
类里面的函数的命名的规范:很符合基本的规范
静态常量名称大写
private static final int MAX_NORMAL_CACHE_SIZE_IN_MB = 16;
/** @param sizeLimit Maximum size for cache (in bytes) */
public LimitedMemoryCache(int sizeLimit) {
this.sizeLimit = sizeLimit;
cacheSize = new AtomicInteger();
if (sizeLimit > MAX_NORMAL_CACHE_SIZE) {
L.w("You set too large memory cache size (more than %1$d Mb)", MAX_NORMAL_CACHE_SIZE_IN_MB);
}
}
方法名首字母小写;
类首字母大写;
...
这些虽然都懂我,但是很多时候,在写的过程中就忘了或者懒得去标注说明;看来以后得慢慢养成刷牙般的习惯;
3、出现的算法
出现了FIFO,Fuzzy,使用频率UsingFreq,Lru算法
算法部分原理目前还很难掌握~对算法不怎么样,以后有时间再系统学下,目前能简单看得懂就行;
这个部分用一个FuzzyKeyMemoryCache简单说明下怎么利用JAVA已经做好的算法来写程序吧
<span style="color:#333333;">import android.graphics.Bitmap;
import com.nostra13.universalimageloader.cache.memory.MemoryCache;
import java.util.Collection;
import java.util.Comparator;
/**
* Decorator for {@link MemoryCache}. Provides special feature for cache: some different keys are considered as
* equals (using {@link Comparator comparator}). And when you try to put some value into cache by key so entries with
* "equals" keys will be removed from cache before.<br />
* <b>NOTE:</b> Used for internal needs. Normally you don't need to use this class.
*/
public class FuzzyKeyMemoryCache implements MemoryCache {
private final MemoryCache cache;
private final Comparator<String> keyComparator;
public FuzzyKeyMemoryCache(</span><span style="color:#ff0000;">MemoryCache </span><span style="color:#333333;">cache, </span><span style="color:#ff0000;">Comparator</span><span style="color:#333333;"><String> keyComparator) {
this.cache = cache;
this.keyComparator = keyComparator;
}
@Override
public boolean put(String key, Bitmap value) {
// Search equal key and remove this entry
synchronized (cache) {
String keyToRemove = null;
for (String cacheKey : cache.keys()) {
//-1代表o1里的某一个属性比o2的小 0代表等于 1代表大于
if (keyComparator.compare(key, cacheKey) == 0) {
keyToRemove = cacheKey;
break;
}
}
//如果判断出目前的MemoryCache里面有这个key,说明已经缓存了,所以就不用再去存了
if (keyToRemove != null) {
cache.remove(keyToRemove);
}
}
return cache.put(key, value);
}
@Override
public Bitmap get(String key) {
return cache.get(key);
}
@Override
public Bitmap remove(String key) {
return cache.remove(key);
}
@Override
public void clear() {
cache.clear();
}
@Override
public Collection<String> keys() {
return cache.keys();
}
}</span>
4、最后,总结一下一个很迷惑我的地方:接口作为参数 --可以看到MemoryCache 和Comparator<String>是一个接口作为参数传入进来的;
在实际用的时候,接口作为参数非常有用:这样便根据传进入的参数的不同而实现不同的功能;可以把自己内部的数据暴露给外部;
比如:我们最简单的按钮监听器
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("BButton", "pressed button 1");
//change button 1's text, left icon, and type
button1.setText("hello");
button1.setLeftIcon("fa-star");
button1.setBootstrapType("success");
}
});
这个View v是怎么暴露给你的呢?
写个简单的DEMO--你可以传入参数进行不同的功能;可以获得类暴露给你的数据
public interface House {
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>String window();
<span style="white-space:pre"> </span>int Bigdoor(String str);
<span style="white-space:pre"> </span>void smallDoor(String str);
}
public class TestHouse {
void test(House house){
String windowStr=house.window();
System.out.println("从外面的窗户得到的数据"+windowStr);
String str_samll="用smallDoor向外暴露数据";
house.smallDoor(str_samll);
String str_big="用bigdoor向外暴露数据";
String str_int_big=house.Bigdoor(str_big)+"";
System.out.println(str_int_big);
}
public static void main(String[] args) {
TestHouse instance=new TestHouse();
instance.test(new House() {
//只能进去
@Override
public String window() {
// TODO Auto-generated method stub
return "window";
}
//只能出去
@Override
public void smallDoor(String str) {
System.out.println(str);
}
//既能进去又能出来
@Override
public int Bigdoor(String str) {
System.out.println(str);
return 2;
}
});
}
}