package com.androidol.util.tiles; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import android.graphics.Bitmap; import com.androidol.basetypes.Pixel; import com.androidol.constants.UtilConstants; import com.androidol.tile.Tile; import com.androidol.util.Util; public class Tile2DArrayCache implements UtilConstants { private static final long serialVersionUID = 1L; private final int dimension; private Bitmap[][] bitmapMatrix; private HashMap<String, Pixel> bitmapCellMatrix; /** * Constructor */ public Tile2DArrayCache(final int dimension) { // TODO: determine minimum dimension size based on tile size // TODO: take into account that different layers may have different tile sizes this.dimension = Math.min(Math.max(MIN_MEMORY_CACHE_DIM, dimension), MAX_MEMORY_CACHE_DIM); this.bitmapMatrix = new Bitmap[dimension][dimension]; this.bitmapCellMatrix = new HashMap<String, Pixel>(); for(int i=0; i<dimension; i++) { for(int j=0; j<dimension; j++) { this.bitmapMatrix[i][j] = null; } } } /** * API Method: clear */ public synchronized void clear() { for(int i=0; i<this.dimension; i++) { for(int j=0; j<this.dimension; j++) { if(this.bitmapMatrix[i][j]!=null && this.bitmapMatrix[i][j].isRecycled()==false) { this.bitmapMatrix[i][j].recycle(); } this.bitmapMatrix[i][j] = null; } } } /** * API Method: put * * @param url * @param bitmap * @return {Bitmap} */ public synchronized Bitmap put(final String url, final Bitmap bitmap, final Tile tile) { Pixel cell = tile.getCell(); removeStaledLinks(cell); this.bitmapCellMatrix.put(url, cell); int row = (int)cell.getX(); int col = (int)cell.getY(); if(this.bitmapMatrix[row][col]!=null && this.bitmapMatrix[row][col].isRecycled()==false) { this.bitmapMatrix[row][col].recycle(); this.bitmapMatrix[row][col] = null; } this.bitmapMatrix[row][col] = bitmap; //calculateNumOfCachedTiles(); return bitmap; } /** * API Method: get * * @param url * @return {Bitmap} */ public synchronized Bitmap get(final String url) { if(this.bitmapCellMatrix.get(url) == null) { return null; }; Pixel cell = this.bitmapCellMatrix.get(url); int row = (int)cell.getX(); int col = (int)cell.getY(); Bitmap bitmap = null; try { bitmap = this.bitmapMatrix[row][col]; if(bitmap== null || bitmap.isRecycled() == true) { bitmap = null; } } catch(IndexOutOfBoundsException e) { Util.printDebugMessage(" ...Tile2DArrayCache...try to get a cached tile at (" + row + "," + col + ") which doesn't exist..."); return null; } //Util.printDebugMessage(" ...get tile: " + url + "...at (" + row + "," + col + ")..."); return bitmap; } /** * * @param url * @return */ public synchronized Bitmap get(final int row, final int col) { Bitmap bitmap = null; try { bitmap = this.bitmapMatrix[row][col]; if(bitmap== null || bitmap.isRecycled() == true) { bitmap = null; } } catch(IndexOutOfBoundsException e) { Util.printDebugMessage(" ...Tile2DArrayCache...try to get a cached tile at (" + row + "," + col + ") which doesn't exist..."); return null; } //Util.printDebugMessage(" ...get tile: " + url + "...at (" + row + "," + col + ")..."); return bitmap; } /** * API Method: remove * * @param url * @return {Bitmap} */ public synchronized Bitmap remove(final String url) { if(this.bitmapCellMatrix.get(url)==null) { return null; }; Pixel cell = this.bitmapCellMatrix.get(url); int row = (int)cell.getX(); int col = (int)cell.getY(); Bitmap bitmap = null; try { if(this.bitmapMatrix[row][col]!=null && this.bitmapMatrix[row][col].isRecycled()==false) { this.bitmapMatrix[row][col].recycle(); this.bitmapMatrix[row][col] = null; } } catch(IndexOutOfBoundsException e) { Util.printDebugMessage(" ...Tile2DArrayCache...try to remove a cached tile which doesn't exist..."); return null; } finally { this.bitmapCellMatrix.remove(url); } //calculateNumOfCachedTiles(); //Util.printDebugMessage(" ...get tile: " + url + "...at (" + row + "," + col + ")..."); return bitmap; } /** * private method: calculateNumOfCachedTiles * print out number of cached tiles */ /* private void calculateNumOfCachedTiles() { int numOfCachedTiles = 0; for(int i=0; i<dimension; i++) { for(int j=0; j<dimension; j++) { if(this.bitmapMatrix[i][j]!=null && this.bitmapMatrix[i][j].isRecycled()==false) { numOfCachedTiles++; } } } Util.printDebugMessage(" ...number of bitmap cached: " + numOfCachedTiles + "..."); } */ /** * private method: removeStaledLinks * * @param cell {Pixel} * @return */ private void removeStaledLinks(Pixel cell) { Iterator<String> i = (Iterator<String>)this.bitmapCellMatrix.keySet().iterator(); synchronized(this.bitmapCellMatrix) { while(i.hasNext()) { String url = i.next(); Pixel staledCell = this.bitmapCellMatrix.get(url); if(((int)staledCell.getX() == (int)cell.getX()) && ((int)staledCell.getY() == (int)cell.getY())) { /* * Do NOT remove from HashMap directly, which causes ConcurrentModificationException exception * when multi-thread are trying to iterating and modifiying the hash map at the same time */ // DO NOT DO THIS //this.bitmapCellMatrix.remove(url); /* * Instead remove it from iterator */ i.remove(); } } } } }