-
LruMemoryCache(这个类就是这个开源框架默认的内存缓存类,缓存的是bitmap的强引用,下面我会从源码上面分析这个类)
-
UsingFreqLimitedMemoryCache(如果缓存的图片总量超过限定值,先删除使用频率最小的bitmap)
-
LRULimitedMemoryCache(这个也是使用的lru算法,和LruMemoryCache不同的是,他缓存的是bitmap的弱引用)
-
FIFOLimitedMemoryCache(先进先出的缓存策略,当超过设定值,先删除最先加入缓存的bitmap)
-
LargestLimitedMemoryCache(当超过缓存限定值,先删除最大的bitmap对象)
-
LimitedAgeMemoryCache(当 bitmap加入缓存中的时间超过我们设定的值,将其删除)
-
WeakMemoryCache(这个类缓存bitmap的总大小没有限制,唯一不足的地方就是不稳定,缓存的图片容易被回收掉)
1
2
3
|
ImageLoaderConfiguration configuration =
new
ImageLoaderConfiguration.Builder(
this
)
.memoryCache(
new
WeakMemoryCache())
.build();
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
package
com.nostra13.universalimageloader.cache.memory.impl;
import
android.graphics.Bitmap;
import
com.nostra13.universalimageloader.cache.memory.MemoryCacheAware;
import
java.util.Collection;
import
java.util.HashSet;
import
java.util.LinkedHashMap;
import
java.util.Map;
/**
* A cache that holds strong references to a limited number of Bitmaps. Each time a Bitmap is accessed, it is moved to
* the head of a queue. When a Bitmap is added to a full cache, the Bitmap at the end of that queue is evicted and may
* become eligible for garbage collection.<br />
* <br />
* <b>NOTE:</b> This cache uses only strong references for stored Bitmaps.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.8.1
*/
public
class
LruMemoryCache
implements
MemoryCacheAware<String, Bitmap> {
private
final
LinkedHashMap<String, Bitmap> map;
private
final
int
maxSize;
/** Size of this cache in bytes */
private
int
size;
/** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */
public
LruMemoryCache(
int
maxSize) {
if
(maxSize <=
0
) {
throw
new
IllegalArgumentException(
"maxSize <= 0"
);
}
this
.maxSize = maxSize;
this
.map =
new
LinkedHashMap<String, Bitmap>(
0
,
0
.75f,
true
);
}
/**
* Returns the Bitmap for {@code key} if it exists in the cache. If a Bitmap was returned, it is moved to the head
* of the queue. This returns null if a Bitmap is not cached.
*/
@Override
public
final
Bitmap get(String key) {
if
(key ==
null
) {
throw
new
NullPointerException(
"key == null"
);
}
synchronized
(
this
) {
return
map.get(key);
}
}
/** Caches {@code Bitmap} for {@code key}. The Bitmap is moved to the head of the queue. */
@Override
public
final
boolean
put(String key, Bitmap value) {
if
(key ==
null
|| value ==
null
) {
throw
new
NullPointerException(
"key == null || value == null"
);
}
synchronized
(
this
) {
size += sizeOf(key, value);
Bitmap previous = map.put(key, value);
if
(previous !=
null
) {
size -= sizeOf(key, previous);
}
}
trimToSize(maxSize);
return
true
;
}
/**
* Remove the eldest entries until the total of remaining entries is at or below the requested size.
*
* @param maxSize the maximum size of the cache before returning. May be -1 to evict even 0-sized elements.
*/
private
void
trimToSize(
int
maxSize) {
while
(
true
) {
String key;
Bitmap value;
synchronized
(
this
) {
if
(size <
0
|| (map.isEmpty() && size !=
0
)) {
throw
new
IllegalStateException(getClass().getName() +
".sizeOf() is reporting inconsistent results!"
);
}
if
(size <= maxSize || map.isEmpty()) {
break
;
}
Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
if
(toEvict ==
null
) {
break
;
}
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= sizeOf(key, value);
}
}
}
/** Removes the entry for {@code key} if it exists. */
@Override
public
final
void
remove(String key) {
if
(key ==
null
) {
throw
new
NullPointerException(
"key == null"
);
}
synchronized
(
this
) {
Bitmap previous = map.remove(key);
if
(previous !=
null
) {
size -= sizeOf(key, previous);
}
}
}
@Override
public
Collection<String> keys() {
synchronized
(
this
) {
return
new
HashSet<String>(map.keySet());
}
}
@Override
public
void
clear() {
trimToSize(-
1
);
// -1 will evict 0-sized elements
}
/**
* Returns the size {@code Bitmap} in bytes.
* <p/>
* An entry's size must not change while it is in the cache.
*/
private
int
sizeOf(String key, Bitmap value) {
return
value.getRowBytes() * value.getHeight();
}
@Override
public
synchronized
final
String toString() {
return
String.format(
"LruCache[maxSize=%d]"
, maxSize);
}
}
|
-
FileCountLimitedDiscCache(可以设定缓存图片的个数,当超过设定值,删除掉最先加入到硬盘的文件)
-
LimitedAgeDiscCache(设定文件存活的最长时间,当超过这个值,就删除该文件)
-
TotalSizeLimitedDiscCache(设定缓存bitmap的最大值,当超过这个值,删除最先加入到硬盘的文件)
-
UnlimitedDiscCache(这个缓存类没有任何的限制)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
/*******************************************************************************
* Copyright 2011-2013 Sergey Tarasevich
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package
com.nostra13.universalimageloader.cache.disc.impl;
import
com.nostra13.universalimageloader.cache.disc.LimitedDiscCache;
import
com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
import
com.nostra13.universalimageloader.core.DefaultConfigurationFactory;
import
com.nostra13.universalimageloader.utils.L;
import
java.io.File;
/**
* Disc cache limited by total cache size. If cache size exceeds specified limit then file with the most oldest last
* usage date will be deleted.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @see LimitedDiscCache
* @since 1.0.0
*/
public
class
TotalSizeLimitedDiscCache
extends
LimitedDiscCache {
private
static
final
int
MIN_NORMAL_CACHE_SIZE_IN_MB =
2
;
private
static
final
int
MIN_NORMAL_CACHE_SIZE = MIN_NORMAL_CACHE_SIZE_IN_MB *
1024
*
1024
;
/**
* @param cacheDir Directory for file caching. <b>Important:</b> Specify separate folder for cached files. It's
* needed for right cache limit work.
* @param maxCacheSize Maximum cache directory size (in bytes). If cache size exceeds this limit then file with the
* most oldest last usage date will be deleted.
*/
public
TotalSizeLimitedDiscCache(File cacheDir,
int
maxCacheSize) {
this
(cacheDir, DefaultConfigurationFactory.createFileNameGenerator(), maxCacheSize);
}
/**
* @param cacheDir Directory for file caching. <b>Important:</b> Specify separate folder for cached files. It's
* needed for right cache limit work.
* @param fileNameGenerator Name generator for cached files
* @param maxCacheSize Maximum cache directory size (in bytes). If cache size exceeds this limit then file with the
* most oldest last usage date will be deleted.
*/
public
TotalSizeLimitedDiscCache(File cacheDir, FileNameGenerator fileNameGenerator,
int
maxCacheSize) {
super
(cacheDir, fileNameGenerator, maxCacheSize);
if
(maxCacheSize < MIN_NORMAL_CACHE_SIZE) {
L.w(
"You set too small disc cache size (less than %1$d Mb)"
, MIN_NORMAL_CACHE_SIZE_IN_MB);
}
}
@Override
protected
int
getSize(File file) {
return
(
int
) file.length();
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
/*******************************************************************************
* Copyright 2011-2013 Sergey Tarasevich
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package
com.nostra13.universalimageloader.cache.disc;
import
com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
import
com.nostra13.universalimageloader.core.DefaultConfigurationFactory;
import
java.io.File;
import
java.util.Collections;
import
java.util.HashMap;
import
java.util.Map;
import
java.util.Map.Entry;
import
java.util.Set;
import
java.util.concurrent.atomic.AtomicInteger;
/**
* Abstract disc cache limited by some parameter. If cache exceeds specified limit then file with the most oldest last
* usage date will be deleted.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @see BaseDiscCache
* @see FileNameGenerator
* @since 1.0.0
*/
public
abstract
class
LimitedDiscCache
extends
BaseDiscCache {
private
static
final
int
INVALID_SIZE = -
1
;
//记录缓存文件的大小
private
final
AtomicInteger cacheSize;
//缓存文件的最大值
private
final
int
sizeLimit;
private
final
Map<File, Long> lastUsageDates = Collections.synchronizedMap(
new
HashMap<File, Long>());
/**
* @param cacheDir Directory for file caching. <b>Important:</b> Specify separate folder for cached files. It's
* needed for right cache limit work.
* @param sizeLimit Cache limit value. If cache exceeds this limit then file with the most oldest last usage date
* will be deleted.
*/
public
LimitedDiscCache(File cacheDir,
int
sizeLimit) {
this
(cacheDir, DefaultConfigurationFactory.createFileNameGenerator(), sizeLimit);
}
/**
* @param cacheDir Directory for file caching. <b>Important:</b> Specify separate folder for cached files. It's
* needed for right cache limit work.
* @param fileNameGenerator Name generator for cached files
* @param sizeLimit Cache limit value. If cache exceeds this limit then file with the most oldest last usage date
* will be deleted.
*/
public
LimitedDiscCache(File cacheDir, FileNameGenerator fileNameGenerator,
int
sizeLimit) {
super
(cacheDir, fileNameGenerator);
this
.sizeLimit = sizeLimit;
cacheSize =
new
AtomicInteger();
calculateCacheSizeAndFillUsageMap();
}
/**
* 另开线程计算cacheDir里面文件的大小,并将文件和最后修改的毫秒数加入到Map中
*/
private
void
calculateCacheSizeAndFillUsageMap() {
new
Thread(
new
Runnable() {
@Override
public
void
run() {
int
size =
0
;
File[] cachedFiles = cacheDir.listFiles();
if
(cachedFiles !=
null
) {
// rarely but it can happen, don't know why
for
(File cachedFile : cachedFiles) {
//getSize()是一个抽象方法,子类自行实现getSize()的逻辑
size += getSize(cachedFile);
//将文件的最后修改时间加入到map中
lastUsageDates.put(cachedFile, cachedFile.lastModified());
}
cacheSize.set(size);
}
}
}).start();
}
/**
* 将文件添加到Map中,并计算缓存文件的大小是否超过了我们设置的最大缓存数
* 超过了就删除最先加入的那个文件
*/
@Override
public
void
put(String key, File file) {
//要加入文件的大小
int
valueSize = getSize(file);
//获取当前缓存文件大小总数
int
curCacheSize = cacheSize.get();
//判断是否超过设定的最大缓存值
while
(curCacheSize + valueSize > sizeLimit) {
int
freedSize = removeNext();
if
(freedSize == INVALID_SIZE)
break
;
// cache is empty (have nothing to delete)
curCacheSize = cacheSize.addAndGet(-freedSize);
}
cacheSize.addAndGet(valueSize);
Long currentTime = System.currentTimeMillis();
file.setLastModified(currentTime);
lastUsageDates.put(file, currentTime);
}
/**
* 根据key生成文件
*/
@Override
public
File get(String key) {
File file =
super
.get(key);
Long currentTime = System.currentTimeMillis();
file.setLastModified(currentTime);
lastUsageDates.put(file, currentTime);
return
file;
}
/**
* 硬盘缓存的清理
*/
@Override
public
void
clear() {
lastUsageDates.clear();
cacheSize.set(
0
);
super
.clear();
}
/**
* 获取最早加入的缓存文件,并将其删除
*/
private
int
removeNext() {
if
(lastUsageDates.isEmpty()) {
return
INVALID_SIZE;
}
Long oldestUsage =
null
;
File mostLongUsedFile =
null
;
Set<Entry<File, Long>> entries = lastUsageDates.entrySet();
synchronized
(lastUsageDates) {
for
(Entry<File, Long> entry : entries) {
if
(mostLongUsedFile ==
null
) {
mostLongUsedFile = entry.getKey();
oldestUsage = entry.getValue();
}
else
{
Long lastValueUsage = entry.getValue();
if
(lastValueUsage < oldestUsage) {
oldestUsage = lastValueUsage;
mostLongUsedFile = entry.getKey();
}
}
}
}
int
fileSize =
0
;
if
(mostLongUsedFile !=
null
) {
if
(mostLongUsedFile.exists()) {
fileSize = getSize(mostLongUsedFile);
if
(mostLongUsedFile.delete()) {
lastUsageDates.remove(mostLongUsedFile);
}
}
else
{
lastUsageDates.remove(mostLongUsedFile);
}
}
return
fileSize;
}
/**
* 抽象方法,获取文件大小
* @param file
* @return
*/
protected
abstract
int
getSize(File file);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* Creates default implementation of {@link DiskCache} depends on incoming parameters
*/
public
static
DiskCache createDiskCache(Context context, FileNameGenerator diskCacheFileNameGenerator,
long
diskCacheSize,
int
diskCacheFileCount) {
File reserveCacheDir = createReserveDiskCacheDir(context);
if
(diskCacheSize >
0
|| diskCacheFileCount >
0
) {
File individualCacheDir = StorageUtils.getIndividualCacheDirectory(context);
LruDiscCache diskCache =
new
LruDiscCache(individualCacheDir, diskCacheFileNameGenerator, diskCacheSize,
diskCacheFileCount);
diskCache.setReserveCacheDir(reserveCacheDir);
return
diskCache;
}
else
{
File cacheDir = StorageUtils.getCacheDirectory(context);
return
new
UnlimitedDiscCache(cacheDir, reserveCacheDir, diskCacheFileNameGenerator);
}
}
|