Picasso 內(nèi)部緩存

Picasso是Android中常用的圖片加載框架,本文注重解析其緩存邏輯碧囊。如果你沒有使用過picasso咖楣,請簡單的看下它的主頁瑟匆。

清空緩存

項目中經(jīng)常需要你手動清空picasso的緩存甸箱,比如用戶更新了個人頭像育叁。如果你是帶著相同問題找到這個帖子的話,那以下就是你的答案芍殖。(2016/10/9更新)

Picasso.with(context).invalidate(url);  // clear bitmap cache in memory
// 遍歷disk cache 的url key豪嗽,找出你想刪的image url,然后remove掉豌骏。

// Picasso不提供getDownloader()龟梦,所以你得用以下方法繞過。
OkHttpDownloader okHttpDownloader = new OkHttpDownloader(context);
OkHttpClient client = ReflectionHelpers.getField(okHttpDownloader, "client");
picassoDiskCache = client.getCache(); // 拿到disk cache
Picasso picasso = new Picasso.Builder(context)
        .downloader(okHttpDownloader)
        .build();
Iterator<String> iterator = picassoDiskCache.urls()
while (iterator.hasNext()) {
   String url = iterator.next();
   if (imgUrl.equals(url)) {
     // remove disk cache
     iterator.remove();
     break;
   }
 }

加載圖片的邏輯

首先Picasso使用兩級緩存模型窃躲,內(nèi)存緩存及硬盤緩存变秦。
Picasso常用的方法為:

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

內(nèi)存緩存

我們看下當picasso試圖加載圖片利用內(nèi)存緩存的邏輯 (以下邏輯摘自上面into(imageView)方法),其中 quickMemoryCacheCheck() 就是對內(nèi)存緩存進行搜索框舔,命中則使用內(nèi)存中的緩存:

// 根據(jù)內(nèi)存設定蹦玫,如果啟用內(nèi)存緩存則當命中時使用內(nèi)存圖片
if (shouldReadFromMemoryCache(memoryPolicy)) {
  Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
  if (bitmap != null) {
    picasso.cancelRequest(target);
    setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
    if (picasso.loggingEnabled) {
      log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
    }
    if (callback != null) {
      callback.onSuccess();
    }
    return;
  }
}

繼續(xù)深入這個quickMemoryCacheCheck() 方法

Bitmap quickMemoryCacheCheck(String key) {
  Bitmap cached = cache.get(key);
  if (cached != null) {
    stats.dispatchCacheHit();
  } else {
    stats.dispatchCacheMiss();
  }
  return cached;
}

其實就是檢查cache這個對象,picasso使用的是自己寫的LRU緩存刘绣,邏輯和Android SDK的差不多樱溉。這個LruCache的內(nèi)存大小為~15%

/** Create a cache using an appropriate portion of the available RAM as the maximum size. */
public LruCache(@NonNull Context context) {  
   this(Utils.calculateMemoryCacheSize(context));
}

// Utils.calculateMemoryCacheSize
static int calculateMemoryCacheSize(Context context) {
  ActivityManager am = getService(context, ACTIVITY_SERVICE);
  boolean largeHeap = (context.getApplicationInfo().flags & FLAG_LARGE_HEAP) != 0;
  int memoryClass = am.getMemoryClass();
  if (largeHeap && SDK_INT >= HONEYCOMB) {
    memoryClass = ActivityManagerHoneycomb.getLargeMemoryClass(am);
  }
  // Target ~15% of the available heap.
  return (int) (1024L * 1024L * memoryClass / 7);
}

它的內(nèi)存緩存鍵值可以看出設計者的idea,它使用的是url和顯示imageView所要求的屬性等纬凤。因為即使是同一張圖片福贞,在不同大小/效果的imageView所使用的bitmap也是不同的。以下是鍵值的例子:

http://i.imgur.com/DAl0KB8.jpg\nresize:540x540

所以在清空一個url對應的內(nèi)存緩存時的邏輯時這樣的(Picasso.invlidate(url)):

/**
 * Invalidate all memory cached images for the specified {@code uri}.
 *
 * @see #invalidate(String)
 * @see #invalidate(File)
 */
public void invalidate(@Nullable Uri uri) {
  if (uri != null) {
    cache.clearKeyUri(uri.toString());
  }
}

// cache.clearKeyUri(uri.toString());
@Override public final synchronized void clearKeyUri(String uri) {
  int uriLength = uri.length();
  for (Iterator<Map.Entry<String, Bitmap>> i = map.entrySet().iterator(); i.hasNext();) {
    Map.Entry<String, Bitmap> entry = i.next();
    String key = entry.getKey();
    Bitmap value = entry.getValue();
    int newlineIndex = key.indexOf(KEY_SEPARATOR);
    // 如果對應的url相同停士,刪除各種圖片屬性的bitmap緩存
    if (newlineIndex == uriLength && key.substring(0, newlineIndex).equals(uri)) {
      i.remove();
      size -= Utils.getBitmapBytes(value);
    }
  }
}  

以上是內(nèi)存緩存邏輯挖帘,以下是硬盤緩存邏輯。因為硬盤緩存實際是控制在okhttp中恋技,這里我們就簡單的講下picasso是怎么自定義它的硬盤緩存的拇舀。

硬盤緩存

