欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android 圖片緩存機(jī)制的深入理解

 更新時(shí)間:2017年08月18日 09:22:49   投稿:lqh  
這篇文章主要介紹了Android 圖片緩存機(jī)制的深入理解的相關(guān)資料,這里提供了實(shí)現(xiàn)實(shí)例幫助大家理解圖片緩存機(jī)制的知識(shí),需要的朋友可以參考下

Android 圖片緩存機(jī)制的深入理解

Android加載一張圖片到用戶界面是很簡(jiǎn)單的,但是當(dāng)一次加載多張圖片時(shí),情況就變得復(fù)雜起來(lái)。很多情況下(像ListView、GridView或ViewPager等組件),屏幕上已顯示的圖片和即將滑動(dòng)到當(dāng)前屏幕上的圖片數(shù)量基本上是沒(méi)有限制的。

這些組件通過(guò)重用已經(jīng)移除屏幕的子視圖來(lái)將降低內(nèi)存的使用,垃圾回收器也會(huì)及時(shí)釋放那些已經(jīng)不再使用的已下載的圖片,這些都是很好的方法,但是為了保持一個(gè)流暢的、快速加載的用戶界面,就應(yīng)該避免當(dāng)再次回到某個(gè)頁(yè)面時(shí)而重新處理圖片。內(nèi)存緩存和磁盤(pán)緩存可以幫我們做到這些,它們?cè)试S組件快速地重新加載已處理好的圖片。

使用內(nèi)存緩存

內(nèi)存緩存允許快速地訪問(wèn)圖片,但它以占用App寶貴的內(nèi)存為代價(jià)。LruCache類(API Level 4的Support Library也支持)特別適合來(lái)做圖片緩存,它使用一個(gè)強(qiáng)引用的LinkedHashMap來(lái)保存最近使用的對(duì)象,并且會(huì)在緩存數(shù)量超出預(yù)設(shè)的大小之前移除最近最少使用的對(duì)象。

說(shuō)明:以前流行的內(nèi)存緩存方案是使用軟引用或弱引用來(lá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ù)測(cè)的方式釋放,因此可能會(huì)導(dǎo)致App超過(guò)內(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.圖片的訪問(wèn)頻率如何?是否有一些圖片比另一些訪問(wèn)更加頻繁?如果這樣的話,或許可以將某些圖片一直保存在內(nèi)存里或者針對(duì)不同的圖片分組設(shè)置不同的LruCache對(duì)象。

6.你能否平衡圖片質(zhì)量和數(shù)量之間的關(guān)系?有時(shí)候存儲(chǔ)更多低質(zhì)量的圖片更加有用,當(dāng)在需要的時(shí)候,再通過(guò)后臺(tái)任務(wù)下載高質(zhì)量的圖片。

這里沒(méi)有一個(gè)具體的大小和計(jì)算公式適用于所有的App,你需要分析你的使用情況并得到一個(gè)合適的方案。當(dāng)一個(gè)緩存太小時(shí)會(huì)導(dǎo)致無(wú)益的額外的開(kāi)銷,而緩存太大時(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); 
} 

說(shuō)明:在上述例子中,我們分配了應(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頁(yè)這樣的圖片。

當(dāng)加載一張圖片到ImageView時(shí),首先檢查L(zhǎng)ruCache,如果找到圖片,就直接用來(lái)更新ImageView,如果沒(méi)找到就開(kāi)啟一個(gè)后臺(tái)線程來(lá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; 
  } 
  ... 
} 

使用磁盤(pán)緩存

雖然內(nèi)存緩存在快速訪問(wèn)最近使用的圖片時(shí)是很有用的,但是你無(wú)法保證你所需要的圖片就在緩存中,類似GridView這樣展示大量數(shù)據(jù)的組件可以很輕易地就占滿內(nèi)存緩存。你的App也可能被類似電話這樣的任務(wù)打斷,當(dāng)App被切換到后臺(tái)后也可能被殺死,內(nèi)存緩存也可能被銷毀,一旦用戶回到之前的界面,你的App依然要重新處理每個(gè)圖片。

磁盤(pán)緩存可以用來(lái)輔助存儲(chǔ)處理過(guò)的圖片,當(dāng)內(nèi)存緩存中圖片不可用時(shí),可以從磁盤(pán)緩存中查找,從而減少加載次數(shù)。當(dāng)然,從磁盤(pán)讀取圖片要比從內(nèi)存讀取慢并且讀取時(shí)間是不可預(yù)期的,因此需要使用后臺(tái)線程來(lái)讀取。

說(shuō)明:ContentProvider 可能是一個(gè)合適的存儲(chǔ)頻繁訪問(wèn)的圖片的地方,比如在Image Gallery應(yīng)用中。

這里的示例代碼是從Android源代碼中剝離出來(lái)的DiskLruCache,以下是更新后的實(shí)例代碼,在內(nèi)存緩存的基礎(chǔ)上增加了磁盤(pán)緩存:

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); 
} 

