這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)Android中怎么實(shí)現(xiàn)圖片緩存機(jī)制,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

Android 圖片緩存機(jī)制的深入理解
Android加載一張圖片到用戶界面是很簡單的,但是當(dāng)一次加載多張圖片時(shí),情況就變得復(fù)雜起來。很多情況下(像ListView、GridView或ViewPager等組件),屏幕上已顯示的圖片和即將滑動(dòng)到當(dāng)前屏幕上的圖片數(shù)量基本上是沒有限制的。
這些組件通過重用已經(jīng)移除屏幕的子視圖來將降低內(nèi)存的使用,垃圾回收器也會(huì)及時(shí)釋放那些已經(jīng)不再使用的已下載的圖片,這些都是很好的方法,但是為了保持一個(gè)流暢的、快速加載的用戶界面,就應(yīng)該避免當(dāng)再次回到某個(gè)頁面時(shí)而重新處理圖片。內(nèi)存緩存和磁盤緩存可以幫我們做到這些,它們允許組件快速地重新加載已處理好的圖片。
使用內(nèi)存緩存
內(nèi)存緩存允許快速地訪問圖片,但它以占用App寶貴的內(nèi)存為代價(jià)。LruCache類(API Level 4的Support Library也支持)特別適合來做圖片緩存,它使用一個(gè)強(qiáng)引用的LinkedHashMap來保存最近使用的對象,并且會(huì)在緩存數(shù)量超出預(yù)設(shè)的大小之前移除最近最少使用的對象。
說明:以前流行的內(nèi)存緩存方案是使用軟引用或弱引用來緩存圖片,然而現(xiàn)在不推薦這樣做了,因?yàn)閺腶ndroid 2.3(API Level 9)起,垃圾收集器更傾向于先回收軟引用或弱引用,這樣就使它們變得低效。另外在Android 3.0(API Level 11)之前,圖片的像素?cái)?shù)據(jù)是存儲(chǔ)在本地內(nèi)存(native memory)中的,它以一種不可預(yù)測的方式釋放,因此可能會(huì)導(dǎo)致App超過內(nèi)存限制甚至崩潰。
為了給LruCache設(shè)置一個(gè)合適的大小,以下是應(yīng)該考慮的一些因素:
1.你的Activity或App的可用內(nèi)存是多少?
2.一次展示到屏幕上的圖片是多少?有多少圖片需要預(yù)先準(zhǔn)備好以便隨時(shí)加載到屏幕?
3.設(shè)備的屏幕尺寸和密度是多少?像Galaxy Nexus這樣的高分辨率(xhdpi)設(shè)備比Nexus S這樣分辨率(hdpi)的設(shè)備在緩存相同數(shù)量的圖片時(shí)需要更大的緩存空間。
4.圖片的尺寸和配置是怎樣的?每張圖片會(huì)占用多少內(nèi)存?
5.圖片的訪問頻率如何?是否有一些圖片比另一些訪問更加頻繁?如果這樣的話,或許可以將某些圖片一直保存在內(nèi)存里或者針對不同的圖片分組設(shè)置不同的LruCache對象。
6.你能否平衡圖片質(zhì)量和數(shù)量之間的關(guān)系?有時(shí)候存儲(chǔ)更多低質(zhì)量的圖片更加有用,當(dāng)在需要的時(shí)候,再通過后臺(tái)任務(wù)下載高質(zhì)量的圖片。
這里沒有一個(gè)具體的大小和計(jì)算公式適用于所有的App,你需要分析你的使用情況并得到一個(gè)合適的方案。當(dāng)一個(gè)緩存太小時(shí)會(huì)導(dǎo)致無益的額外的開銷,而緩存太大時(shí)也可能會(huì)引起Java.lang.OutOfMemory異常,另外緩存越大,留給App其他部分的內(nèi)存相應(yīng)就越小。
這里是一個(gè)為圖片設(shè)置LruCache的示例:
private LruCache<String, Bitmap> mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
};
...
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}說明:在上述例子中,我們分配了應(yīng)用內(nèi)存的1/8作為緩存大小,在一個(gè)normal/hdpi的設(shè)備上最少也有4MB(32/8)的大小。一個(gè)800*480分辨率的屏幕上的一個(gè)填滿圖片的GridView大概占用1.5MB(800*480*4byte)的內(nèi)存,因此該Cache至少可以緩存2.5頁這樣的圖片。
當(dāng)加載一張圖片到ImageView時(shí),首先檢查LruCache,如果找到圖片,就直接用來更新ImageView,如果沒找到就開啟一個(gè)后臺(tái)線程來處理:
public void loadBitmap(int resId, ImageView imageView) {
final String imageKey = String.valueOf(resId);
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
if (bitmap != null) {
mImageView.setImageBitmap(bitmap);
} else {
mImageView.setImageResource(R.drawable.image_placeholder);
BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
task.execute(resId);
}
}上述線程中,在解碼圖片之后,也需要把它添加到內(nèi)存緩存中:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
// Decode image in background.
@Override
protected Bitmap doInBackground(Integer... params) {
final Bitmap bitmap = decodeSampledBitmapFromResource(
getResources(), params[0], 100, 100));
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
return bitmap;
}
...
}使用磁盤緩存
雖然內(nèi)存緩存在快速訪問最近使用的圖片時(shí)是很有用的,但是你無法保證你所需要的圖片就在緩存中,類似GridView這樣展示大量數(shù)據(jù)的組件可以很輕易地就占滿內(nèi)存緩存。你的App也可能被類似電話這樣的任務(wù)打斷,當(dāng)App被切換到后臺(tái)后也可能被殺死,內(nèi)存緩存也可能被銷毀,一旦用戶回到之前的界面,你的App依然要重新處理每個(gè)圖片。
磁盤緩存可以用來輔助存儲(chǔ)處理過的圖片,當(dāng)內(nèi)存緩存中圖片不可用時(shí),可以從磁盤緩存中查找,從而減少加載次數(shù)。當(dāng)然,從磁盤讀取圖片要比從內(nèi)存讀取慢并且讀取時(shí)間是不可預(yù)期的,因此需要使用后臺(tái)線程來讀取。
說明:ContentProvider 可能是一個(gè)合適的存儲(chǔ)頻繁訪問的圖片的地方,比如在Image Gallery應(yīng)用中。
這里的示例代碼是從Android源代碼中剝離出來的DiskLruCache,以下是更新后的實(shí)例代碼,在內(nèi)存緩存的基礎(chǔ)上增加了磁盤緩存:
private DiskLruCache mDiskLruCache;
private final Object mDiskCacheLock = new Object();
private boolean mDiskCacheStarting = true;
private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
private static final String DISK_CACHE_SUBDIR = "thumbnails";
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// Initialize memory cache
...
// Initialize disk cache on background thread
File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);
new InitDiskCacheTask().execute(cacheDir);
...
}
class InitDiskCacheTask extends AsyncTask<File, Void, Void> {
@Override
protected Void doInBackground(File... params) {
synchronized (mDiskCacheLock) {
File cacheDir = params[0];
mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);
mDiskCacheStarting = false; // Finished initialization
mDiskCacheLock.notifyAll(); // Wake any waiting threads
}
return null;
}
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
// Decode image in background.
@Override
protected Bitmap doInBackground(Integer... params) {
final String imageKey = String.valueOf(params[0]);
// Check disk cache in background thread
Bitmap bitmap = getBitmapFromDiskCache(imageKey);
if (bitmap == null) { // Not found in disk cache
// Process as normal
final Bitmap bitmap = decodeSampledBitmapFromResource(
getResources(), params[0], 100, 100));
}
// Add final bitmap to caches
addBitmapToCache(imageKey, bitmap);
return bitmap;
}
...
}
public void addBitmapToCache(String key, Bitmap bitmap) {
// Add to memory cache as before
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
// Also add to disk cache
synchronized (mDiskCacheLock) {
if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {
mDiskLruCache.put(key, bitmap);
}
}
}
public Bitmap getBitmapFromDiskCache(String key) {
synchronized (mDiskCacheLock) {
// Wait while disk cache is started from background thread
while (mDiskCacheStarting) {
try {
mDiskCacheLock.wait();
} catch (InterruptedException e) {}
}
if (mDiskLruCache != null) {
return mDiskLruCache.get(key);
}
}
return null;
}
// Creates a unique subdirectory of the designated app cache directory. Tries to use external
// but if not mounted, falls back on internal storage.
public static File getDiskCacheDir(Context context, String uniqueName) {
// Check if media is mounted or storage is built-in, if so, try and use external cache dir
// otherwise use internal cache dir
final String cachePath =
Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
!isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :
context.getCacheDir().getPath();
return new File(cachePath + File.separator + uniqueName);
}上述就是小編為大家分享的Android中怎么實(shí)現(xiàn)圖片緩存機(jī)制了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
網(wǎng)站標(biāo)題:Android中怎么實(shí)現(xiàn)圖片緩存機(jī)制-創(chuàng)新互聯(lián)
當(dāng)前地址:http://www.chinadenli.net/article24/djcoce.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供響應(yīng)式網(wǎng)站、企業(yè)建站、虛擬主機(jī)、外貿(mào)建站、網(wǎng)頁設(shè)計(jì)公司、搜索引擎優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容