OSMDroid源码分析之瓦片载入基类:MapTileModuleProviderBase

来源:互联网 发布:python 设计模式 23种 编辑:程序博客网 时间:2024/05/29 18:08

源码

先来看看源码:

package org.osmdroid.tileprovider.modules;import java.util.HashMap;import java.util.Iterator;import java.util.LinkedHashMap;import java.util.Map;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.RejectedExecutionException;import org.osmdroid.tileprovider.ExpirableBitmapDrawable;import org.osmdroid.tileprovider.MapTile;import org.osmdroid.tileprovider.MapTileRequestState;import org.osmdroid.tileprovider.constants.OpenStreetMapTileProviderConstants;import org.osmdroid.tileprovider.tilesource.ITileSource;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import android.graphics.drawable.Drawable;/** * An abstract base class for modular tile providers * * @author Marc Kurtz * @author Neil Boyd */public abstract class MapTileModuleProviderBase implements OpenStreetMapTileProviderConstants {/** * Gets the human-friendly name assigned to this tile provider. * * @return the thread name */protected abstract String getName();/** * Gets the name assigned to the thread for this provider. * * @return the thread name */protected abstract String getThreadGroupName();/** * It is expected that the implementation will construct an internal member which internally * implements a {@link TileLoader}. This method is expected to return a that internal member to * methods of the parent methods. * * @return the internal member of this tile provider. */protected abstract Runnable getTileLoader();/** * Returns true if implementation uses a data connection, false otherwise. This value is used to * determine if this provider should be skipped if there is no data connection. * * @return true if implementation uses a data connection, false otherwise */public abstract boolean getUsesDataConnection();/** * Gets the minimum zoom level this tile provider can provide * * @return the minimum zoom level */public abstract int getMinimumZoomLevel();/** * Gets the maximum zoom level this tile provider can provide * * @return the maximum zoom level */public abstract int getMaximumZoomLevel();/** * Sets the tile source for this tile provider. * * @param tileSource *            the tile source */public abstract void setTileSource(ITileSource tileSource);private final ExecutorService mExecutor;private static final Logger logger = LoggerFactory.getLogger(MapTileModuleProviderBase.class);protected final Object mQueueLockObject = new Object();protected final HashMap<MapTile, MapTileRequestState> mWorking;protected final LinkedHashMap<MapTile, MapTileRequestState> mPending;public MapTileModuleProviderBase(int pThreadPoolSize, final int pPendingQueueSize) {if (pPendingQueueSize < pThreadPoolSize) {logger.warn("The pending queue size is smaller than the thread pool size. Automatically reducing the thread pool size.");pThreadPoolSize = pPendingQueueSize;}mExecutor = Executors.newFixedThreadPool(pThreadPoolSize,new ConfigurablePriorityThreadFactory(Thread.NORM_PRIORITY, getThreadGroupName()));mWorking = new HashMap<MapTile, MapTileRequestState>();mPending = new LinkedHashMap<MapTile, MapTileRequestState>(pPendingQueueSize + 2, 0.1f,true) {private static final long serialVersionUID = 6455337315681858866L;@Overrideprotected boolean removeEldestEntry(final Map.Entry<MapTile, MapTileRequestState> pEldest) {if (size() > pPendingQueueSize) {MapTile result = null;// get the oldest tile that isn't in the mWorking queueIterator<MapTile> iterator = mPending.keySet().iterator();while (result == null && iterator.hasNext()) {final MapTile tile = iterator.next();if (!mWorking.containsKey(tile)) {result = tile;}}if (result != null) {MapTileRequestState state = mPending.get(result);removeTileFromQueues(result);state.getCallback().mapTileRequestFailed(state);}}return false;}};}public void loadMapTileAsync(final MapTileRequestState pState) {synchronized (mQueueLockObject) {if (DEBUG_TILE_PROVIDERS) {logger.debug("MapTileModuleProviderBase.loadMaptileAsync() on provider: "+ getName() + " for tile: " + pState.getMapTile());if (mPending.containsKey(pState.getMapTile()))logger.debug("MapTileModuleProviderBase.loadMaptileAsync() tile already exists in request queue for modular provider. Moving to front of queue.");elselogger.debug("MapTileModuleProviderBase.loadMaptileAsync() adding tile to request queue for modular provider.");}// this will put the tile in the queue, or move it to the front of// the queue if it's already presentmPending.put(pState.getMapTile(), pState);}try {mExecutor.execute(getTileLoader());} catch (final RejectedExecutionException e) {logger.warn("RejectedExecutionException", e);}}private void clearQueue() {synchronized (mQueueLockObject) {mPending.clear();mWorking.clear();}}/** * Detach, we're shutting down - Stops all workers. */public void detach() {this.clearQueue();this.mExecutor.shutdown();}void removeTileFromQueues(final MapTile mapTile) {synchronized (mQueueLockObject) {if (DEBUG_TILE_PROVIDERS) {logger.debug("MapTileModuleProviderBase.removeTileFromQueues() on provider: "+ getName() + " for tile: " + mapTile);}mPending.remove(mapTile);mWorking.remove(mapTile);}}/** * Load the requested tile. An abstract internal class whose objects are used by worker threads * to acquire tiles from servers. It processes tiles from the 'pending' set to the 'working' set * as they become available. The key unimplemented method is 'loadTile'. */protected abstract class TileLoader implements Runnable {/** * Load the requested tile. * * @return the tile if it was loaded successfully, or null if failed to *         load and other tile providers need to be called * @param pState * @throws CantContinueException */protected abstract Drawable loadTile(MapTileRequestState pState)throws CantContinueException;protected void onTileLoaderInit() {// Do nothing by default}protected void onTileLoaderShutdown() {// Do nothing by default}protected MapTileRequestState nextTile() {synchronized (mQueueLockObject) {MapTile result = null;// get the most recently accessed tile// - the last item in the iterator that's not already being// processedIterator<MapTile> iterator = mPending.keySet().iterator();// TODO this iterates the whole list, make this faster...while (iterator.hasNext()) {final MapTile tile = iterator.next();if (!mWorking.containsKey(tile)) {if (DEBUG_TILE_PROVIDERS) {logger.debug("TileLoader.nextTile() on provider: " + getName()+ " found tile in working queue: " + tile);}result = tile;}}if (result != null) {if (DEBUG_TILE_PROVIDERS) {logger.debug("TileLoader.nextTile() on provider: " + getName()+ " adding tile to working queue: " + result);}mWorking.put(result, mPending.get(result));}return (result != null ? mPending.get(result) : null);}}/** * A tile has loaded. */protected void tileLoaded(final MapTileRequestState pState, final Drawable pDrawable) {if (DEBUG_TILE_PROVIDERS) {logger.debug("TileLoader.tileLoaded() on provider: " + getName() + " with tile: "+ pState.getMapTile());}removeTileFromQueues(pState.getMapTile());pState.getCallback().mapTileRequestCompleted(pState, pDrawable);}/** * A tile has loaded but it's expired. * Return it <b>and</b> send request to next provider. */protected void tileLoadedExpired(final MapTileRequestState pState, final Drawable pDrawable) {if (DEBUG_TILE_PROVIDERS) {logger.debug("TileLoader.tileLoadedExpired() on provider: " + getName()+ " with tile: " + pState.getMapTile());}removeTileFromQueues(pState.getMapTile());pState.getCallback().mapTileRequestExpiredTile(pState, pDrawable);}protected void tileLoadedFailed(final MapTileRequestState pState) {if (DEBUG_TILE_PROVIDERS) {logger.debug("TileLoader.tileLoadedFailed() on provider: " + getName()+ " with tile: " + pState.getMapTile());}removeTileFromQueues(pState.getMapTile());pState.getCallback().mapTileRequestFailed(pState);}/** * This is a functor class of type Runnable. The run method is the encapsulated function. */@Overridefinal public void run() {onTileLoaderInit();MapTileRequestState state;Drawable result = null;while ((state = nextTile()) != null) {if (DEBUG_TILE_PROVIDERS) {logger.debug("TileLoader.run() processing next tile: " + state.getMapTile());}try {result = null;result = loadTile(state);} catch (final CantContinueException e) {logger.info("Tile loader can't continue: " + state.getMapTile(), e);clearQueue();} catch (final Throwable e) {logger.error("Error downloading tile: " + state.getMapTile(), e);}if (result == null) {tileLoadedFailed(state);} else if (ExpirableBitmapDrawable.isDrawableExpired(result)) {tileLoadedExpired(state, result);} else {tileLoaded(state, result);}}onTileLoaderShutdown();}}/** * Thrown by a tile provider module in TileLoader.loadTile() to signal that it can no longer * function properly. This will typically clear the pending queue. */public class CantContinueException extends Exception {private static final long serialVersionUID = 146526524087765133L;public CantContinueException(final String pDetailMessage) {super(pDetailMessage);}public CantContinueException(final Throwable pThrowable) {super(pThrowable);}}}

这个类主要提供了一个异步加载TileModule的接口,并且这个类默认是提供了对异步加载的同步处理和加载线程队列的处理;

Demo

如果我们自己要实现自己的Tile加载类只需要实现getTileLoader和TileLoader的loadTile接口即可,下面写了个demo:

/* (non-Javadoc) * @see org.osmdroid.tileprovider.modules.MapTileModuleProviderBase#getTileLoader() */@Overrideprotected Runnable getTileLoader() {return new MapTileModuleProviderBase.TileLoader() {@Overrideprotected Drawable loadTile(MapTileRequestState pState) throws CantContinueException {String path =  pState.getMapTile().toString();Drawable drawable;try {drawable = getDrawable(path);// do it } catch (final LowMemoryException e) {// low memory so empty the queuethrow new CantContinueException(e);}if (drawable == null) {throw new CantContinueException("not map data!");}return drawable;}};}
如果自己管理加载队列需要修改的内容有点多,因为要实现相关UI处理接口等一系列接口(不推荐)。




















0 0
原创粉丝点击