說(shuō)明:初始化磁盤(pán)緩存需要磁盤(pán)操作因此它不應(yīng)在主線程進(jìn)行,然而這意味著有可能在磁盤(pán)緩存尚未初始化之前就有訪問(wèn)操作發(fā)生,為了解決這個(gè)問(wèn)題,在上面的實(shí)現(xiàn)中,使用一個(gè)鎖對(duì)象來(lái)確保只有在磁盤(pán)緩存初始化之后才會(huì)從磁盤(pán)緩存讀取數(shù)據(jù)。

內(nèi)存緩存可以直接在UI線程讀取,然而磁盤(pán)緩存必須在后臺(tái)線程檢查,磁盤(pán)操作不應(yīng)該在UI線程發(fā)生。當(dāng)圖片處理完畢后,務(wù)必將最終的圖片添加到內(nèi)存緩存和磁盤(pán)緩存以備后續(xù)使用。

以上就是對(duì)Android 圖片緩存機(jī)制的詳解,如有疑問(wèn)請(qǐng)留言或者到本站社區(qū)交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!

相關(guān)文章

  • Android網(wǎng)絡(luò)數(shù)據(jù)開(kāi)關(guān)用法簡(jiǎn)單示例

    Android網(wǎng)絡(luò)數(shù)據(jù)開(kāi)關(guān)用法簡(jiǎn)單示例

    這篇文章主要介紹了Android網(wǎng)絡(luò)數(shù)據(jù)開(kāi)關(guān)用法,通過(guò)自定義函數(shù)調(diào)用系統(tǒng)服務(wù)實(shí)現(xiàn)開(kāi)關(guān)功能,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2016-06-06
  • Flutter文本Text和輸入框TextField組件使用示例

    Flutter文本Text和輸入框TextField組件使用示例

    這篇文章主要為大家介紹了Flutter文本Text和輸入文本框TextField組件使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • Android AlertDialog對(duì)話框用法示例

    Android AlertDialog對(duì)話框用法示例

    這篇文章主要介紹了Android AlertDialog對(duì)話框用法,結(jié)合實(shí)例形式分析了AlertDialog對(duì)話框的功能及常見(jiàn)使用技巧,需要的朋友可以參考下
    2016-06-06
  • Android封裝Banner控件方法介紹

    Android封裝Banner控件方法介紹

    android-banner實(shí)現(xiàn)了一般banner循環(huán)輪播的效果,一頁(yè)只顯示一張圖片,也可以一頁(yè)顯示一張圖和相鄰兩個(gè)圖片的一部分,此項(xiàng)目?jī)H僅是banner展示圖片,沒(méi)有多余的諸如指示器、頁(yè)面切換動(dòng)畫(huà)等效果代碼,詳見(jiàn)效果圖和案例代碼
    2023-03-03
  • Android模擬登錄評(píng)論CSDN實(shí)現(xiàn)代碼

    Android模擬登錄評(píng)論CSDN實(shí)現(xiàn)代碼

    本篇文章主要介紹了Android模擬登錄評(píng)論CSDN實(shí)現(xiàn)代碼,可以實(shí)現(xiàn)登陸發(fā)表評(píng)論到官方網(wǎng)站,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2016-11-11
  • 分析Android Choreographer源碼

    分析Android Choreographer源碼

    Choreographer的作用主要是配合Vsync,給上層App的渲染提供一個(gè)穩(wěn)定的Message處理的時(shí)機(jī),也就是Vsync到來(lái)的時(shí)候,系統(tǒng)通過(guò)對(duì)Vsync信號(hào)周期的調(diào)整,來(lái)控制每一幀繪制操作的時(shí)機(jī)
    2021-06-06
  • Android實(shí)現(xiàn)QQ圖片說(shuō)說(shuō)照片選擇效果

    Android實(shí)現(xiàn)QQ圖片說(shuō)說(shuō)照片選擇效果

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)QQ圖片說(shuō)說(shuō)照片選擇效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • Flutter插件開(kāi)發(fā)之HmsScanKit實(shí)現(xiàn)示例詳解

    Flutter插件開(kāi)發(fā)之HmsScanKit實(shí)現(xiàn)示例詳解

    這篇文章主要為大家介紹了Flutter插件開(kāi)發(fā)之HmsScanKit實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • Android 中添加水平線和垂直線方法總結(jié)

    Android 中添加水平線和垂直線方法總結(jié)

    這篇文章主要介紹了Android 中添加水平線和垂直線方法總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Android 使用AsyncTask實(shí)現(xiàn)多任務(wù)多線程斷點(diǎn)續(xù)傳下載

    Android 使用AsyncTask實(shí)現(xiàn)多任務(wù)多線程斷點(diǎn)續(xù)傳下載

    這篇文章主要介紹了Android 使用AsyncTask實(shí)現(xiàn)多任務(wù)多線程斷點(diǎn)續(xù)傳下載的相關(guān)資料,需要的朋友可以參考下
    2018-05-05

最新評(píng)論