依然從into(imageView)開始

Action action =
    new TargetAction(picasso, target, request, memoryPolicy, networkPolicy, errorDrawable,
        requestKey, tag, errorResId);
picasso.enqueueAndSubmit(action);

在picasso.enqueueAndSubmit()中,跟蹤幾層邏輯后蜻底,其實用的是picasso的downloader去下載響應的圖片骄崩。而當服務器返回的頭中有緩存頭的信息的話(比如cache-control等),okhttp就會為我們緩存它薄辅。http頭樣例:

cache-control: public, max-age=31536000

具體的網(wǎng)絡請求邏輯各位可以到github上看下OkHttpDownloader.java (根據(jù)不同版本的okhttp依賴Downloader類可能不一樣) 這個文件的load()方法要拂。而在初始化OkHttpDownloader時,picasso創(chuàng)建了一個自己的disk cache object站楚,大小是~2%的硬盤空間脱惰。默認路徑是data/data/your package name/cache/picasso-cache/。

  /** Create new downloader that uses OkHttp. This will install an image cache into your application
   * cache directory.
   */
  public OkHttpDownloader(final Context context) {
    this(Utils.createDefaultCacheDir(context));
  }

  /**
   * Create new downloader that uses OkHttp. This will install an image cache into the specified
   * directory.
   *
   * @param cacheDir The directory in which the cache should be stored
   */
  public OkHttpDownloader(final File cacheDir) {
    this(cacheDir, Utils.calculateDiskCacheSize(cacheDir));
  }

  /**
   * Create new downloader that uses OkHttp. This will install an image cache into your application
   * cache directory.
   *
   * @param maxSize The size limit for the cache.
   */
  public OkHttpDownloader(final Context context, final long maxSize) {
    this(Utils.createDefaultCacheDir(context), maxSize);
  }

  @TargetApi(JELLY_BEAN_MR2)
  static long calculateDiskCacheSize(File dir) {
    long size = MIN_DISK_CACHE_SIZE;

    try {
      StatFs statFs = new StatFs(dir.getAbsolutePath());
      //noinspection deprecation
      long blockCount =
          SDK_INT < JELLY_BEAN_MR2 ? (long) statFs.getBlockCount() : statFs.getBlockCountLong();
      //noinspection deprecation
      long blockSize =
          SDK_INT < JELLY_BEAN_MR2 ? (long) statFs.getBlockSize() : statFs.getBlockSizeLong();
      long available = blockCount * blockSize;
      // Target 2% of the total space.
      size = available / 50;
    } catch (IllegalArgumentException ignored) {
    }

    // Bound inside min/max size for disk cache.
    return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE);
  }

這個磁盤緩存是DiskLruCache窿春。至此關于picasso的緩存歸納如下:

  • 加載圖片的url及圖片屬性在內(nèi)存緩存中存在拉一,如果存在讀取內(nèi)存
  • 如果不命中內(nèi)存緩存采盒,加載圖片到disk和memory中。
  • 內(nèi)存和硬盤緩存相互獨立舅踪。

感謝閱讀纽甘,歡迎指正良蛮!
注:全文使用的代碼片段均來自picasso 2.5.2版本抽碌。

2016/10/9日更新:更新清除硬盤緩存的代碼,以友好的方式獲取cache對象决瞳。當時看的代碼是master上货徙,所以是沒有okhttp3downloader的,不過2.5.2版本上的okhttpdownloader硬盤緩存的邏輯時一樣的皮胡,所以無大礙痴颊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市屡贺,隨后出現(xiàn)的幾起案子蠢棱,更是在濱河造成了極大的恐慌,老刑警劉巖甩栈,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泻仙,死亡現(xiàn)場離奇詭異,居然都是意外死亡量没,警方通過查閱死者的電腦和手機玉转,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來殴蹄,“玉大人究抓,你說我怎么就攤上這事∠疲” “怎么了刺下?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長稽荧。 經(jīng)常有香客問我怠李,道長,這世上最難降的妖魔是什么蛤克? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任捺癞,我火速辦了婚禮,結(jié)果婚禮上构挤,老公的妹妹穿的比我還像新娘髓介。我一直安慰自己,他們只是感情好筋现,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布唐础。 她就那樣靜靜地躺著箱歧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪一膨。 梳的紋絲不亂的頭發(fā)上呀邢,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音豹绪,去河邊找鬼价淌。 笑死,一個胖子當著我的面吹牛瞒津,可吹牛的內(nèi)容都是我干的蝉衣。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼巷蚪,長吁一口氣:“原來是場噩夢啊……” “哼病毡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起屁柏,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤啦膜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后淌喻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體僧家,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年似嗤,在試婚紗的時候發(fā)現(xiàn)自己被綠了啸臀。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡烁落,死狀恐怖乘粒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伤塌,我是刑警寧澤灯萍,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站每聪,受9級特大地震影響旦棉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜药薯,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一绑洛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧童本,春花似錦真屯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽运沦。三九已至,卻和暖如春配深,著一層夾襖步出監(jiān)牢的瞬間携添,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工篓叶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留烈掠,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓澜共,卻偏偏與公主長得像向叉,于是被迫代替她去往敵國和親锥腻。 傳聞我的和親對象是個殘疾皇子嗦董,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容