diff --git a/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/bitmap/BitmapTileLayer.java b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/bitmap/BitmapTileLayer.java
index e8d35b4f..3593e4ed 100644
--- a/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/bitmap/BitmapTileLayer.java
+++ b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/bitmap/BitmapTileLayer.java
@@ -19,8 +19,6 @@ import java.net.URL;
 import org.oscim.backend.canvas.Bitmap;
 import org.oscim.core.MapPosition;
 import org.oscim.core.Tile;
-import org.oscim.event.MapEvent;
-import org.oscim.event.UpdateEvent;
 import org.oscim.gdx.client.GwtBitmap;
 import org.oscim.layers.tile.TileLayer;
 import org.oscim.layers.tile.bitmap.TileSource.FadeStep;
@@ -50,66 +48,33 @@ public class BitmapTileLayer extends TileLayer<TileLoader> {
 		mFade = mTileSource.getFadeSteps();
 	}
 
-	//	@Override
-	//	public void onUpdate(MapPosition pos, boolean changed, boolean clear) {
-	//		super.onUpdate(pos, changed, clear);
-	//
-	//		if (mFade == null) {
-	//			mRenderLayer.setBitmapAlpha(1);
-	//			return;
-	//		}
-	//
-	//		float alpha = 0;
-	//		for (FadeStep f : mFade) {
-	//			if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd)
-	//				continue;
-	//
-	//			if (f.alphaStart == f.alphaEnd) {
-	//				alpha = f.alphaStart;
-	//				break;
-	//			}
-	//			double range = f.scaleEnd / f.scaleStart;
-	//			float a = (float)((range - (pos.scale / f.scaleStart)) / range);
-	//			a = FastMath.clamp(a, 0, 1);
-	//			// interpolate alpha between start and end
-	//			alpha = a * f.alphaStart + (1 - a) * f.alphaEnd;
-	//			break;
-	//		}
-	//
-	//		mRenderLayer.setBitmapAlpha(alpha);
-	//	}
-
 	@Override
-	public void handleEvent(MapEvent event) {
-		super.handleEvent(event);
+	public void onMapUpdate(MapPosition pos, boolean changed, boolean clear) {
+		super.onMapUpdate(pos, changed, clear);
 
-		if (event instanceof UpdateEvent) {
+		if (mFade == null) {
+			mRenderLayer.setBitmapAlpha(1);
+			return;
+		}
 
-			if (mFade == null) {
-				mRenderLayer.setBitmapAlpha(1);
-				return;
-			}
-			MapPosition pos = mMap.getMapPosition();
+		float alpha = 0;
+		for (FadeStep f : mFade) {
+			if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd)
+				continue;
 
-			float alpha = 0;
-			for (FadeStep f : mFade) {
-				if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd)
-					continue;
-
-				if (f.alphaStart == f.alphaEnd) {
-					alpha = f.alphaStart;
-					break;
-				}
-				double range = f.scaleEnd / f.scaleStart;
-				float a = (float) ((range - (pos.scale / f.scaleStart)) / range);
-				a = FastMath.clamp(a, 0, 1);
-				// interpolate alpha between start and end
-				alpha = a * f.alphaStart + (1 - a) * f.alphaEnd;
+			if (f.alphaStart == f.alphaEnd) {
+				alpha = f.alphaStart;
 				break;
 			}
-
-			mRenderLayer.setBitmapAlpha(alpha);
+			double range = f.scaleEnd / f.scaleStart;
+			float a = (float) ((range - (pos.scale / f.scaleStart)) / range);
+			a = FastMath.clamp(a, 0, 1);
+			// interpolate alpha between start and end
+			alpha = a * f.alphaStart + (1 - a) * f.alphaEnd;
+			break;
 		}
+
+		mRenderLayer.setBitmapAlpha(alpha);
 	}
 
 	@Override
diff --git a/vtm-gdx/src/org/oscim/gdx/GdxMap.java b/vtm-gdx/src/org/oscim/gdx/GdxMap.java
index a3db1e53..683f771a 100644
--- a/vtm-gdx/src/org/oscim/gdx/GdxMap.java
+++ b/vtm-gdx/src/org/oscim/gdx/GdxMap.java
@@ -82,6 +82,33 @@ public abstract class GdxMap implements ApplicationListener {
 				}, delay / 1000f);
 				return true;
 			}
