如果圖片資源是靜態的,當我們要在View上顯示圖片時,只需要簡單的將圖片賦值給ImageView就可以了,但如果需要浏覽網絡上的圖片時該如何做呢?有可能圖片很大,有可能網速很慢並且不穩定,這種情況下該如何增加用戶體驗。Android官方的BitmapFun示例程序已經給了我們很好的解決方法 - 其實萬變不離其中,還是采用了提升性能的兩種常用方法:異步和緩存。
不多說我們先來看BitmapFun的主要類圖:
ImageWorker:這個是加載圖片的核心類,建議大家看源代碼從這個類看起。它的主要功能是從內存/磁盤緩存中加載圖片,或者是從網絡上下載。這裡第一要使用緩存,第二從網絡上下載,必然要使用異步線程,所以這裡從類圖中大家也可以看到它有兩個關聯類BitmapWorkerTask(繼承自AsynTask)和ImageCache, 分別用來處理異步和緩存。
ImageWorker提供給外部的主要接口是loadImage方法 - 加載圖片,如果內存中有,直接加載。否則使用異步線程(BitmapWorkerTask)後台加載 - 從磁盤或者是網絡上下載
public void loadImage(Object data, ImageView imageView) {}
BitmapWorkerTask: 異步處理圖片 - 下載並綁定圖片
ImageCache: 圖片的緩存處理,這裡使用了二級緩存: 內存和磁盤。這裡從類圖也可以看到它有一個關聯類DiskLruCache。
DiskLruCache: 關於這個類網上有一些詳盡的解釋文章。在看這個類時切忌一開始就看代碼,一定要先看類的說明,主要是journal file的格式,否則你就很難明白它的一些代碼為什麼那麼寫。這裡把幾個重點提一下,相信大家再看代碼會容易的多
1 前5行是固定(最開始是固定的5行,值是可變)
2 一條記錄用類Entry來描述,一條記錄就是一個Entry實例, 比如 CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054 在程序中就是一個Entry實例
3 Editor是Entry的操作器,用來讀寫數據
* libcore.io.DiskLruCache
* 1
* 100
* 2
*
* CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
* DIRTY 335c4c6028171cfddfbaae1a9c313c52
* CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
* REMOVE 335c4c6028171cfddfbaae1a9c313c52
* DIRTY 1ab96a171faeeee38496d8b330771a7a
* CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
* READ 335c4c6028171cfddfbaae1a9c313c52
* READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
*
寫到這裡先停一下,我們再次說明ImageWorker的功能,從內存直接加載或異步加載(從磁盤緩存或網絡下載)圖片。緩存的實現-ImageCache, 異步加載的實現 - BitmapWorkerTask。是不是比較清晰了。
接下來繼續往下看:
ImageResizer:繼承自ImageWorker,可能有的童鞋會問ImageWorker不是已經實現異步和緩存了嗎,這個類是干嘛的呢?這個主要是根據給定的大小對Image做調整。比如當圖片太大時,不能簡單的加載到內存,需要做大小調整處理。
這裡對它的幾個主要接口說明一下:
setImageSize:設置圖片要調整的大小
calculateInSampleSize: 計算縮放比例 - 根據原圖大小和要調整後的大小計算
decodeSampledBitmapFrom***:得到調整大小後的圖片,,這裡好幾個方法,數據源不一樣而已,沒啥大區別。
ImageFetcher:繼承自ImageResizer。從網絡下載圖片。這裡要澄清一點,processBitmap在ImageWorker中是一個抽象方法,並沒有實現體,在本示例中,是在ImageFetcher中實現的。之所以這樣設計,是因為圖片的來源是不確定和可變的,有可能從網絡下載,有可能從本地數據庫獲取。
protected abstract Bitmap processBitmap(Object data);
最後就是在View這一層如何來使用了,從類圖中可以看出,View這一層基本上操作ImageFetcher就可以了。以IamgeGridFragment為例:
1 在onCreate中實例化ImageFetcher,並且添加緩存處理實例
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The ImageFetcher takes care of loading images into our ImageView children asynchronously
mImageFetcher = new ImageFetcher(getActivity(), mImageThumbSize);
mImageFetcher.setLoadingImage(R.drawable.empty_photo);
mImageFetcher.addImageCache(getActivity().getSupportFragmentManager(), cacheParams);
}
2 onCreateView中異步加載圖片
@Override
public View onCreateView(
mGridView.setAdapter(mAdapter);
}
ImageAdapter.getView - 調用ImageFetcher.loadImage加載圖片
@Override
public View getView(int position, View convertView, ViewGroup container) {
mImageFetcher.loadImage(Images.imageThumbUrls[position - mNumColumns], imageView);
return imageView;
}
3 在destroty時候關閉緩存
@Override
public void onDestroy() {
super.onDestroy();
mImageFetcher.closeCache();