地図アプリには主要なふたつのキャッシュがある。 一つは、レンダリングに使うOSMバイナリレコードブロックのキャッシュ、 もう一つは、地図表示用のタイルビットマップ画像のキャッシュである。
これまでは、この二つのキャッシュを異なる独自のプログラムで実装していた。
新地図アプリでは、LinkedHashMap を使った LRUキャッシュを使うことにより プログラムを大幅に簡略化する。
LinkedHashMapを使うと、キーによる検索は迅速である。 登録数が上限に達すると最後に使われたのが最も古い(Least Recentry)ものが削除される。
import java.util.LinkedHashMap; public class LRUCache<K,V> extends LinkedHashMap<K,V> { protected int limit; public LRUCache(int size) { super(size, 0.75F, true); // true: LRU, false: FIFO this.limit = size; } @Override protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) { return size() > limit; } }
スマホはパソコンに比べて極めて高精細である。パソコンではタイル画像は 256x256 画素であるが、 スマホではこれを縦横3倍にした 768x768画素とする。これにより、画面には最大で 3x4タイルが表示される。
8インチタブレットの場合は縦横1.5倍の 384x384画素タイルとしているので、スマホと同じく、画面には最大で 3x4タイルが表示される。
キャッシュ容量として 12 は必須であるが、zoom を上下させたとき、頻繁に総入れ替えするのはパフォーマンス上好ましくない。 従って、最低 20タイル分程度は確保した方がよい。現在は、30タイル分を確保している。 スマホでは 256x256x4x30x9 = 67.5MB、タブレットでは 256x256x4x30x2.25 = 16.9MBのメモリが使われる。
プログラムをシンプルにするため、画面表示でタイルがキャッシュになかった場合、その時点で、キャッシュにエントリしている。
ストレージにあれば、非同期に読み込みが行われる。ストレージになかった場合、国土地理院地図の場合、非同期処理の延長で ダウンロードが始まる。
ズーム切替で該当タイルが一切保存されていなかった場合、最大で12タイルのダウンロードがほぼ同時に発生する。
OSM地図の場合はレンダリングであり、メモリ使用量がおおきいため、同時に実行できるのは精々4~6タイル程度である。 このため、ダウンロードと同じ方法は採れない。
レンダリングはタイル表示とは独立している。キャッシュを順次検索してレンダリングを求めているものを探す。 レンダリングが終わったら、キャッシュ上のタイルの状態を ready に変える。また、タイル画像はストレージにも保存する。
レンダリングは複数(4~6)スレッドで行う。 一つのタイルのレンダリングについては、最初に、陸地ポリゴンの描画を行う。 使われるブロック(OSMバイナリレコード)は一つである。 次に、OSM地物のレンダリングを行う。 このとき、高ズームでは、二つのブロックから取り出したレコードをマージしてレンダリングを行う。 中低ズームでは一つのブロックでレンダリングを行う。
複数のスレッドがそれぞれ異なるタイルのレンダリングを担当しているが、大抵の場合、同じブロックデータが使われる。 中ズームでは、異なるブロックデータとなることもある。
japan-high zoom 8 の最大ブロックサイズは 31.5MB、zoom 12の最大は 17.3MB である。 キャッシュは最大10ブロック程度でもいいかも知れないが、現在は 20 としている。
メモリ使用量は一定ではない。地図アプリでは画面左下にメモリ使用量を常時表示しているが、 そこに内訳として、ブロックキャッシュのメモリ使用量を表示する。
起動時に皇居を中心とした地図のレンダリングを行った場合、ブロックキャッシュに86MB使用された。