1.Bitmap的高效加載html
a.Bitmap(位圖):指一張圖片,常見格式:.png
、.jpg
等java
b.必要性:直接加載大容量的高清Bitmap很容易出現顯示不完整、內存溢出OOM的問題(如報錯:android
java.lang.OutofMemoryError:bitmap size exceeds VM budget
複製代碼
c.核心思想:按必定的採樣率將圖片縮小後再加載進來。git
d.工具類:github
decodeFile()
:從文件系統加載出一個Bitmap對象decodeResource()
:從資源文件加載出一個Bitmap對象decodeStream()
:從輸入流加載出一個Bitmap對象decodeByteArray()
:從字節數組加載出一個Bitmap對象注:算法
- 對應着BitmapFactory類的幾個native方法;
decodeFile()
和decodeResource()
又間接調用decodeStream()
。
BitmapFactory.Options
的參數
注意:BitmapFactory獲取的圖片寬高信息和圖片的位置以及程序運行的設備有關,會致使獲取到不一樣的結果。數組
e.加載流程緩存
BitmapFactory.Options.inJustDecodeBounds
參數設爲true並加載圖片。BitmapFactory.Options
中取出圖片的原始寬高信息,對應outWidth和outHeight參數。BitmapFactory.Options.inJustDecodeBounds
參數設爲false,而後從新加載圖片。經常使用的獲取採樣率的代碼片斷:安全
/**
* 對一個Resources的資源文件進行指定長寬來加載進內存, 並把這個bitmap對象返回
*
* @param res 資源文件對象
* @param resId 要操做的圖片id
* @param reqWidth 最終想要獲得bitmap的寬度
* @param reqHeight 最終想要獲得bitmap的高度
* @return 返回採樣以後的bitmap對象
*/
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight){
BitmapFactory.Options options = new BitmapFactory.Options();
//1.設置inJustDecodeBounds=true獲取圖片尺寸
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res,resId,options);
//3.計算縮放比
options.inSampleSize = calculateInSampleSize(options,reqHeight,reqWidth);
//4.再設爲false,從新從資源文件中加載圖片
options.inJustDecodeBounds =false;
return BitmapFactory.decodeResource(res,resId,options);
}
/**
* 一個計算工具類的方法, 傳入圖片的屬性對象和想要實現的目標寬高. 經過計算獲得採樣值
* @param options 要操做的原始圖片屬性
* @param reqWidth 最終想要獲得bitmap的寬度
* @param reqHeight 最終想要獲得bitmap的高度
* @return 返回採樣率
*/
private static int calculateInSampleSize(BitmapFactory.Options options, int reqHeight, int reqWidth) {
//2.height、width爲圖片的原始寬高
int height = options.outHeight;
int width = options.outWidth;
int inSampleSize = 1;
if(height>reqHeight||width>reqWidth){
int halfHeight = height/2;
int halfWidth = width/2;
//計算縮放比,是2的指數
while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){
inSampleSize*=2;
}
}
return inSampleSize;
}
複製代碼
如今假設ImageView指望圖片大小是爲100*100像素:bash
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),R.mipmap.ic_launcher,100,100);
複製代碼
推薦閱讀:Android開發之高效加載Bitmap
2.緩存策略
爲減小流量消耗,可採用緩存策略。經常使用的緩存算法是LRU(Least Recently Used):
- 核心思想:當緩存滿時, 會優先淘汰那些近期最少使用的緩存對象。
- 兩種方式:LruCache(內存緩存)、DiskLruCache(磁盤緩存)。
a.LruCache(內存緩存)
LinkedHashMap
以強引用的方式存儲外界的緩存對象,並提供get
和put
方法來完成緩存的獲取和添加操做,當緩存滿時會移除較早使用的緩存對象,再添加新的緩存對象。public class LruCache<K, V> {
private final LinkedHashMap<K, V> map;
...
複製代碼
注:幾種引用的含義
- 強引用:直接的對象引用,不會被gc回收;
- 軟引用:當系統內存不足時,對象會被gc回收;
- 弱引用:隨時會被gc回收。
LinkedHashMap
利用一個雙重連接鏈表來維護全部條目item。
而LruCache利用是
accessOrder=true
時的LinkedHashMap實現LRU算法,使得最近訪問的數據會在鏈表尾部,在容量溢出時,將鏈表頭部的數據移除。
sizeOf()
用於計算每一個緩存對象的大小;實例:
//初始化LruCache對象
public void initLruCache()
{
//1.獲取當前進程的可用內存,轉換成KB單位
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
//2.分配緩存的大小
int maxSize = maxMemory / 8;
//3.建立LruCache對象並重寫sizeOf方法
lruCache = new LruCache<String, Bitmap>(maxSize)
{
@Override
protected int sizeOf(String key, Bitmap value) {
// TODO Auto-generated method stub
return value.getWidth() * value.getHeight() / 1024;
}
};
}
//4.LruCache對數據的操做
public void fun()
{
//添加數據
lruCache.put("lizhuo", bm1);
lruCache.put("sushe", bm2);
lruCache.put("jiqian", bm3);
//獲取數據
Bitmap b1 = (lruCache.get("lizhuo"));
Bitmap b2 = (lruCache.get("sushe"));
Bitmap b3 = (lruCache.get("jiqian"));
//刪除數據
lruCache.remove("sushe");
}
複製代碼
推薦閱讀:詳細解讀LruCache類、LruCache 源碼解析
b.DiskLruCache(磁盤緩存)
與LruCache區別:DiskLruCache非泛型類,不能添加類型,而是採用文件存儲,存儲和讀取經過I/O流處理。
open()
方法;flush()
將數據寫入磁盤。(1)先來介紹DiskLruCache的建立:
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
複製代碼
其中,參數含義:
①directory:磁盤緩存的存儲路徑。有兩種目錄:
/sdcard/Android/data/package_name/cache
目錄,當應用被卸載後會被刪除。②appVersion:當前應用的版本號,通常設爲1。
③valueCount:單個節點所對應的數據的個數,通常設爲1。
④maxSize:緩存的總大小,超出這個設定值後DiskLruCache會清除一些緩存
例如,典型的建立過程:
DiskLruCache mDiskLruCache = null;
try {
File cacheDir = getDiskCacheDir(context, "bitmap");
if (!cacheDir.exists()) {
//若緩存地址的路徑不存在就建立一個
cacheDir.mkdirs();
}
mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
} catch (IOException e) {
e.printStackTrace();
}
//用於獲取到緩存地址的路徑
public File getDiskCacheDir(Context context, String uniqueName) {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())|| !Environment.isExternalStorageRemovable()) {
//當SD卡存在或者SD卡不可被移除,獲取路徑 /sdcard/Android/data/<application package>/cache
cachePath = context.getExternalCacheDir().getPath();
} else {
//反之,獲取路徑/data/data/<application package>/cache
cachePath = context.getCacheDir().getPath();
}
return new File(cachePath + File.separator + uniqueName);
}
//用於獲取到當前應用程序的版本號
public int getAppVersion(Context context) {
try {
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
return info.versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return 1;
}
複製代碼
(2)添加緩存操做:經過Editor完成
DiskLruCache.edit()
獲取對應key的Editor;Editor.newOutputStream(0)
獲得一個輸出流;Editor.commit()
提交寫操做,若發生異常,則調用Editor.abort()
進行回退。核心代碼:
//1.返回url的MD5算法結果
String key = hashKeyFormUrl(url);
//2.獲取Editor對象
Editor editor = mDiskLruCache.edit(key);
//3.建立輸出流,其中常量DISK_CACHE_INDEX = 0
OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
//4.寫入數據
outputStream.wirte(data);
//5.提交寫操做
editor.commit();
複製代碼
(3)查找緩存操做:和緩存添加的過程相似
DiskLruCache.get()
獲取對應key的Snapshot對象;Snapshot.getInputStream(0)
獲得一個輸入流(可向下轉型爲FileInputStream);核心代碼:
//1.返回url的MD5算法結果
String key = hashKeyFormUrl(url);
//2.獲取Snapshot對象
Snapshot snapshot = mDiskLruCache.get(key);
//3.建立輸入流,其中常量DISK_CACHE_INDEX = 0
InputStream inputStream = snapshot.getInputStream(DISK_CACHE_INDEX);
//4.讀出數據
int data = inputStream.read();
複製代碼
- 問題:FileInputStream是一種有序的文件流,調用兩次
BitmapFactory.decodeStream()
會影響文件流的位置屬性,致使第二次解析結果爲空。- 解決辦法:經過文件流獲得其對應的文件描述符,再調用
BitmapFactory.decodeFileDescriptor()
來加載一張縮放後的圖片。
推薦閱讀:Android DiskLruCache徹底解析、 源碼解析
3.ImageLoader 的使用
a.ImageLoader內部封裝了Bitmap的高效加載、LruCache和DiskLruCache。
b.應具有功能:
更多瞭解:Android 開源框架Universal-Image-Loader徹底解析、開源框架ImageLoader的完美例子
c.使用場景:
但願這篇文章對你有幫助~