+
+			/**
+			 * Update all Layers on Main thread.
+			 *
+			 * @param forceRedraw
+			 *            also render frame FIXME (does nothing atm)
+			 */
+			private void redrawMapInternal(boolean forceRedraw) {
+				// FIXME needed?
+				GLState.blend(false);
+				GLState.test(false, false);
+
+				updateLayers();
+
+				mRenderRequest = true;
+				Gdx.graphics.requestRendering();
+			}
+
+			/* private */boolean mWaitRedraw;
+			private final Runnable mRedrawRequest = new Runnable() {
+				@Override
+				public void run() {
+					mWaitRedraw = false;
+					redrawMapInternal(false);
+				}
+			};
+
 		};
 
 		mMapRenderer = new MapRenderer(mMap);
@@ -200,30 +227,6 @@ public abstract class GdxMap implements ApplicationListener {
 	public void resume() {
 	}
 
-	/**
-	 * Update all Layers on Main thread.
-	 *
-	 * @param forceRedraw
-	 *            also render frame FIXME (does nothing atm)
-	 */
-	void redrawMapInternal(boolean forceRedraw) {
-		GLState.blend(false);
-		GLState.test(false, false);
-
-		mMap.updateLayers();
-
-		mRenderRequest = true;
-		Gdx.graphics.requestRendering();
-	}
-
-	/* private */boolean mWaitRedraw;
-	private final Runnable mRedrawRequest = new Runnable() {
-		@Override
-		public void run() {
-			mWaitRedraw = false;
-			redrawMapInternal(false);
-		}
-	};
 
 	class TouchHandler implements InputProcessor {
 
diff --git a/vtm/src/org/oscim/event/Dispatcher.java b/vtm/src/org/oscim/event/Dispatcher.java
new file mode 100644
index 00000000..7fc5012f
--- /dev/null
+++ b/vtm/src/org/oscim/event/Dispatcher.java
@@ -0,0 +1,19 @@
+package org.oscim.event;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class Dispatcher<T> {
+	protected List<T> listeners = new ArrayList<T>();
+
+	public void addListener(T l) {
+		if (!listeners.contains(l))
+			listeners.add(l);
+	}
+
+	public void removeListener(T l) {
+		listeners.remove(l);
+	}
+
+	public abstract void dispatch();
+}
diff --git a/vtm/src/org/oscim/event/IListener.java b/vtm/src/org/oscim/event/IListener.java
new file mode 100644
index 00000000..55b6ffd7
--- /dev/null
+++ b/vtm/src/org/oscim/event/IListener.java
@@ -0,0 +1,5 @@
+package org.oscim.event;
+
+public interface IListener {
+
+}
diff --git a/vtm/src/org/oscim/event/UpdateEvent.java b/vtm/src/org/oscim/event/UpdateEvent.java
deleted file mode 100644
index 1a650b11..00000000
--- a/vtm/src/org/oscim/event/UpdateEvent.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.oscim.event;
-
-
-public class UpdateEvent extends MapEvent {
-
-	private static final long serialVersionUID = 1L;
-	public static final String TYPE = "UpdateEvent";
-
-	public UpdateEvent(Object source) {
-		super(source);
-	}
-
-	public boolean positionChanged;
-	public boolean clearMap;
-
-}
diff --git a/vtm/src/org/oscim/layers/tile/TileLayer.java b/vtm/src/org/oscim/layers/tile/TileLayer.java
index 23a72a2a..ebbedac5 100644
--- a/vtm/src/org/oscim/layers/tile/TileLayer.java
+++ b/vtm/src/org/oscim/layers/tile/TileLayer.java
@@ -16,16 +16,14 @@ package org.oscim.layers.tile;
 
 import java.util.ArrayList;
 
-import org.oscim.event.EventListener;
-import org.oscim.event.MapEvent;
-import org.oscim.event.UpdateEvent;
+import org.oscim.core.MapPosition;
 import org.oscim.layers.Layer;
 import org.oscim.map.Map;
 import org.oscim.tiling.TileLoader;
 import org.oscim.tiling.TileManager;
 import org.oscim.tiling.TileRenderer;
 
-public abstract class TileLayer<T extends TileLoader> extends Layer implements EventListener {
+public abstract class TileLayer<T extends TileLoader> extends Layer implements Map.UpdateListener {
 	//private final static String TAG = TileLayer.class.getName();
 	private final static int MAX_ZOOMLEVEL = 17;
 	private final static int MIN_ZOOMLEVEL = 2;
@@ -61,8 +59,6 @@ public abstract class TileLayer<T extends TileLoader> extends Layer implements E
 		// RenderLayer is working in GL Thread and actually
 		// drawing loaded tiles to screen.
 		mRenderer = mRenderLayer = new TileRenderer(mTileManager);
-
-		map.addListener(UpdateEvent.TYPE, this);
 	}
 
 	abstract protected T createLoader(TileManager tm);
@@ -71,55 +67,32 @@ public abstract class TileLayer<T extends TileLoader> extends Layer implements E
 		return (TileRenderer) mRenderer;
 	}
 
-	//	@Override
-	//	public void onUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
-	//
-	//		if (clear || mInitial) {
-	//			mRenderLayer.clearTiles();
-	//			mTileManager.init(mInitial);
-	//			mInitial = false;
-	//			changed = true;
-	//		}
-	//
-	//		if (changed && mTileManager.update(mapPosition))
-	//			notifyLoaders();
-	//	}
-
 	@Override
-	public void handleEvent(MapEvent event) {
-		if (event instanceof UpdateEvent) {
-
-			UpdateEvent e = (UpdateEvent) event;
-
-			boolean changed = e.positionChanged;
-
-			if (e.clearMap || mInitial) {
-				mRenderLayer.clearTiles();
-				mTileManager.init(mInitial);
-				mInitial = false;
-				changed = true;
-			}
-
-			if (changed && mTileManager.update(mMap.getMapPosition()))
-				notifyLoaders();
+	public void onMapUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
+		if (clear || mInitial) {
+			mRenderLayer.clearTiles();
+			mTileManager.init(mInitial);
+			mInitial = false;
+			changed = true;
 		}
+
+		if (changed && mTileManager.update(mapPosition))
+			notifyLoaders();
 	}
 
 	@Override
 	public void onDetach() {
-		mMap.removeListener(UpdateEvent.TYPE, this);
-
 		for (T loader : mTileLoader) {
 			loader.pause();
 			loader.interrupt();
 			loader.cleanup();
 
-			//			try {
-			//				tileWorker.join(10000);
-			//			} catch (InterruptedException e) {
-			//				// restore the interrupted status
-			//				Thread.currentThread().interrupt();
-			//			}
+			//try {
+			//	tileWorker.join(10000);
+			//} catch (InterruptedException e) {
+			//	// restore the interrupted status
+			//	Thread.currentThread().interrupt();
+			//}
 		}
 		mTileManager.destroy();
 	}
diff --git a/vtm/src/org/oscim/layers/tile/bitmap/BitmapTileLayer.java b/vtm/src/org/oscim/layers/tile/bitmap/BitmapTileLayer.java
index cf843773..58214650 100644
--- a/vtm/src/org/oscim/layers/tile/bitmap/BitmapTileLayer.java
+++ b/vtm/src/org/oscim/layers/tile/bitmap/BitmapTileLayer.java
@@ -24,8 +24,6 @@ import org.oscim.backend.CanvasAdapter;
 import org.oscim.backend.canvas.Bitmap;
 import org.oscim.core.MapPosition;
 import org.oscim.core.Tile;
-import org.oscim.event.MapEvent;
-import org.oscim.event.UpdateEvent;
 import org.oscim.layers.tile.TileLayer;
 import org.oscim.layers.tile.bitmap.TileSource.FadeStep;
 import org.oscim.map.Map;
@@ -53,66 +51,33 @@ public class BitmapTileLayer extends TileLayer<TileLoader>  {
 	}
 
 
-//	@Override
-//	public void onUpdate(MapPosition pos, boolean changed, boolean clear) {
-//		super.onUpdate(pos, changed, clear);
-//
-//		if (mFade == null) {
-//			mRenderLayer.setBitmapAlpha(1);
-//			return;
-//		}
-//
-//		float alpha = 0;
-//		for (FadeStep f : mFade) {
-//			if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd)
-//				continue;
-//
-//			if (f.alphaStart == f.alphaEnd) {
-//				alpha = f.alphaStart;
-//				break;
-//			}
-//			double range = f.scaleEnd / f.scaleStart;
-//			float a = (float)((range - (pos.scale / f.scaleStart)) / range);
-//			a = FastMath.clamp(a, 0, 1);
-//			// interpolate alpha between start and end
-//			alpha = a * f.alphaStart + (1 - a) * f.alphaEnd;
-//			break;
-//		}
-//
-//		mRenderLayer.setBitmapAlpha(alpha);
-//	}
-
 	@Override
-	public void handleEvent(MapEvent event) {
-		super.handleEvent(event);
+	public void onMapUpdate(MapPosition pos, boolean changed, boolean clear) {
+		super.onMapUpdate(pos, changed, clear);
 
-		if (event instanceof UpdateEvent){
+		if (mFade == null) {
+			mRenderLayer.setBitmapAlpha(1);
+			return;
+		}
 
-			if (mFade == null) {
-				mRenderLayer.setBitmapAlpha(1);
-				return;
-			}
-			MapPosition pos = mMap.getMapPosition();
+		float alpha = 0;
+		for (FadeStep f : mFade) {
+			if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd)
+				continue;
 
-			float alpha = 0;
-			for (FadeStep f : mFade) {
-				if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd)
-					continue;
-
-				if (f.alphaStart == f.alphaEnd) {
-					alpha = f.alphaStart;
-					break;
-				}
-				double range = f.scaleEnd / f.scaleStart;
-				float a = (float)((range - (pos.scale / f.scaleStart)) / range);
-				a = FastMath.clamp(a, 0, 1);
-				// interpolate alpha between start and end
-				alpha = a * f.alphaStart + (1 - a) * f.alphaEnd;
+			if (f.alphaStart == f.alphaEnd) {
+				alpha = f.alphaStart;
 				break;
 			}
-
-			mRenderLayer.setBitmapAlpha(alpha);
+			double range = f.scaleEnd / f.scaleStart;
+			float a = (float)((range - (pos.scale / f.scaleStart)) / range);
+			a = FastMath.clamp(a, 0, 1);
+			// interpolate alpha between start and end
+			alpha = a * f.alphaStart + (1 - a) * f.alphaEnd;
+			break;
 		}
+
+		mRenderLayer.setBitmapAlpha(alpha);
 	}
 
 	@Override
diff --git a/vtm/src/org/oscim/layers/tile/vector/labeling/LabelLayer.java b/vtm/src/org/oscim/layers/tile/vector/labeling/LabelLayer.java
index 5a492c67..37741a30 100644
--- a/vtm/src/org/oscim/layers/tile/vector/labeling/LabelLayer.java
+++ b/vtm/src/org/oscim/layers/tile/vector/labeling/LabelLayer.java
@@ -15,15 +15,15 @@
 package org.oscim.layers.tile.vector.labeling;
 
 import org.oscim.backend.Log;
+import org.oscim.core.MapPosition;
 import org.oscim.event.EventListener;
 import org.oscim.event.MapEvent;
 import org.oscim.event.MotionEvent;
-import org.oscim.event.UpdateEvent;
 import org.oscim.layers.Layer;
 import org.oscim.map.Map;
 import org.oscim.tiling.TileRenderer;
 
-public class LabelLayer extends Layer implements EventListener {
+public class LabelLayer extends Layer implements EventListener, Map.UpdateListener {
 	private final static String TAG = LabelLayer.class.getName();
 	private final TextRenderer mTextRenderer;
 
@@ -31,7 +31,7 @@ public class LabelLayer extends Layer implements EventListener {
 
 	public LabelLayer(Map map, TileRenderer tileRenderLayer) {
 		super(map);
-		map.addListener(UpdateEvent.TYPE, this);
+
 		map.addListener(MotionEvent.TYPE, this);
 
 		//mTextLayer = new org.oscim.renderer.layers.TextRenderLayer(map, tileRenderLayer);
@@ -41,7 +41,6 @@ public class LabelLayer extends Layer implements EventListener {
 
 	@Override
 	public void onDetach() {
-		mMap.removeListener(UpdateEvent.TYPE, this);
 		mMap.removeListener(MotionEvent.TYPE, this);
 
 		// TODO stop and clear labeling thread
@@ -50,13 +49,7 @@ public class LabelLayer extends Layer implements EventListener {
 
 	@Override
 	public void handleEvent(MapEvent event) {
-		if (event instanceof UpdateEvent) {
-
-			UpdateEvent e = (UpdateEvent) event;
-			if (e.clearMap)
-				mTextRenderer.clearLabels();
-
-		} else if (event instanceof MotionEvent) {
+		 if (event instanceof MotionEvent) {
 			MotionEvent e = (MotionEvent) event;
 
 			int action = e.getAction() & MotionEvent.ACTION_MASK;
@@ -75,11 +68,11 @@ public class LabelLayer extends Layer implements EventListener {
 		}
 	}
 
-	//	@Override
-	//	public void onUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
-	//		if (clear)
-	//			mTextRenderer.clearLabels();
-	//	}
+		@Override
+		public void onMapUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
+			if (clear)
+				mTextRenderer.clearLabels();
+		}
 
 	//	@Override
 	//	public boolean onTouchEvent(MotionEvent e) {
diff --git a/vtm/src/org/oscim/map/Layers.java b/vtm/src/org/oscim/map/Layers.java
deleted file mode 100644
index 00bee1d5..00000000
--- a/vtm/src/org/oscim/map/Layers.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2012 osmdroid authors
- * Copyright 2013 Hannes Janetzek
- *
- * This program is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
- * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.oscim.map;
-
-import java.util.AbstractList;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-import org.oscim.layers.Layer;
-import org.oscim.renderer.LayerRenderer;
-
-public class Layers extends AbstractList<Layer> {
-	//private final static String TAG = Layers.class.getName();
-
-	private final CopyOnWriteArrayList<Layer> mLayerList;
-
-	Layers() {
-		mLayerList = new CopyOnWriteArrayList<Layer>();
-	}
-
-	@Override
-	public synchronized Layer get(int index) {
-		return mLayerList.get(index);
-	}
-
-	@Override
-	public synchronized int size() {
-		return mLayerList.size();
-	}
-
-	@Override
-	public synchronized void add(int index, Layer element) {
-		mLayerList.add(index, element);
-		mDirtyLayers = true;
-	}
-
-	@Override
-	public synchronized Layer remove(int index) {
-		mDirtyLayers = true;
-		return mLayerList.remove(index);
-	}
-
-	@Override
-	public synchronized Layer set(int index, Layer element) {
-		mDirtyLayers = true;
-		return mLayerList.set(index, element);
-	}
-
-	private boolean mDirtyLayers;
-	private LayerRenderer[] mLayerRenderer;
-
-	public LayerRenderer[] getLayerRenderer() {
-		if (mDirtyLayers)
-			updateLayers();
-
-		return mLayerRenderer;
-	}
-
-	public void destroy() {
-		if (mDirtyLayers)
-			updateLayers();
-
-		for (Layer o : mLayers)
-			o.onDetach();
-	}
-
-	Layer[] mLayers;
-
-	private synchronized void updateLayers() {
-		if (!mDirtyLayers)
-			return;
-
-		mLayers = new Layer[mLayerList.size()];
-
-		int numRenderLayers = 0;
-
-		for (int i = 0, n = mLayerList.size(); i < n; i++) {
-			Layer o = mLayerList.get(i);
-
-			if (o.getRenderer() != null)
-				numRenderLayers++;
-			mLayers[i] = o;
-		}
-
-		mLayerRenderer = new LayerRenderer[numRenderLayers];
-
-		for (int i = 0, cntR = 0, n = mLayerList.size(); i < n; i++) {
-			Layer o = mLayerList.get(i);
-			LayerRenderer l = o.getRenderer();
-			if (l != null)
-				mLayerRenderer[cntR++] = l;
-		}
-
-		mDirtyLayers = false;
-	}
-}
diff --git a/vtm/src/org/oscim/map/Map.java b/vtm/src/org/oscim/map/Map.java
index ad28d35e..b67c55b3 100644
--- a/vtm/src/org/oscim/map/Map.java
+++ b/vtm/src/org/oscim/map/Map.java
@@ -14,20 +14,24 @@
  */
 package org.oscim.map;
 
+import java.util.AbstractList;
 import java.util.ArrayList;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import org.oscim.backend.Log;
 import org.oscim.core.BoundingBox;
-import org.oscim.core.GeoPoint;
 import org.oscim.core.MapPosition;
+import org.oscim.core.MercatorProjection;
+import org.oscim.event.Dispatcher;
 import org.oscim.event.EventDispatcher;
 import org.oscim.event.EventListener;
+import org.oscim.event.IListener;
 import org.oscim.event.MotionEvent;
-import org.oscim.event.UpdateEvent;
+import org.oscim.layers.Layer;
 import org.oscim.layers.MapEventLayer;
 import org.oscim.layers.tile.bitmap.BitmapTileLayer;
 import org.oscim.layers.tile.vector.VectorTileLayer;
-import org.oscim.layers.tile.vector.VectorTileLoader;
+import org.oscim.renderer.LayerRenderer;
 import org.oscim.renderer.MapRenderer;
 import org.oscim.theme.IRenderTheme;
 import org.oscim.theme.InternalRenderTheme;
@@ -94,16 +98,21 @@ public abstract class Map implements EventDispatcher {
 
 	private InternalRenderTheme mCurrentTheme;
 
+	/**
+	 * Utility function to set theme of base vector-layer and
+	 * use map background color from theme.
+	 */
 	public void setTheme(InternalRenderTheme theme) {
 		if (mBaseLayer == null) {
 			Log.e(TAG, "No base layer set");
 			throw new IllegalStateException();
 		}
 
-		if (mCurrentTheme == theme){
+		if (mCurrentTheme == theme) {
 			Log.d(TAG, "same theme: " + theme);
 			return;
 		}
+
 		IRenderTheme t = ThemeLoader.load(theme);
 		if (t == null) {
 			Log.e(TAG, "Invalid theme");
@@ -126,7 +135,7 @@ public abstract class Map implements EventDispatcher {
 	 * Request call to onUpdate for all layers. This function can
 	 * be called from any thread. Request will be handled on main
 	 * thread.
-	 *
+	 * 
 	 * @param forceRedraw pass true to render next frame
 	 */
 	public abstract void updateMap(boolean forceRedraw);
@@ -151,16 +160,21 @@ public abstract class Map implements EventDispatcher {
 	/**
 	 * Post a task to run on a shared worker-thread. Only use for
 	 * tasks running less than a second!
-	 * */
-	public void addTask(Runnable task){
+	 */
+	public void addTask(Runnable task) {
 		mAsyncExecutor.post(task);
 	}
 
+	/**
+	 * Return screen width in pixel.
+	 */
 	public abstract int getWidth();
 
+	/**
+	 * Return screen height in pixel.
+	 */
 	public abstract int getHeight();
 
-
 	/**
 	 * Request to clear all layers before rendering next frame
 	 */
@@ -169,27 +183,9 @@ public abstract class Map implements EventDispatcher {
 	}
 
 	/**
-	 * Do not call directly! This function is run on main-loop
-	 * before rendering a frame.
+	 * This function is run on main-loop before rendering a frame.
+	 * Caution: Do not call directly!
 	 */
-	public void updateLayers() {
-		boolean changed = false;
-
-		// get the current MapPosition
-		changed |= mViewport.getMapPosition(mMapPosition);
-
-		//mLayers.onUpdate(mMapPosition, changed, mClearMap);
-
-		UpdateEvent e = new UpdateEvent(this);
-		e.clearMap = mClearMap;
-		e.positionChanged = changed;
-
-		for (EventListener l : mUpdateListeners)
-			l.handleEvent(e);
-
-		mClearMap = false;
-	}
-
 	public void setDebugSettings(DebugSettings debugSettings) {
 		mDebugSettings = debugSettings;
 		//MapTileLoader.setDebugSettings(debugSettings);
@@ -197,6 +193,9 @@ public abstract class Map implements EventDispatcher {
 
 	public DebugSettings getDebugSettings() {
 		return mDebugSettings;
+
+	protected void updateLayers() {
+		mUpdateDispatcher.dispatch();
 	}
 
 	public void setMapPosition(MapPosition mapPosition) {
@@ -242,32 +241,169 @@ public abstract class Map implements EventDispatcher {
 		return mAnimator;
 	}
 
-
-	ArrayList<EventListener> mUpdateListeners = new ArrayList<EventListener>();
 	ArrayList<EventListener> mMotionListeners = new ArrayList<EventListener>();
 
 	@Override
 	public void addListener(String type, EventListener listener) {
-		if (type == UpdateEvent.TYPE)
-			mUpdateListeners.add(listener);
-		else if (type == MotionEvent.TYPE)
+		if (type == MotionEvent.TYPE)
 			mMotionListeners.add(listener);
-
 	}
+
 	@Override
 	public void removeListener(String type, EventListener listener) {
-		if (type == UpdateEvent.TYPE)
-			mUpdateListeners.remove(listener);
-		else if (type == MotionEvent.TYPE)
+		if (type == MotionEvent.TYPE)
 			mMotionListeners.remove(listener);
 	}
 
-	public MapPosition getMapPosition() {
-		return mMapPosition;
-	}
-
 	public void handleMotionEvent(MotionEvent e) {
 		for (EventListener l : mMotionListeners)
 			l.handleEvent(e);
 	}
+
+	/**
+	 * Listener interface for map update notifications. 
+	 * 
+	 * NOTE: Layers implementing this interface they will be automatically 
+	 * registered when the layer is added to the map and unresitered when
+	 * the layer is removed.
+	 */
+	public interface UpdateListener extends IListener {
+		void onMapUpdate(MapPosition mapPosition, boolean positionChanged, boolean clear);
+	}
+
+	private class UpdateDispatcher extends Dispatcher<UpdateListener> {
+		@Override
+		public void dispatch() {
+			boolean changed = false;
+
+			// get the current MapPosition
+			changed |= mViewport.getMapPosition(mMapPosition);
+
+			for (UpdateListener l : listeners)
+				l.onMapUpdate(mMapPosition, changed, mClearMap);
+
+			mClearMap = false;
+		}
+	}
+
+	private final UpdateDispatcher mUpdateDispatcher = new UpdateDispatcher();
+
+	/**
+	 * Register UpdateListener
+	 */
+	public void addUpdateListener(UpdateListener l) {
+		mUpdateDispatcher.addListener(l);
+	}
+
+	/**
+	 * Unregister UpdateListener
+	 */
+	public void removeUpdateListener(UpdateListener l) {
+		mUpdateDispatcher.removeListener(l);
+	}
+
+	public final class Layers extends AbstractList<Layer> {
+
+		private final CopyOnWriteArrayList<Layer> mLayerList;
+
+		Layers() {
+			mLayerList = new CopyOnWriteArrayList<Layer>();
+		}
+
+		@Override
+		public synchronized Layer get(int index) {
+			return mLayerList.get(index);
+		}
+
+		@Override
+		public synchronized int size() {
+			return mLayerList.size();
+		}
+
+		@Override
+		public synchronized void add(int index, Layer layer) {
+			if (mLayerList.contains(layer))
+				throw new IllegalArgumentException("layer added twice");
+
+			if (layer instanceof UpdateListener)
+				addUpdateListener((UpdateListener) layer);
+
+			mLayerList.add(index, layer);
+			mDirtyLayers = true;
+		}
+
+		@Override
+		public synchronized Layer remove(int index) {
+			mDirtyLayers = true;
+
+			Layer remove = mLayerList.remove(index);
+
+			if (remove instanceof UpdateListener)
+				removeUpdateListener((UpdateListener) remove);
+
+			return remove;
+		}
+
+		@Override
+		public synchronized Layer set(int index, Layer layer) {
+			if (mLayerList.contains(layer))
+				throw new IllegalArgumentException("layer added twice");
+
+			mDirtyLayers = true;
+			Layer remove = mLayerList.set(index, layer);
+
+			if (remove instanceof UpdateListener)
+				removeUpdateListener((UpdateListener) remove);
+
+			return remove;
+		}
+
+		private boolean mDirtyLayers;
+		private LayerRenderer[] mLayerRenderer;
+
+		public LayerRenderer[] getLayerRenderer() {
+			if (mDirtyLayers)
+				updateLayers();
+
+			return mLayerRenderer;
+		}
+
+		public void destroy() {
+			if (mDirtyLayers)
+				updateLayers();
+
+			for (Layer o : mLayers)
+				o.onDetach();
+		}
+
+		Layer[] mLayers;
+
+		private synchronized void updateLayers() {
+			if (!mDirtyLayers)
+				return;
+
+			mLayers = new Layer[mLayerList.size()];
+
+			int numRenderLayers = 0;
+
+			for (int i = 0, n = mLayerList.size(); i < n; i++) {
+				Layer o = mLayerList.get(i);
+
+				if (o.getRenderer() != null)
+					numRenderLayers++;
+				mLayers[i] = o;
+			}
+
+			mLayerRenderer = new LayerRenderer[numRenderLayers];
+
+			for (int i = 0, cntR = 0, n = mLayerList.size(); i < n; i++) {
+				Layer o = mLayerList.get(i);
+				LayerRenderer l = o.getRenderer();
+				if (l != null)
+					mLayerRenderer[cntR++] = l;
+			}
+
+			mDirtyLayers = false;
+		}
+	}
 }