GlideのDiskLruCacheの実装を読む
この記事では、Glideで使用されている DiskLruCache のキャッシュ情報を保存する処理の実装と仕様についてみていきます。
glide/DiskLruCache.java at v4.11.0 · bumptech/glide
DiskLruCacheの概要
DiskLruCache は、LRUでファイルのキャッシュを行うクラスです。キャッシュ情報の管理には、ジャーナルファイルとメモリ上のデータ構造を使用しています。
ジャーナルファイルについては後で述べますが、キャッシュの読み込みや書き込みを行った履歴を記録したファイルとなっています。
キャッシュの操作を行った時には、はじめにメモリ上の LinkedHashMap に保存された後、ジャーナルファイルへの保存が行われます。
DiskLruCache のインスタンスは DiskLruCache.open を使用して取得することができ、 open をした時にジャーナルファイルからメモリへキャッシュ情報が読み込まれます。
ジャーナルファイルの中身
ジャーナルファイルの中身は以下のような形式になっています。
libcore.io.DiskLruCache 1 100 2 CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054 DIRTY 335c4c6028171cfddfbaae1a9c313c52 CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342 REMOVE 335c4c6028171cfddfbaae1a9c313c52 DIRTY 1ab96a171faeeee38496d8b330771a7a CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234 READ 335c4c6028171cfddfbaae1a9c313c52 READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
はじめの4行はそれぞれ、
libcore.io.DiskLruCache : 固定の文字列 1 : ディスクキャッシュのバージョン 100 : アプリが指定するバージョン(Glideでは固定で1) 2 : ValueCount 1つのkeyに紐づく値の個数(Glideでは固定で1)
を表しています。ディスクキャッシュやアプリが指定するバージョンが変わると、一度キャッシュがすべてクリアされます。
5行目以降がキャッシュ情報の取得や更新のログとなっています。アクションと付加情報がスペース区切りで1行ごとに記されていて、アクションは表に示す4種類があります。
| アクション | 付加情報1 | 付加情報2~ |
|---|---|---|
| READ | キャッシュキー | - |
| REMOVE | キャッシュキー | - |
| DIRTY | キャッシュキー | - |
| CLEAN | キャッシュキー | キャッシュしている各ファイルのサイズ ValueCountの数 |
各アクションの意味
それぞれのアクションについて説明していきたいと思います。
READ/REMOVE は想像がつきやすいですが、キャッシュ情報の取得/削除を行った時のアクションです。
個人的には、データベースなどに関する知識があまりないため、DIRTY や CLEAN が表す意味を理解するのが少し難しかったです。
DiskLruCache では、キャッシュ情報を書き換える際に DiskLruCache#edit などを使用して取得できる Editor クラスを介する必要があります。アプリ側では、取得した Editor に対してキャッシュ情報の追加や上書きを行った後に Editor#commit を呼び出すことで、実際に画像ファイルキャッシュの更新が行われます。
この時に Editor を取得してから commit を行うまでの状態が DIRTY となります。( DIRTY のログが書き込まれるのはEditor 作成時)
DIRTY となっている情報を commit し終わると CLEAN のログが書き込まれます。
キャッシュのmax sizeを超えた時の処理
何度も画像のキャッシュを行っていくと、当然ですがキャッシュサイズが増えていきmax sizeを越えることがあります。そのサイズを超えた時は、以下の処理が行われてキャッシュサイズがmax size以下に抑えられます。
ここでいう 無駄なログ には何パターンかありますが、例えば
キーAの情報が編集A(DIRTY)->更新A(CLEAN)->取得(READ)-> .. ->更新X(CLEAN)と操作された時の最後のDIRTYまたはCLEAN以外のログキーAの最終的な情報がキャッシュに保存されていれば良いので、それまでの更新履歴などは不要
- キャッシュ情報を取得した時のログ(READ)
その他の処理
キャッシュ情報の追加や削除を行う時には上記のメモリキャッシュの更新とジャーナルファイルへのログの記録が主な処理となっています。
しかし、エラーが起きる可能性もあり、その場合は基本的にキャッシュ情報の削除が行われます(キャッシュは消しても問題はないはずなので安全な方にたおしている)
また、 DIRTY から CLEAN へ状態がうつる場合の処理についても少し触れておきます。
DIRTY と CLEAN はそれぞれ別々のファイルとして画像を保存するようになっていて、commit 時に DIRTY 用のファイルから CLEAN 用のファイルへリネームが行われます。
glide/DiskLruCache.java at v4.11.0 · bumptech/glide
キャッシュファイルの名前の規則
DIRTY 用ファイルの名前: ${key}.${index}.tmp
CLEAN 用ファイルの名前: ${key}.${index}
glide/DiskLruCache.java at v4.11.0 · bumptech/glide
まとめ
DiskLruCache ではただ画像ファイルを保存しているだけではなく、キャッシュの操作ログも記録をしておくことでLRUとして動作することを可能にしていた。
ジャーナルファイルについてはデータベースの処理とも関連がありそうなので今後さらに調べていきたいです。