馴れ初め(ダイジェスト)
TwitteアイコンのキャッシュにSoftReferenceを使っていたのですが、2.2 と 2.3 / 4.xで挙動が違って困っていたところ、以下のツイートに出会う。
2.3 から GC の方式が変わったので、SoftReference や WeakReference で Bitmap をキャッシュするのはあんまり意味ないからオススメしないそうだ
— Yuki Anzaiさん (@yanzm) 8月 14, 2012
これどこ情報なんだろう?ListViewに表示するTwitterアイコンのキャッシュにSoftReference使ってるけど、確かに2.2、2.3、4.xで動き違って困ってた。2.3と4.xはすぐキャッシュが解放されてて再取得が走る... > RT
— koji okabeさん (@kojiokb) 8月 14, 2012
@yanzm 知りませんでした(・o・) そのうち自作アプリもLruCache使うようにしてみます。developer.android.com/training/displ…
— HaRuさん (@h6a_h4i) 8月 14, 2012
これか。明日じっくり読んでみる。 Android Training - Advanced Training - Caching Bitmaps developer.android.com/training/displ…
— koji okabeさん (@kojiokb) 8月 14, 2012
ということで、Android Training「Caching Bitmaps」で使われていたLruCacheに辿り着いた次第です。
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
Twitter と 有益な情報をツイートしてくれる方々に感謝です。
コード
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.graphics.Bitmap; | |
import android.support.v4.util.LruCache; | |
public final class ImageCache { | |
private static final int MEM_CACHE_SIZE = 1 * 1024 * 1024; // 1MB | |
private static LruCache<String, Bitmap> sLruCache; | |
static { | |
sLruCache = new LruCache<String, Bitmap>(MEM_CACHE_SIZE) { | |
@Override | |
protected int sizeOf(String key, Bitmap bitmap) { | |
return bitmap.getRowBytes() * bitmap.getHeight(); | |
} | |
}; | |
} | |
private ImageCache() { | |
} | |
public static void setImage(String key, Bitmap bitmap) { | |
if (getImage(key) == null) { | |
sLruCache.put(key, bitmap); | |
} | |
} | |
public static Bitmap getImage(String key) { | |
return sLruCache.get(key); | |
} | |
} |
使い方
LruCacheはAPI Level 12からのクラスですが
http://developer.android.com/reference/android/util/LruCache.html
Support Libraryに入っているので
http://developer.android.com/reference/android/support/v4/util/LruCache.html
API Level 4から使えるようになっています。
使い方としては
・KeyとValueの型を指定してインスタンス化する
・LruCache#sizeOf()をオーバーライドしてBitmapのByte数を返してあげる
だけです。
Android TrainingのサンプルコードではBitmap#getByteCount()を使っていますがAPI Level 12からのメソッドなので上記の処理に書き換えています。
上記のコードではキャッシュサイズを1MB固定にしていますが、端末のDalvikVMヒープサイズから動的に決めたい場合は、Android TrainingのサンプルコードのようにすればOKです。
あとは、Twitterアイコン画像URLをKeyにBitmapをキャッシュしておいて、画像使用時にImageCache.getImage(Twitterアイコン画像URL)がnullだったら再取得というような処理にしておけばよいかと思います。
まとめ
2.3以降GCがモア アグレッシブになっているので、SoftReferenceを使用した場合、ListViewの行が画面から消えるとすぐに画像キャッシュがクリアされてしまう。
従って、今後はLruCacheを使いましょう。
といったところでしょうか。
LruCacheを利用することで、SoftReferenceを使用した場合の2.2 と 2.3以上でキャッシュの挙動が違うという問題が解決できました。
公式ドキュメントはちゃんとチェックしておかないとダメですね。。
因に僕は、常にこの構成でデバッグしています。
書いたコードに対してすぐにバージョン間の差異が確認できるので良いかなと思っています。
今のところ誰からも賛同を得られていませんが...
Fragmentationと共に生きる方法
http://neta-abc.blogspot.jp/2012/08/fragmentation.html