Android技術積累:圖片快取管理
如果每次載入同一張圖片都要從網路獲取,那代價實在太大了。所以同一張圖片只要從網路獲取一次就夠了,然後在本地快取起來,之後載入同一張圖片時就從快取中載入就可以了。從記憶體快取讀取圖片是最快的,但是因為記憶體容量有限,所以最好再加上檔案快取。檔案快取空間也不是無限大的,容量越大讀取效率越低,因此可以設定一個限定大小比如10M,或者限定儲存時間比如一天。
因此,載入圖片的流程應該是:
1、先從記憶體快取中獲取,取到則返回,取不到則進行下一步;
2、從檔案快取中獲取,取到則返回並更新到記憶體快取,取不到則進行下一步;
3、從網路下載圖片,並更新到記憶體快取和檔案快取。
接下來看記憶體快取類:ImageMemoryCache
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
|
public
class ImageMemoryCache { /**
* 從記憶體讀取資料速度是最快的,為了更大限度使用記憶體,這裡使用了兩層快取。
* 硬引用快取不會輕易被回收,用來儲存常用資料,不常用的轉入軟引用快取。
*/ private
static final
int SOFT_CACHE_SIZE = 15 ;
//軟引用快取容量 private
static LruCache<String, Bitmap> mLruCache;
//硬引用快取 private
static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache;
//軟引用快取 public
ImageMemoryCache(Context context) { int
memClass = ((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
int
cacheSize = 1024
* 1024 * memClass /
4 ; //硬引用快取容量,為系統可用記憶體的1/4
mLruCache =
new LruCache<String, Bitmap>(cacheSize) {
@Override protected
int sizeOf(String key, Bitmap value) {
if
(value != null )
return
value.getRowBytes() * value.getHeight(); else return
0 ; }
@Override protected
void entryRemoved( boolean
evicted, String key, Bitmap oldValue, Bitmap newValue) {
if
(oldValue != null )
// 硬引用快取容量滿的時候,會根據LRU演算法把最近沒有被使用的圖片轉入此軟引用快取
mSoftCache.put(key,
new SoftReference<Bitmap>(oldValue));
}
};
mSoftCache =
new LinkedHashMap<String, SoftReference<Bitmap>>(SOFT_CACHE_SIZE,
0 .75f, true ) {
private
static final
long serialVersionUID = 6040103833179403725L;
@Override protected
boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {
if
(size() > SOFT_CACHE_SIZE){ return
true ; }
return
false ; }
};
}
/**
* 從快取中獲取圖片
*/ public
Bitmap getBitmapFromCache(String url) { Bitmap bitmap;
//先從硬引用快取中獲取
synchronized
(mLruCache) { bitmap = mLruCache.get(url);
if
(bitmap != null ) {
//如果找到的話,把元素移到LinkedHashMap的最前面,從而保證在LRU演算法中是最後被刪除
mLruCache.remove(url);
mLruCache.put(url, bitmap);
return
bitmap; }
}
//如果硬引用快取中找不到,到軟引用快取中找
synchronized
(mSoftCache) { SoftReference<Bitmap> bitmapReference = mSoftCache.get(url);
if
(bitmapReference != null ) {
bitmap = bitmapReference.get();
if
(bitmap != null ) {
//將圖片移回硬快取
mLruCache.put(url, bitmap);
mSoftCache.remove(url);
return
bitmap; }
else { mSoftCache.remove(url);
}
}
}
return
null ; }
/**
* 新增圖片到快取
*/ public
void addBitmapToCache(String url, Bitmap bitmap) {
if
(bitmap != null ) {
synchronized
(mLruCache) { mLruCache.put(url, bitmap);
}
}
}
public
void clearCache() {
mSoftCache.clear();
}
} |
檔案快取類:ImageFileCache
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
|
public
class ImageFileCache { private
static final
String CACHDIR = "ImgCach" ;
private
static final
String WHOLESALE_CONV = ".cach" ;
private
static final
int MB = 1024 * 1024 ;
private
static final
int CACHE_SIZE = 10 ;
private
static final
int FREE_SD_SPACE_NEEDED_TO_CACHE = 10 ;
public
ImageFileCache() { //清理檔案快取
removeCache(getDirectory());
}
/** 從快取中獲取圖片 **/ public
Bitmap getImage( final
String url) { final
String path = getDirectory() + "/"
+ convertUrlToFileName(url); File file =
new File(path);
if
(file.exists()) { Bitmap bmp = BitmapFactory.decodeFile(path);
if
(bmp == null ) {
file.delete();
}
else { updateFileTime(path);
return
bmp; }
}
return
null ; }
/** 將圖片存入檔案快取 **/ public
void saveBitmap(Bitmap bm, String url) {
if
(bm == null ) {
return ;
}
//判斷sdcard上的空間
if
(FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
//SD空間不足
return ;
}
String filename = convertUrlToFileName(url);
String dir = getDirectory();
File dirFile =
new File(dir);
if
(!dirFile.exists()) dirFile.mkdirs();
File file =
new File(dir + "/"
+ filename); try
{ file.createNewFile();
OutputStream outStream =
new FileOutputStream(file);
bm.compress(Bitmap.CompressFormat.JPEG,
100 , outStream);
outStream.flush();
outStream.close();
}
catch (FileNotFoundException e) {
Log.w( "ImageFileCache" ,
"FileNotFoundException" );
}
catch (IOException e) {
Log.w( "ImageFileCache" ,
"IOException" );
}
}
/**
* 計算儲存目錄下的檔案大小,
* 當檔案總大小大於規定的CACHE_SIZE或者sdcard剩餘空間小於FREE_SD_SPACE_NEEDED_TO_CACHE的規定
* 那麼刪除40%最近沒有被使用的檔案
*/ private
boolean removeCache(String dirPath) {
File dir =
new File(dirPath);
File[] files = dir.listFiles();
if
(files == null ) {
return
true ; }
if
(!android.os.Environment.getExternalStorageState().equals(
android.os.Environment.MEDIA_MOUNTED)) {
return
false ; }
int
dirSize = 0 ;
for
( int
i = 0 ; i < files.length; i++) {
if
(files[i].getName().contains(WHOLESALE_CONV)) { dirSize += files[i].length();
}
}
if
(dirSize > CACHE_SIZE * MB || FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
int
removeFactor = ( int ) (( 0.4
* files.length) + 1 );
Arrays.sort(files,
new FileLastModifSort());
for
( int
i = 0 ; i < removeFactor; i++) {
if
(files[i].getName().contains(WHOLESALE_CONV)) { files[i].delete();
}
}
}
if
(freeSpaceOnSd() <= CACHE_SIZE) { return
false ; }
return
true ; }
/** 修改檔案的最後修改時間 **/ public
void updateFileTime(String path) {
File file =
new File(path);
long
newModifiedTime = System.currentTimeMillis(); file.setLastModified(newModifiedTime);
}
/** 計算sdcard上的剩餘空間 **/ private
int freeSpaceOnSd() {
StatFs stat =
new StatFs(Environment.getExternalStorageDirectory().getPath());
double
sdFreeMB = (( double )stat.getAvailableBlocks() * ( double ) stat.getBlockSize()) / MB;
return
( int ) sdFreeMB;
}
/** 將url轉成檔名 **/ private
String convertUrlToFileName(String url) { String[] strs = url.split( "/" );
return
strs[strs.length - 1 ] + WHOLESALE_CONV;
}
/** 獲得快取目錄 **/ private
String getDirectory() { String dir = getSDPath() +
"/" + CACHDIR;
return
dir; }
/** 取SD卡路徑 **/ private
String getSDPath() { File sdDir =
null ; boolean
sdCardExist = Environment.getExternalStorageState().equals(
android.os.Environment.MEDIA_MOUNTED);
//判斷sd卡是否存在 if
(sdCardExist) { sdDir = Environment.getExternalStorageDirectory();
//獲取根目錄 }
if
(sdDir != null ) {
return
sdDir.toString(); }
else { return
"" ; }
}
/**
* 根據檔案的最後修改時間進行排序
*/ private
class FileLastModifSort
implements Comparator<File> {
public
int compare(File arg0, File arg1) {
if
(arg0.lastModified() > arg1.lastModified()) { return
1 ; }
else if
(arg0.lastModified() == arg1.lastModified()) { return
0 ; }
else { return
- 1 ;
}
}
}
} |
從網路獲取圖片:
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
|
public
class ImageGetFormHttp { private
static final
String LOG_TAG = "ImageGetForHttp" ;
public
static Bitmap downloadBitmap(String url) {
final
HttpClient client = new
DefaultHttpClient(); final
HttpGet getRequest = new
HttpGet(url); try
{ HttpResponse response = client.execute(getRequest);
final
int statusCode = response.getStatusLine().getStatusCode();
if
(statusCode != HttpStatus.SC_OK) { Log.w( "ImageDownloader" ,
"Error " + statusCode +
" while retrieving bitmap from "
+ url); return
null ; }
final
HttpEntity entity = response.getEntity(); if
(entity != null ) {
InputStream inputStream =
null ; try
{ inputStream = entity.getContent();
FilterInputStream fit =
new FlushedInputStream(inputStream);
return
BitmapFactory.decodeStream(fit); }
finally {
if
(inputStream != null ) {
inputStream.close();
inputStream =
null ; }
entity.consumeContent();
}
}
}
catch (IOException e) {
getRequest.abort();
Log.w(LOG_TAG,
"I/O error while retrieving bitmap from "
+ url, e); }
catch (IllegalStateException e) {
getRequest.abort();
Log.w(LOG_TAG,
"Incorrect URL: "
+ url); }
catch (Exception e) {
getRequest.abort();
Log.w(LOG_TAG,
"Error while retrieving bitmap from "
+ url, e); }
finally {
client.getConnectionManager().shutdown();
}
return
null ; }
/*
* An InputStream that skips the exact number of bytes provided, unless it reaches EOF.
*/ static
class FlushedInputStream
extends FilterInputStream {
public
FlushedInputStream(InputStream inputStream) { super (inputStream);
}
@Override public
long skip( long
n) throws
IOException { long
totalBytesSkipped = 0L; while
(totalBytesSkipped < n) { long
bytesSkipped = in.skip(n - totalBytesSkipped); if
(bytesSkipped == 0L) { int
b = read(); if
(b < 0 ) {
break ;
// we reached EOF }
else { bytesSkipped =
1 ; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return
totalBytesSkipped; }
}
} |
最後,獲取一張圖片的流程就如下程式碼所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/*** 獲得一張圖片,從三個地方獲取,首先是記憶體快取,然後是檔案快取,最後從網路獲取 ***/ public
Bitmap getBitmap(String url) { // 從記憶體快取中獲取圖片
Bitmap result;
result = memoryCache.getBitmapFromCache(url);
if
(result == null ) {
// 檔案快取中獲取
result = fileCache.getImage(url);
if
(result == null ) {
// 從網路獲取
result = ImageGetFormHttp.downloadBitmap(url);
if
(result != null ) {
fileCache.saveBitmap(result, url);
memoryCache.addBitmapToCache(url, result);
}
}
else { // 新增到記憶體快取
memoryCache.addBitmapToCache(url, result);
}
}
return
result; } |
相關文章
- 快取框架積累快取框架
- 日常技術積累
- Android 圖片快取處理Android快取
- Android圖片快取框架GlideAndroid快取框架IDE
- 快取圖片快取
- 圖片快取快取
- Android ImageLoader 框架之圖片快取Android框架快取
- Android使用LruCache、DiskLruCache實現圖片快取+圖片瀑布流Android快取
- Android 之 遠端圖片獲取和本地快取Android快取
- 圖片三級快取及OOM--android快取OOMAndroid
- 快取技術快取
- 熱點部落格,技術歷程和技術積累 (個人)
- 工作中如何做好技術積累
- Android中圖片的三層快取詳解Android快取
- android ListView非同步載入圖片(雙快取)AndroidView非同步快取
- 【知識積累】BufferedImage類實現圖片的切分
- SDWebImage實現圖片展示、快取、清除快取Web快取
- 備忘錄:hadoop技術一點積累Hadoop
- 技術積累——C++ 呼叫 python 專案C++Python
- Android應用開發之(利用好圖片快取)Android快取
- Python快取技術Python快取
- 位元組快取技術快取
- 快取技術淺談快取
- ASP快取技術 (轉)快取
- React Native圖片快取元件React Native快取元件
- 圖片快取(源於SDK文件)快取
- 前端常用的快取技術前端快取
- 快取技術方案改造思考快取
- 【同行說技術】Android圖片處理技術資料彙總(一)Android
- Swift iOS : WebView快取圖片的方法SwiftiOSWebView快取
- 我的圖片四級快取框架快取框架
- Redis 快取雪崩,快取擊穿和快取穿透技術方案總結Redis快取穿透
- Android圖片快取之Bitmap詳解Android快取
- Android 圖片載入快取問題:為什麼你的Glide快取沒有起作用?Android快取IDE
- 【技術積累】軟體設計模式【第一版】設計模式
- 8月21日至8月27日技術積累
- 【技術積累】如何處理Feign的超時問題
- Android平滑圖片載入和快取庫 Glide 使用詳解Android快取IDE