LruCache源碼解析

本文包含內容

  1. LruCache源碼解析 構造方法、增、刪、改、查
  2. 如何實現Lru的

1. 構造方法

public LruCache(int maxSize) {
	// maxsize不允許<0 建議使用Runtime.getRuntime().maxMemory() / 8
    if (maxSize <= 0) {
        throw new IllegalArgumentException("maxSize <= 0");
    }
    this.maxSize = maxSize;
	// 初始化LinkedHashMap 
    this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
初始化示例
public class ImageLoader {

    private LruCache<String, Bitmap> lruCache;

    public ImageLoader() {
        int maxMemory = (int)(Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;
        lruCache = new LruCache<String, Bitmap>(cacheSize) {
			// 初始化LruCache的時候要重寫sizeOf方法,告訴Cache添加的元素大小
			// 如果超出最大值就進行刪除操作。
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes();
            }
        };
    }

    public void addBitmap(String key, Bitmap bitmap) {
        if (getBitmap(key) == null) {
            lruCache.put(key, bitmap);
        }
    }

    public Bitmap getBitmap(String key) {
        return lruCache.get(key);
    }
}

2. 增

public final V put(K key, V value) {
    if (key == null || value == null) {
        throw new NullPointerException("key == null || value == null");
    }

    V previous;
    synchronized (this) {
        putCount++;
		// 計算當前總size大小 safeSizeOf() 詳見2.1
        size += safeSizeOf(key, value);
		// 執行LinkedHashMap的put操作,如果是覆蓋操作會把oldValue返回回來 , 詳見2.2
        previous = map.put(key, value);
		// 如果oldValue != null size減去oldValue的大小
        if (previous != null) {
            size -= safeSizeOf(key, previous);
        }
    }

    if (previous != null) {
		// 如果oldValue != null , 調用entryRemoved,通知調用者對要刪除的內容進行處理
		// 如果不處理忽略即可,LruCache中entryRemoved()是個空方法
		// 詳見 2.3
        entryRemoved(false, key, previous, value);
    }

	// 處理size,如果超過maxsize做刪除處理 , 詳見2.4
    trimToSize(maxSize);
    return previous;
}
2.1 safeSizeOf()
private int safeSizeOf(K key, V value) {
	// 在初始化的時候重寫sizeOf,並傳回value的大小
    int result = sizeOf(key, value);
    if (result < 0) {
        throw new IllegalStateException("Negative size: " + key + "=" + value);
    }
    return result;
}
2.2 map.put()
 @Override public V put(K key, V value) {
    if (key == null) {
        return putValueForNullKey(value);
    }

    int hash = Collections.secondaryHash(key);
    HashMapEntry<K, V>[] tab = table;
    int index = hash & (tab.length - 1);
	// 遍歷是否有相同的key的Entry , 有就覆蓋,並返回oldValue
    for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
        if (e.hash == hash && key.equals(e.key)) {
            preModify(e);
            V oldValue = e.value;
            e.value = value;
            return oldValue;
        }
    }

    // No entry for (non-null) key is present; create one
    modCount++;
	// 是否需要擴容,詳見HashMap擴容源碼分析
    if (size++ > threshold) {
        tab = doubleCapacity();
        index = hash & (tab.length - 1);
    }
	// 添加新的Entry , 詳見2.2.1
    addNewEntry(key, value, hash, index);
    return null;
}
2.2.1 addNewEntry(key, value, hash, index);
void addNewEntry(K key, V value, int hash, int index) {
	// 初始化HashMapEntry操作,詳見2.2.2
    table[index] = new HashMapEntry<K, V>(key, value, hash, table[index]);
}
2.2.2 new HashMapEntry
static class HashMapEntry<K, V> implements Entry<K, V> {
    final K key;
    V value;
    final int hash;
    HashMapEntry<K, V> next;
	
	// 把當前Entry設置爲頭部,next指向table[index]下的首個Entry
    HashMapEntry(K key, V value, int hash, HashMapEntry<K, V> next) {
        this.key = key;
        this.value = value;
        this.hash = hash;
        this.next = next;
	}
	...
}
2.3 entryRemoved(false, key, previous, value)
// evicted true:在超過maxsize清理空間時被刪除的,false:在put或者removed的時候被刪除的
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
2.4 trimToSize(maxsize)
private void trimToSize(int maxSize) {
    while (true) {
        K key;
        V value;
        synchronized (this) {
            if (size < 0 || (map.isEmpty() && size != 0)) {
                throw new IllegalStateException(getClass().getName()
                        + ".sizeOf() is reporting inconsistent results!");
            }

			// 當前size < 最大size 不作處理
            if (size <= maxSize) {
                break;
            }

            // BEGIN LAYOUTLIB CHANGE
            // get the last item in the linked list.
            // This is not efficient, the goal here is to minimize the changes
            // compared to the platform version.
            Map.Entry<K, V> toEvict = null;
			// 遍歷到最後一個entry
            for (Map.Entry<K, V> entry : map.entrySet()) {
                toEvict = entry;
            }
            // END LAYOUTLIB CHANGE

            if (toEvict == null) {
                break;
            }
		
            key = toEvict.getKey();
            value = toEvict.getValue();
			// 刪除最後一個
            map.remove(key);
            size -= safeSizeOf(key, value);
            evictionCount++;
        }
		// 控制存儲空間刪除的Entry,所以參數爲true
        entryRemoved(true, key, value, null);
    }
}

3. 刪

// 刪除流程類似於trimToSize中remove過程
public final V remove(K key) {
    if (key == null) {
        throw new NullPointerException("key == null");
    }

    V previous;
    synchronized (this) {
        previous = map.remove(key);
        if (previous != null) {
            size -= safeSizeOf(key, previous);
        }
    }

    if (previous != null) {
		// 通過remove的Entry,第一參數爲false
        entryRemoved(false, key, previous, null);
    }

    return previous;
}

4. 查

public final V get(K key) {
    if (key == null) {
        throw new NullPointerException("key == null");
    }

    V mapValue;
    synchronized (this) {
		// 如果有緩存,取出緩存直接返回,結束
        mapValue = map.get(key);
        if (mapValue != null) {
            hitCount++;
            return mapValue;
        }
        missCount++;
    }

	// 如果沒有緩存,如果用戶在初始化Lrucache重寫了create(key),方法並返回內容,擇把用戶返回內容put緩存起來
    V createdValue = create(key);
	// 如果沒有重寫create,或者重寫返回null,結束
    if (createdValue == null) {
        return null;
    }

    synchronized (this) {
        createCount++;
		// 把create()中生成的Value緩存起來,並返回該位置的oldValue
        mapValue = map.put(key, createdValue);
		// 如果有該緩存,就撤銷這次put過程,把原來的mapValue重新put進去
        if (mapValue != null) {
            // There was a conflict so undo that last put
            map.put(key, mapValue);
        } else {
			// 如果原有位置是空的,那麼進行size++操作
            size += safeSizeOf(key, createdValue);
        }
    }

    if (mapValue != null) {
		// 如果原有位置不是空 , 把原有內容返回
        entryRemoved(false, key, createdValue, mapValue);
        return mapValue;
    } else {
		// 如果原有位置是空,重新計算大小
        trimToSize(maxSize);
        return createdValue;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章