一. 問(wèn)題
前段時(shí)間在公司項(xiàng)目中遇到一個(gè)問(wèn)題疫鹊,在集成百度地圖時(shí)想要在地圖上動(dòng)態(tài)添加 ICON动壤,這些 ICON 都是隨時(shí)可變的逃呼,起初設(shè)計(jì)時(shí)做裙,每次添加的 ICON 都是動(dòng)態(tài) new 出一個(gè)新的Bitmap,這樣可以滿足 ICON 隨時(shí)可變的需求嗅蔬,但是需求完成之后剑按,通過(guò) AS 觀察到 APP 內(nèi)存飆升,APP 內(nèi)存吃緊帶來(lái)的壞處我就不再贅述了澜术,為了解決這個(gè)問(wèn)題艺蝴,我就設(shè)計(jì)了一個(gè)簡(jiǎn)單的內(nèi)存緩存框架來(lái)解決這個(gè)問(wèn)題,有效的減少了 APP 的內(nèi)存消耗鸟废。
二. 解決
解決問(wèn)題時(shí)的問(wèn)題思路我就不再說(shuō)了猜敢,直接看下源碼~
/**
* Created by daiyiming on 2016/12/10.
* 本地內(nèi)存緩存
*/
public final class MemoryCache<KO, VO> {
public static final int CAPACITY_DEFAULT = 10; // 默認(rèn)緩存
public static final int CAPACITY_INFINITY = -1; // 無(wú)限緩存
public static final int MAX_CAPACITY_DEFAULT = 20; // 默認(rèn)最大緩存
public static final int MAX_CAPACITY_INFINITY = -1; // 無(wú)限最大緩存
private final LinkedList<ValueHolder<KO, VO>> mCacheList; // 緩存
private final HashSet<KO> mKeySet; // 鍵緩存
private volatile int mCapacity; // 容量
private volatile int mMaxCapacity; // 最大容量
private volatile boolean mAllowUpdate; // 鍵值沖突時(shí)是否準(zhǔn)許更新
/**
* 鍵值對(duì)組合類
*
* @param <KI> 鍵類型
* @param <VI> 值類型
*/
private static final class ValueHolder<KI, VI> {
private final KI mKey; // 鍵不準(zhǔn)許修改,若修改則用添加新的對(duì)象
private VI mValue; // 值可以修改,用于更新
private ValueHolder(KI key, VI value) {
mKey = key;
mValue = value;
}
/**
* 更新值對(duì)象
* @param value 新的值對(duì)象
*/
private void update(VI value) {
recycleValue();
mValue = value;
}
/**
* 回收Holder
*/
private void recycle() {
recycleKey();
recycleValue();
}
private void recycleKey() {
if (mKey instanceof IRecycleInterface) {
((IRecycleInterface) mKey).recycle();
}
}
private void recycleValue() {
if (mValue instanceof IRecycleInterface) {
((IRecycleInterface) mValue).recycle();
}
}
}
/**
* 值接口缩擂,實(shí)現(xiàn)幫助對(duì)象鍵值內(nèi)存回收
*/
public interface IRecycleInterface {
void recycle();
}
public MemoryCache() {
this(CAPACITY_DEFAULT, MAX_CAPACITY_DEFAULT);
}
public MemoryCache(int capacity, int maxCapacity) {
if (capacity < CAPACITY_INFINITY
|| maxCapacity < MAX_CAPACITY_INFINITY) {
throw new IllegalArgumentException("MemoryCache:構(gòu)造函數(shù)參數(shù)錯(cuò)誤");
}
mCacheList = new LinkedList<>();
mKeySet = new HashSet<>();
mCapacity = capacity;
mMaxCapacity = maxCapacity;
mAllowUpdate = false;
}
public synchronized void put(KO key, VO value) {
if (key == null || value == null) {
return;
}
if (mKeySet.contains(key)) { // 如果已經(jīng)存在則復(fù)用對(duì)象
ListIterator<ValueHolder<KO, VO>> iterator = mCacheList.listIterator();
while (iterator.hasNext()) {
ValueHolder<KO, VO> holder = iterator.next();
if (holder.mKey.equals(key)) {
holder.update(value);
// 如果不準(zhǔn)許更新則刪除重新添加
if (!mAllowUpdate && iterator.previousIndex() != 0) {
iterator.remove();
mCacheList.addFirst(holder);
}
break;
}
}
} else { // 不存在則添加
mKeySet.add(key);
mCacheList.addFirst(new ValueHolder<>(key, value));
}
// 如果大于最大容量且不是無(wú)限容量則清除末尾一個(gè)
if (mMaxCapacity != MAX_CAPACITY_INFINITY
&& mCacheList.size() > mMaxCapacity) {
remove(mCacheList.size() - 1);
}
}
public synchronized VO get(KO key) {
if (mKeySet.contains(key)) {
ListIterator<ValueHolder<KO, VO>> iterator = mCacheList.listIterator();
while (iterator.hasNext()) {
ValueHolder<KO, VO> holder = iterator.next();
if (holder.mKey.equals(key)) { // 找到
if (iterator.previousIndex() != 0) { // 如果不是在頭部就移動(dòng)到頭部
iterator.remove();
mCacheList.addFirst(holder);
}
// 刪除一個(gè)超出容量的數(shù)據(jù)
if (mCapacity != CAPACITY_INFINITY
&& mCacheList.size() > mCapacity) {
remove(mCacheList.size() - 1);
}
return holder.mValue;
}
}
}
return null;
}
public synchronized boolean contains(KO k) {
return mKeySet.contains(k);
}
public synchronized void clear() {
mKeySet.clear();
for (ValueHolder<KO, VO> holder : mCacheList) {
holder.recycle();
}
mCacheList.clear();
}
public synchronized int size() {
return mCacheList.size();
}
public synchronized void remove(KO key) {
if (mKeySet.contains(key)) {
ListIterator<ValueHolder<KO, VO>> iterator = mCacheList.listIterator();
while (iterator.hasNext()) {
ValueHolder<KO, VO> holder = iterator.next();
if (holder.mKey.equals(key)) {
iterator.remove();
mKeySet.remove(holder.mKey);
holder.recycle();
break;
}
}
}
}
public synchronized void remove(int position) {
if (position >= 0 && position < size()) {
ValueHolder<KO, VO> removedHolder = mCacheList.remove(position);
mKeySet.remove(removedHolder.mKey);
removedHolder.recycle();
}
}
public void setCapacity(int capacity) {
mCapacity = capacity;
}
public void setMaxCapacity(int maxCapacity) {
mMaxCapacity = maxCapacity;
}
public int getCapacity() {
return mCapacity;
}
public int getMaxCapacity() {
return mMaxCapacity;
}
/**
* 準(zhǔn)許更新
* 決定添加過(guò)程中鍵值重復(fù)時(shí)直接原位置更新還是刪除重新添加
*
* @param allowUpdate 是否準(zhǔn)許更新
*/
public void allowUpdate(boolean allowUpdate) {
mAllowUpdate = allowUpdate;
}
}
通過(guò)一個(gè)鏈表保存需要緩存的元素鼠冕,每次添加的新元素放到鏈表的首部,添加時(shí)檢測(cè)如果超出最大容量胯盯,則從鏈表的尾部刪除一個(gè)元素懈费,為什么這么做呢?主要是為了防止瘋狂 put 元素導(dǎo)致 OOM博脑。在每次get元素的時(shí)候憎乙,通過(guò)傳入的 key 值遍歷鏈表,當(dāng)命中想要獲取的元素的時(shí)候叉趣,將這個(gè)元素移到鏈表的首部泞边,這樣可以保證頻繁獲取的元素在鏈表靠前的位置,減少獲取時(shí)間疗杉。在 get 成功的時(shí)候阵谚,如果鏈表長(zhǎng)度大于容量(注意與最大容量的區(qū)別),我們就從列表末尾刪除一個(gè)元素烟具,因?yàn)槟┪驳脑乇砻鬟@個(gè)元素很大的可能是長(zhǎng)時(shí)間沒(méi)人使用(get操作)梢什,即可視為過(guò)期元素。
為什么要設(shè)置一個(gè)容量和最大容量的區(qū)別呢净赴?因?yàn)榭紤]到可能存在瘋狂 put 的操作绳矩,即用戶一直在 put罩润,如果最大容量大于容量玖翅,就給了最先 put 的元素有被重新拿到鏈表首部的可能性,即一定程度的加強(qiáng)了這個(gè)元素被再次利用的可能性割以,在之后的 get 的操作中金度,鏈表尾部元素逐漸被清除,鏈表長(zhǎng)度逐漸回歸正常严沥。又因?yàn)樵O(shè)置了最大容量猜极,給 put 操作設(shè)置了上限,所以基本不會(huì)有 OOM消玄。
當(dāng)然防止有些時(shí)候需要緩存的時(shí)候 IO 連接跟伏,SOCKET 連接,Bitmap 等需要手動(dòng)銷毀的東西翩瓜,我還實(shí)現(xiàn)了一個(gè) Recycle 接口受扳,如果緩存的鍵或者值需要用戶自定義銷毀操作,則實(shí)現(xiàn)這個(gè)接口即可兔跌。
具體見(jiàn)下圖
三. 使用
/**
* Created by daiyiming on 2016/12/11.
* 緩存執(zhí)行類
*/
public final class MarkerMemoryCache {
private static MemoryCache<Key, Value> sLocalCache = new MemoryCache<>();
private static void confirmEnable() {
if (sLocalCache == null) {
sLocalCache = new MemoryCache<>();
}
}
public static void put(Key key, Value value) {
confirmEnable();
sLocalCache.put(key, value);
}
public static BitmapDescriptor get(Key key) {
confirmEnable();
Value value = sLocalCache.get(key);
if (value != null) {
return value.mBitmapDescriptor;
}
return null;
}
public static Key generateKey(int kind, String styleDetail, String styleId) {
return new Key(kind, styleDetail == null ? "" : styleDetail, styleId == null ? "" : styleId);
}
public static Value generateValue(BitmapDescriptor bitmapDescriptor) {
return new Value(bitmapDescriptor);
}
public static void clear() {
sLocalCache.clear();
}
public static final class Key {
private final int mKind;
private final String mStyleDetail;
private final String mStyleId;
private final int mHashCode;
private Key(int kind, String styleDetail, String styleId) {
mKind = kind;
mStyleDetail = styleDetail;
mStyleId = styleId;
mHashCode = generateHashCode();
}
private int generateHashCode() {
int result = 17;
result = 31 * result + mKind;
result = 31 * result + mStyleDetail.hashCode();
result = 31 * result + mStyleId.hashCode();
return result;
}
@Override
public boolean equals(Object object) {
if (object instanceof Key) {
if (object == this) {
return true;
}
Key key = (Key) object;
return mKind == key.mKind
&& mStyleDetail.equals(key.mStyleDetail)
&& mStyleId.equals(key.mStyleId);
}
return false;
}
@Override
public int hashCode() {
return mHashCode;
}
}
public static final class Value implements MemoryCache.IRecycleInterface {
private BitmapDescriptor mBitmapDescriptor = null;
private Value(BitmapDescriptor bitmapDescriptor) {
mBitmapDescriptor = bitmapDescriptor;
}
@Override
public void recycle() {
if (mBitmapDescriptor != null) {
Bitmap bitmap = mBitmapDescriptor.getBitmap();
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
}
}
}
}
這個(gè)實(shí)現(xiàn)是復(fù)雜實(shí)現(xiàn)勘高,??基本實(shí)現(xiàn)了所有功能,當(dāng)然?也可以簡(jiǎn)單使用~
第一次在簡(jiǎn)書寫記錄東西哈哈以后繼續(xù)~?