【構建Android快取模組】(三)Controller & 非同步圖片載入
上節課我們學習了快取模組的實現, 快取分做兩份:Memory Cache和File Cache。方法也很簡單,分別是:
- 儲存檔案
- 按唯一key值索引檔案
- 清空快取
區別在於記憶體快取讀取優先,因為它讀寫的速度更快。但是考慮到記憶體限制,退而選用檔案儲存,分擔記憶體快取的壓力。
原理非常簡單,在第一課中已經詳細分析了。那麼要怎麼才能將這個快取模組與UI模組的顯示關聯起來呢?在這裡我們需要一個控制器,掌管資料流向和讀寫,同時控制UI的顯示。
那麼這個控制器需要以下的元素:
- 記憶體快取
- 硬碟快取
- 非同步任務處理
- 控制UI顯示
1 |
//caches |
2 |
private MemoryCache
memoryCache; |
3 |
private FileCache
fileCache; |
4 |
//Asynchronous
task |
5 |
private static AsyncImageLoader
imageLoader; |
01 |
class AsyncImageDownloader extends AsyncTask<Void,
Void, Bitmap>{ |
02 |
private ImageView
imageView; |
03 |
private String
fileName; |
04 |
|
05 |
public AsyncImageDownloader(ImageView
imageView, String fileName){ |
06 |
this .imageView
= imageView; |
07 |
this .fileName
= fileName; |
08 |
} |
09 |
|
10 |
@Override |
11 |
protected void onPreExecute()
{ |
12 |
super .onPreExecute(); |
13 |
imageView.setImageResource(R.drawable.placeholder); |
14 |
} |
15 |
|
16 |
@Override |
17 |
protected Bitmap
doInBackground(Void... arg0) { |
18 |
String
url = Utils.getRealUrlOfPicture(fileName); |
19 |
HttpResponse
response = new HttpRetriever().requestGet(url, null ); |
20 |
Log.i(TAG, "url:
" +
url); |
21 |
Log.i(TAG, "respone:
" +
response); |
22 |
InputStream
in = null ; |
23 |
try { |
24 |
if (response
!= null &&
response.getEntity() != null ) |
25 |
in
= response.getEntity().getContent(); |
26 |
} catch (IllegalStateException
e) { |
27 |
e.printStackTrace(); |
28 |
return null ; |
29 |
} catch (IOException
e) { |
30 |
e.printStackTrace(); |
31 |
return null ; |
32 |
} |
33 |
|
34 |
//TODO
to be optimized: adjust the size of bitmap |
35 |
return BitmapFactory.decodeStream(in); |
36 |
} |
37 |
|
38 |
@Override |
39 |
protected void onPostExecute(Bitmap
result) { |
40 |
super .onPostExecute(result); |
41 |
if (result
!= null &&
imageView != null ) |
42 |
imageView.setImageBitmap(result); |
43 |
|
44 |
//TODO
cache the bitmap both in sdcard & memory |
45 |
memoryCache.put(fileName,
result); //
key is a unique token, value is the bitmap |
46 |
|
47 |
fileCache.put(fileName,
result); |
48 |
} |
49 |
} |
可以看到這個類的建構函式需要兩個引數,分別是檔名和對應要顯示的ImageView,那麼在任務開始的時候,可以為該ImageView設定未下載狀態的圖片,然後下載完成後更新UI。
注:需要提醒的是,這裡的唯一key值,我使用的是檔名,因為我接收到的檔名是唯一的。猿媛們也可以根據自己的需求,設計自己的唯一key值演算法。
接下來,我們需要讀用key值索引相應的Bitmap:
01 |
public Bitmap
getBitmap(String key){ |
02 |
Bitmap
bitmap = null ; |
03 |
//1.
search memory |
04 |
bitmap
= memoryCache.get(key); |
05 |
|
06 |
//2.
search sdcard |
07 |
if (bitmap
== null ){ |
08 |
File
file = fileCache.getFile(key); |
09 |
if (file
!= null ) |
10 |
bitmap
= BitmapHelper.decodeFile(file, null ); |
11 |
} |
12 |
|
13 |
return bitmap; |
14 |
} |
讀取到Bitmap後進行顯示:
01 |
public void displayBitmap(ImageView
imageView, String fileName){ |
02 |
//no
pic for this item |
03 |
if (fileName
== null || "" .equals(fileName)) |
04 |
return ; |
05 |
|
06 |
Bitmap
bitmap = getBitmap(fileName); |
07 |
//search
in cache, if there is no such bitmap, launch downloads |
08 |
if (bitmap
!= null ){ |
09 |
imageView.setImageBitmap(bitmap); |
10 |
} |
11 |
else { |
12 |
Log.w(TAG, "Can't
find the file you required." ); |
13 |
new AsyncImageDownloader(imageView,
fileName).execute(); |
14 |
} |
15 |
} |
不過,我將它應用在一個小專案中,效能還不錯。對於小專案的需求,應該是夠的。
最後,附上使用方法,以及整個類的原始碼。
使用方法:
1 |
AsyncImageLoader
imageLoader = AsyncImageLoader.getInstance( this );、 |
2 |
imageLoader.displayBitmap(imageView,
fileName); |
原始碼:
001 |
<strong> public class AsyncImageLoader
{ |
002 |
003 |
private static final String
TAG = "AsyncImageLoader" ; |
004 |
|
005 |
//caches |
006 |
private MemoryCache
memoryCache; |
007 |
private FileCache
fileCache; |
008 |
//Asynchronous
task |
009 |
private static AsyncImageLoader
imageLoader; |
010 |
011 |
class AsyncImageDownloader extends AsyncTask<Void,
Void, Bitmap>{ |
012 |
private ImageView
imageView; |
013 |
private String
fileName; |
014 |
|
015 |
public AsyncImageDownloader(ImageView
imageView, String fileName){ |
016 |
this .imageView
= imageView; |
017 |
this .fileName
= fileName; |
018 |
} |
019 |
|
020 |
@Override |
021 |
protected void onPreExecute()
{ |
022 |
super .onPreExecute(); |
023 |
imageView.setImageResource(R.drawable.placeholder); |
024 |
} |
025 |
|
026 |
@Override |
027 |
protected Bitmap
doInBackground(Void... arg0) { |
028 |
String
url = Utils.getRealUrlOfPicture(fileName); |
029 |
HttpResponse
response = new HttpRetriever().requestGet(url, null ); |
030 |
Log.i(TAG, "url:
" +
url); |
031 |
Log.i(TAG, "respone:
" +
response); |
032 |
InputStream
in = null ; |
033 |
try { |
034 |
if (response
!= null &&
response.getEntity() != null ) |
035 |
in
= response.getEntity().getContent(); |
036 |
} catch (IllegalStateException
e) { |
037 |
e.printStackTrace(); |
038 |
return null ; |
039 |
} catch (IOException
e) { |
040 |
e.printStackTrace(); |
041 |
return null ; |
042 |
} |
043 |
|
044 |
//TODO
to be optimized: adjust the size of bitmap |
045 |
return BitmapFactory.decodeStream(in); |
046 |
} |
047 |
|
048 |
@Override |
049 |
protected void onPostExecute(Bitmap
result) { |
050 |
super .onPostExecute(result); |
051 |
if (result
!= null &&
imageView != null ) |
052 |
imageView.setImageBitmap(result); |
053 |
|
054 |
//TODO
cache the bitmap both in sdcard & memory |
055 |
memoryCache.put(fileName,
result); //
key is a unique token, value is the bitmap |
056 |
|
057 |
fileCache.put(fileName,
result); |
058 |
} |
059 |
} |
060 |
|
061 |
private AsyncImageLoader(Context
context){ |
062 |
this .memoryCache
= new MemoryCache(); |
063 |
this .fileCache
= new FileCache(context); |
064 |
} |
065 |
|
066 |
public static AsyncImageLoader
getInstance(Context context){ |
067 |
if (imageLoader
== null ) |
068 |
imageLoader
= new AsyncImageLoader(context); |
069 |
|
070 |
return imageLoader; |
071 |
} |
072 |
|
073 |
public void displayBitmap(ImageView
imageView, String fileName){ |
074 |
//no
pic for this item |
075 |
if (fileName
== null || "" .equals(fileName)) |
076 |
return ; |
077 |
|
078 |
Bitmap
bitmap = getBitmap(fileName); |
079 |
//search
in cache, if there is no such bitmap, launch downloads |
080 |
if (bitmap
!= null ){ |
081 |
imageView.setImageBitmap(bitmap); |
082 |
} |
083 |
else { |
084 |
Log.w(TAG, "Can't
find the file you required." ); |
085 |
new AsyncImageDownloader(imageView,
fileName).execute(); |
086 |
} |
087 |
} |
088 |
|
089 |
public Bitmap
getBitmap(String key){ |
090 |
Bitmap
bitmap = null ; |
091 |
//1.
search memory |
092 |
bitmap
= memoryCache.get(key); |
093 |
|
094 |
//2.
search sdcard |
095 |
if (bitmap
== null ){ |
096 |
File
file = fileCache.getFile(key); |
097 |
if (file
!= null ) |
098 |
bitmap
= BitmapHelper.decodeFile(file, null ); |
099 |
} |
100 |
|
101 |
return bitmap; |
102 |
} |
103 |
|
104 |
public void clearCache(){ |
105 |
if (memoryCache
!= null ) |
106 |
memoryCache.clear(); |
107 |
if (fileCache
!= null ) |
108 |
fileCache.clear(); |
109 |
} |
110 |
}</strong> |
原始碼:
附上原始碼,不過伺服器的原始碼暫時還沒有放出來,先看看客戶端的吧。
https://github.com/ryanhoo/SoftRead
相關文章
- Android圖片快取框架GlideAndroid快取框架IDE
- Android 圖片載入快取問題:為什麼你的Glide快取沒有起作用?Android快取IDE
- Android 圖片載入框架Android框架
- 開源框架——圖片載入和快取方案總結框架快取
- Android 高效安全載入圖片Android
- android非同步生成圖片Android非同步
- ListView 之非同步載入圖片亂序View非同步
- webpack模組非同步載入原理解析Web非同步
- Android偽圖片載入進度效果Android
- Android 基礎之圖片載入(二)Android
- Android HAL模組的載入過程Android
- Android圖片載入框架Fresco使用詳解Android框架
- Android 載入網路圖片 以及實現圓角圖片效果Android
- React Native圖片快取元件React Native快取元件
- Magix中的快取模組快取
- 要優雅!Android中這樣載入大圖片和長圖片Android
- 優雅的構建 Android 專案之磁碟快取(DiskLruCache)Android快取
- Gradle自動實現Android元件化模組構建GradleAndroid元件化
- Android9.0使用Glide載入圖片問題AndroidIDE
- Android 6種載入網路圖片的第三方詳解Android
- Android 圖片載入庫Glide知其然知其所以然之載入AndroidIDE
- 預載入與快取快取
- Android常用圖片載入庫介紹及對比Android
- 圖片懶載入
- 圖片載入事件事件
- 預載入圖片
- Flutter 圖片載入Flutter
- swiper 模組載入
- 實現圖片懶載入的三種方式
- 圖片預載入和懶載入
- 通過Gradle自動實現Android元件化模組構建GradleAndroid元件化
- Swift多執行緒之Operation:非同步載入CollectionView圖片Swift執行緒非同步View
- Swift iOS : WebView快取圖片的方法SwiftiOSWebView快取
- mybaits原始碼分析--快取模組(六)AI原始碼快取
- 前端魔法堂:手寫快取模組前端快取
- 載入本地圖片模糊,Glide載入網路圖片卻很清晰地圖IDE
- Android兩種簡單的載入GIF圖片的方法Android
- TestFlight下載App,載入圖片失效。Xcode安裝App,圖片載入正常。APPXCode
- springboot-多模組構建Spring Boot