現在、最大64タイルをメモリ上にキャッシュしている。できるだけメモリの獲得、解放をやめて再利用する。 地図アプリ起動時に全タイル分のビットマップメモリを獲得して置き、必要に応じて、中身だけを書き換える。
タイルの Status は、free(空き、未使用)か、wait(レンダリングやダウンロードなどをリクエスト中)、 busy(レンダリングやダウンロードなどを実行中)、ready(ビットマップが利用可能)のいずれかである。
enum Status { free, waiting, busy, ready }
タイルは Tile.Source src, int zoom, int x, int y で識別できるが、 次のように、全体を一つの 8バイト整数で表す。
long key(Tile.Source src, int zoom, int x, int y) {
return (((long) src.ordinal())<<56) + (((long)zoom)<<48) + (((long)x)<<24) + y;
}
キャッシュ配列をサイクリックバッファ(FIFO)として使用している。 検索は当初 HashMap を使っていたが、パフォーマンス上問題はないため、順次検索に変更した。
また、更新リクエストは以前は ConcurrentLinkedQueue で管理していたが、これをやめ、 キャッシュ配列の順次検索に変えた。
パフォーマンスに拘り現在のプログラムは少々複雑にしすぎた。 一度、原点に戻した方がよいかも知れない。
タイルにあって ready だけを表示、タイルにないものだけを リクエストキューにいれればよい。 タイルにあって、waiting や busy のものは何もしない。
busy から ready に変わったときに、invalidate() が発行され、onDraw が実行される。 この場合、inavlidate()発行から onDrawが実行されるまでに時間がかかる。
このため、現在のプログラムでは、onDraw で発行したレンダリングリクエストの処理が完了するまで、 onDraw の中で待っている。
onDrawの時間は短くなければならないが、レンダリングが直ちに終わるならば、少しの待ちは許される。
protected void onDraw(Canvas canvas) {
// 省略
int numRequests = 0;
long sz48 = src_zoom48(src, zoom);
for (int xx = bx; xx < ex; xx++) {
int x = (xx + MAX) % MAX;
for (int y = by; y < ey; y++) {
long key = key(sz48, x, y);
int ix = Tile.get(key);
if (ix < 0) { // キャッシュにない
ix = Tile.alloc(key); // 描きこむ場所を決める
Tile tile = Tile.cache[ix]; // データをセットするタイル
tile.set(src, zoom, x, y, key);
RenderThread.requests.add(tile);
numRequests++;
} else { // キャッシュにある
Tile tile = Tile.cache[ix];
if (tile.status == Tile.Status.ready) {
int tx = (tile.x + MAX) % MAX;
drawBitmap(canvas, tile.bmp, PX * (tx - bx) - offX, PX * (tile.y - by) - offY);
} else if (tile.status == Tile.Status.busy) {
RenderThread.responses.add(tile);
//System.out.printf("onDraw status=busy, path=%s\n", tile.path);
} else {
System.out.printf("onDraw error: status=%s, path=%s\n",
tile.status.name(), tile.path);
}
}
}
}
// 後処理
}
【プログラム・ソースコード】
Map.java
Tile.java