From 75b78ab951091065f6772ce080ee5798f4b9e601 Mon Sep 17 00:00:00 2001
From: Guardiola31337 <pablo.guardiola@mapbox.com>
Date: Tue, 28 Feb 2017 17:10:58 -0500
Subject: [PATCH] [android] refactor move unit tests from test app to sdk and
 add some annotation manager tests (add marker and add markers)

---
 .../android/MapboxGLAndroidSDK/build.gradle   |   7 +
 .../gradle-tests-staticblockremover.gradle    |  59 +++
 .../com/mapbox/mapboxsdk/LibraryLoader.java   |  15 +
 .../mapboxsdk/maps/AnnotationContainer.java   |  86 ++++
 .../mapboxsdk/maps/AnnotationManager.java     | 449 ++++--------------
 .../mapbox/mapboxsdk/maps/Annotations.java    |  25 +
 .../com/mapbox/mapboxsdk/maps/MapView.java    |  18 +-
 .../com/mapbox/mapboxsdk/maps/MapboxMap.java  |   2 +-
 .../mapboxsdk/maps/MarkerContainer.java       | 267 +++++++++++
 .../com/mapbox/mapboxsdk/maps/Markers.java    |  39 ++
 .../mapbox/mapboxsdk/maps/NativeMapView.java  |   7 +-
 .../mapboxsdk/maps/PolygonContainer.java      | 104 ++++
 .../com/mapbox/mapboxsdk/maps/Polygons.java   |  22 +
 .../mapboxsdk/maps/PolylineContainer.java     | 105 ++++
 .../com/mapbox/mapboxsdk/maps/Polylines.java  |  22 +
 .../net/NativeConnectivityListener.java       |   4 +-
 .../mapboxsdk/offline/OfflineManager.java     |   3 +-
 .../mapboxsdk/offline/OfflineRegion.java      |   5 +-
 .../java/com/mapbox/mapboxsdk/MapboxTest.java |   0
 .../mapboxsdk/annotations/AnnotationTest.java |   0
 .../mapboxsdk/annotations/IconTest.java       |   0
 .../mapboxsdk/annotations/InfoWindowTest.java |   0
 .../mapboxsdk/annotations/MarkerTest.java     |   0
 .../mapboxsdk/annotations/MarkerViewTest.java |   0
 .../mapboxsdk/annotations/PolygonTest.java    |   0
 .../mapboxsdk/annotations/PolylineTest.java   |   0
 .../mapboxsdk/camera/CameraPositionTest.java  |   0
 .../mapboxsdk}/constants/AppConstant.java     |   2 +-
 .../mapboxsdk/geometry/LatLngBoundsTest.java  |   0
 .../mapboxsdk/geometry/LatLngSpanTest.java    |   0
 .../mapbox/mapboxsdk/geometry/LatLngTest.java |   0
 .../geometry/ProjectedMetersTest.java         |   0
 .../mapboxsdk/geometry/VisibleRegionTest.java |   0
 .../mapboxsdk/maps/AnnotationManagerTest.java |  81 ++++
 .../mapboxsdk/maps/MapboxMapOptionsTest.java  |   0
 .../mapboxsdk/maps/TrackingSettingsTest.java  |   0
 .../mapbox/mapboxsdk/maps/UiSettingsTest.java |   0
 .../widgets/MyLocationViewSettingsTest.java   |   0
 .../mapboxsdk/style/layers/FilterTest.java    |   0
 .../mapboxsdk/style/layers/FunctionTest.java  |   0
 .../telemetry/HttpTransportTest.java          |   0
 .../mapbox/mapboxsdk/utils/MockParcel.java    |   0
 .../org.mockito.plugins.MockMaker             |   0
 .../MapboxGLAndroidSDKTestApp/build.gradle    |   6 -
 44 files changed, 939 insertions(+), 389 deletions(-)
 create mode 100644 platform/android/MapboxGLAndroidSDK/gradle-tests-staticblockremover.gradle
 create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java
 create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java
 create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java
 create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java
 create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java
 create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java
 create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java
 create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java
 create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model => MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk}/constants/AppConstant.java (58%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java (100%)
 create mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java (100%)
 rename platform/android/{MapboxGLAndroidSDKTestApp => MapboxGLAndroidSDK}/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker (100%)

diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle
index 1156a6ef434..40712da0657 100644
--- a/platform/android/MapboxGLAndroidSDK/build.gradle
+++ b/platform/android/MapboxGLAndroidSDK/build.gradle
@@ -7,6 +7,8 @@ dependencies {
     compile rootProject.ext.dep.timber
     compile rootProject.ext.dep.okhttp3
     compile rootProject.ext.dep.lost
+    testCompile rootProject.ext.dep.junit
+    testCompile rootProject.ext.dep.mockito
 
     // Mapbox Android Services (GeoJSON support)
     compile(rootProject.ext.dep.mapboxJavaGeoJSON) {
@@ -113,6 +115,10 @@ android {
         warningsAsErrors true
     }
 
+    testOptions {
+        unitTests.returnDefaultValues = true
+    }
+
     buildTypes {
         debug {
             jniDebuggable true
@@ -141,3 +147,4 @@ configurations {
 apply from: 'gradle-javadoc.gradle'
 apply from: 'gradle-publish.gradle'
 apply from: 'gradle-checkstyle.gradle'
+apply from: 'gradle-tests-staticblockremover.gradle'
diff --git a/platform/android/MapboxGLAndroidSDK/gradle-tests-staticblockremover.gradle b/platform/android/MapboxGLAndroidSDK/gradle-tests-staticblockremover.gradle
new file mode 100644
index 00000000000..523dc99dd15
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/gradle-tests-staticblockremover.gradle
@@ -0,0 +1,59 @@
+buildscript {
+    repositories {
+        mavenCentral()
+        mavenLocal()
+    }
+
+    dependencies {
+        classpath 'com.darylteo.gradle:javassist-plugin:0.4.1'
+    }
+}
+
+import com.darylteo.gradle.javassist.tasks.TransformationTask
+import com.darylteo.gradle.javassist.transformers.ClassTransformer
+import javassist.CtClass
+import javassist.CtConstructor
+
+class StaticBlockRemover extends ClassTransformer {
+
+    private static final NATIVE_MAP_VIEW = "com.mapbox.mapboxsdk.maps.NativeMapView";
+    private static
+    final NATIVE_CONNECTIVITY_LISTENER = "com.mapbox.mapboxsdk.net.NativeConnectivityListener";
+    private static final OFFLINE_MANAGER = "com.mapbox.mapboxsdk.offline.OfflineManager";
+    private static final OFFLINE_REGION = "com.mapbox.mapboxsdk.offline.OfflineRegion";
+
+    public void applyTransformations(CtClass clazz) throws Exception {
+        if (shouldFilter(clazz)) {
+            CtConstructor constructor = clazz.getClassInitializer()
+            if (constructor != null) {
+                clazz.removeConstructor(constructor)
+            }
+        }
+    }
+
+    public boolean shouldFilter(CtClass clazz) {
+        return hasAStaticBlock(clazz);
+    }
+
+    private boolean hasAStaticBlock(CtClass clazz) {
+        String name = clazz.getName();
+        boolean isNativeMapView = name.equals(NATIVE_MAP_VIEW);
+        boolean isNativeConnectivityListener = name.equals(NATIVE_CONNECTIVITY_LISTENER);
+        boolean isOfflineManager = name.equals(OFFLINE_MANAGER);
+        boolean isOfflineRegion = name.equals(OFFLINE_REGION);
+
+        return isNativeMapView || isNativeConnectivityListener || isOfflineManager || isOfflineRegion;
+    }
+}
+
+task removeStatic(type: TransformationTask) {
+    // TODO Find a better way to get output classes path
+    String fromToDirPath = buildDir.getAbsolutePath() + "/intermediates/classes/debug"
+    from fromToDirPath
+    transformation = new StaticBlockRemover()
+    into fromToDirPath
+}
+
+afterEvaluate {
+    compileDebugUnitTestSources.dependsOn(removeStatic)
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java
new file mode 100644
index 00000000000..8a75176ccdf
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java
@@ -0,0 +1,15 @@
+package com.mapbox.mapboxsdk;
+
+/**
+ * Centralises the knowledge about "mapbox-gl" library loading.
+ */
+public class LibraryLoader {
+
+  /**
+   * Loads "libmapbox-gl.so" native shared library.
+   */
+  public static void load() {
+    System.loadLibrary("mapbox-gl");
+  }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java
new file mode 100644
index 00000000000..939fadc9c25
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java
@@ -0,0 +1,86 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.support.annotation.NonNull;
+import android.support.v4.util.LongSparseArray;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Encapsulates {@link Annotation}'s functionality..
+ */
+class AnnotationContainer implements Annotations {
+
+  private final NativeMapView nativeMapView;
+  private final LongSparseArray<Annotation> annotations;
+
+  AnnotationContainer(NativeMapView nativeMapView, LongSparseArray<Annotation> annotations) {
+    this.nativeMapView = nativeMapView;
+    this.annotations = annotations;
+  }
+
+  @Override
+  public Annotation obtainBy(long id) {
+    return annotations.get(id);
+  }
+
+  @Override
+  public List<Annotation> obtainAll() {
+    List<Annotation> annotations = new ArrayList<>();
+    for (int i = 0; i < this.annotations.size(); i++) {
+      annotations.add(this.annotations.get(this.annotations.keyAt(i)));
+    }
+    return annotations;
+  }
+
+  @Override
+  public void removeBy(long id) {
+    if (nativeMapView != null) {
+      nativeMapView.removeAnnotation(id);
+    }
+    annotations.remove(id);
+  }
+
+  @Override
+  public void removeBy(@NonNull Annotation annotation) {
+    long id = annotation.getId();
+    removeBy(id);
+  }
+
+  @Override
+  public void removeBy(@NonNull List<? extends Annotation> annotationList) {
+    int count = annotationList.size();
+    long[] ids = new long[count];
+    for (int i = 0; i < count; i++) {
+      ids[i] = annotationList.get(i).getId();
+    }
+
+    removeNativeAnnotations(ids);
+
+    for (long id : ids) {
+      annotations.remove(id);
+    }
+  }
+
+  @Override
+  public void removeAll() {
+    int count = annotations.size();
+    long[] ids = new long[count];
+    for (int i = 0; i < count; i++) {
+      ids[i] = annotations.keyAt(i);
+    }
+
+    removeNativeAnnotations(ids);
+
+    annotations.clear();
+  }
+
+  private void removeNativeAnnotations(long[] ids) {
+    if (nativeMapView != null) {
+      nativeMapView.removeAnnotations(ids);
+    }
+  }
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java
index 6553b64592c..9b6706b90c8 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java
@@ -9,7 +9,6 @@
 import com.mapbox.mapboxsdk.annotations.Annotation;
 import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
 import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
-import com.mapbox.mapboxsdk.annotations.Icon;
 import com.mapbox.mapboxsdk.annotations.Marker;
 import com.mapbox.mapboxsdk.annotations.MarkerView;
 import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
@@ -22,8 +21,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import timber.log.Timber;
-
 /**
  * Responsible for managing and tracking state of Annotations linked to Map. All events related to
  * annotations that occur on {@link MapboxMap} are forwarded to this class.
@@ -42,17 +39,28 @@ class AnnotationManager {
   private final IconManager iconManager;
   private final InfoWindowManager infoWindowManager = new InfoWindowManager();
   private final MarkerViewManager markerViewManager;
-  private final LongSparseArray<Annotation> annotations = new LongSparseArray<>();
+  private final LongSparseArray<Annotation> annotationsArray;
   private final List<Marker> selectedMarkers = new ArrayList<>();
 
   private MapboxMap mapboxMap;
   private MapboxMap.OnMarkerClickListener onMarkerClickListener;
-
-  AnnotationManager(NativeMapView view, MapView mapView, MarkerViewManager markerViewManager) {
+  private Annotations annotations;
+  private Markers markers;
+  private Polygons polygons;
+  private Polylines polylines;
+
+  AnnotationManager(NativeMapView view, MapView mapView, LongSparseArray<Annotation> annotationsArray,
+                    MarkerViewManager markerViewManager, IconManager iconManager, Annotations annotations,
+                    Markers markers, Polygons polygons, Polylines polylines) {
     this.nativeMapView = view;
     this.mapView = mapView;
-    this.iconManager = new IconManager(nativeMapView);
+    this.annotationsArray = annotationsArray;
     this.markerViewManager = markerViewManager;
+    this.iconManager = iconManager;
+    this.annotations = annotations;
+    this.markers = markers;
+    this.polygons = polygons;
+    this.polylines = polylines;
     if (view != null) {
       // null checking needed for unit tests
       nativeMapView.addOnMapChangedListener(markerViewManager);
@@ -77,15 +85,15 @@ void update() {
   //
 
   Annotation getAnnotation(long id) {
-    return annotations.get(id);
+    return annotations.obtainBy(id);
   }
 
   List<Annotation> getAnnotations() {
-    List<Annotation> annotations = new ArrayList<>();
-    for (int i = 0; i < this.annotations.size(); i++) {
-      annotations.add(this.annotations.get(this.annotations.keyAt(i)));
-    }
-    return annotations;
+    return annotations.obtainAll();
+  }
+
+  void removeAnnotation(long id) {
+    annotations.removeBy(id);
   }
 
   void removeAnnotation(@NonNull Annotation annotation) {
@@ -96,25 +104,11 @@ void removeAnnotation(@NonNull Annotation annotation) {
         markerViewManager.removeMarkerView((MarkerView) marker);
       }
     }
-    long id = annotation.getId();
-    if (nativeMapView != null) {
-      nativeMapView.removeAnnotation(id);
-    }
-    annotations.remove(id);
-  }
-
-  void removeAnnotation(long id) {
-    if (nativeMapView != null) {
-      nativeMapView.removeAnnotation(id);
-    }
-    annotations.remove(id);
+    annotations.removeBy(annotation);
   }
 
   void removeAnnotations(@NonNull List<? extends Annotation> annotationList) {
-    int count = annotationList.size();
-    long[] ids = new long[count];
-    for (int i = 0; i < count; i++) {
-      Annotation annotation = annotationList.get(i);
+    for (Annotation annotation : annotationList) {
       if (annotation instanceof Marker) {
         Marker marker = (Marker) annotation;
         marker.hideInfoWindow();
@@ -122,25 +116,17 @@ void removeAnnotations(@NonNull List<? extends Annotation> annotationList) {
           markerViewManager.removeMarkerView((MarkerView) marker);
         }
       }
-      ids[i] = annotationList.get(i).getId();
-    }
-
-    if (nativeMapView != null) {
-      nativeMapView.removeAnnotations(ids);
-    }
-
-    for (long id : ids) {
-      annotations.remove(id);
     }
+    annotations.removeBy(annotationList);
   }
 
   void removeAnnotations() {
     Annotation annotation;
-    int count = annotations.size();
+    int count = annotationsArray.size();
     long[] ids = new long[count];
     for (int i = 0; i < count; i++) {
-      ids[i] = annotations.keyAt(i);
-      annotation = annotations.get(ids[i]);
+      ids[i] = annotationsArray.keyAt(i);
+      annotation = annotationsArray.get(ids[i]);
       if (annotation instanceof Marker) {
         Marker marker = (Marker) annotation;
         marker.hideInfoWindow();
@@ -149,12 +135,7 @@ void removeAnnotations() {
         }
       }
     }
-
-    if (nativeMapView != null) {
-      nativeMapView.removeAnnotations(ids);
-    }
-
-    annotations.clear();
+    annotations.removeAll();
   }
 
   //
@@ -162,139 +143,86 @@ void removeAnnotations() {
   //
 
   Marker addMarker(@NonNull BaseMarkerOptions markerOptions, @NonNull MapboxMap mapboxMap) {
-    Marker marker = prepareMarker(markerOptions);
-    long id = nativeMapView != null ? nativeMapView.addMarker(marker) : 0;
-    marker.setMapboxMap(mapboxMap);
-    marker.setId(id);
-    annotations.put(id, marker);
-    return marker;
+    return markers.addBy(markerOptions, mapboxMap);
   }
 
   List<Marker> addMarkers(@NonNull List<? extends BaseMarkerOptions> markerOptionsList, @NonNull MapboxMap mapboxMap) {
-    int count = markerOptionsList.size();
-    List<Marker> markers = new ArrayList<>(count);
-    if (count > 0) {
-      BaseMarkerOptions markerOptions;
-      Marker marker;
-      for (int i = 0; i < count; i++) {
-        markerOptions = markerOptionsList.get(i);
-        marker = prepareMarker(markerOptions);
-        markers.add(marker);
-      }
-
-      if (markers.size() > 0) {
-        long[] ids = null;
-        if (nativeMapView != null) {
-          ids = nativeMapView.addMarkers(markers);
-        }
+    return markers.addBy(markerOptionsList, mapboxMap);
+  }
 
-        long id = 0;
-        Marker m;
-        for (int i = 0; i < markers.size(); i++) {
-          m = markers.get(i);
-          m.setMapboxMap(mapboxMap);
-          if (ids != null) {
-            id = ids[i];
-          } else {
-            // unit test
-            id++;
-          }
-          m.setId(id);
-          annotations.put(id, m);
-        }
+  void updateMarker(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap) {
+    markers.update(updatedMarker, mapboxMap);
+  }
 
-      }
-    }
-    return markers;
+  List<Marker> getMarkers() {
+    return markers.obtainAll();
   }
 
-  private Marker prepareMarker(BaseMarkerOptions markerOptions) {
-    Marker marker = markerOptions.getMarker();
-    Icon icon = iconManager.loadIconForMarker(marker);
-    marker.setTopOffsetPixels(iconManager.getTopOffsetPixelsForIcon(icon));
-    return marker;
+  @NonNull
+  List<Marker> getMarkersInRect(@NonNull RectF rectangle) {
+    return markers.obtainAllIn(rectangle);
   }
 
   MarkerView addMarker(@NonNull BaseMarkerViewOptions markerOptions, @NonNull MapboxMap mapboxMap,
                        @Nullable MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener) {
-    final MarkerView marker = prepareViewMarker(markerOptions);
+    return markers.addViewBy(markerOptions, mapboxMap, onMarkerViewAddedListener);
+  }
 
-    // add marker to map
-    marker.setMapboxMap(mapboxMap);
-    long id = nativeMapView.addMarker(marker);
-    marker.setId(id);
-    annotations.put(id, marker);
+  List<MarkerView> addMarkerViews(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions,
+                                  @NonNull MapboxMap mapboxMap) {
+    return markers.addViewsBy(markerViewOptions, mapboxMap);
+  }
 
-    if (onMarkerViewAddedListener != null) {
-      markerViewManager.addOnMarkerViewAddedListener(marker, onMarkerViewAddedListener);
-    }
-    markerViewManager.setEnabled(true);
-    markerViewManager.setWaitingForRenderInvoke(true);
-    return marker;
+  List<MarkerView> getMarkerViewsInRect(@NonNull RectF rectangle) {
+    return markers.obtainViewsIn(rectangle);
+  }
+
+  void reloadMarkers() {
+    markers.reload();
   }
 
+  //
+  // Polygons
+  //
 
-  List<MarkerView> addMarkerViews(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions,
-                                  @NonNull MapboxMap mapboxMap) {
-    List<MarkerView> markers = new ArrayList<>();
-    for (BaseMarkerViewOptions markerViewOption : markerViewOptions) {
-      // if last marker
-      if (markerViewOptions.indexOf(markerViewOption) == markerViewOptions.size() - 1) {
-        // get notified when render occurs to invalidate and draw MarkerViews
-        markerViewManager.setWaitingForRenderInvoke(true);
-      }
-      // add marker to map
-      MarkerView marker = prepareViewMarker(markerViewOption);
-      marker.setMapboxMap(mapboxMap);
-      long id = nativeMapView.addMarker(marker);
-      marker.setId(id);
-      annotations.put(id, marker);
-      markers.add(marker);
-    }
-    markerViewManager.setEnabled(true);
-    markerViewManager.update();
-    return markers;
+  Polygon addPolygon(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) {
+    return polygons.addBy(polygonOptions, mapboxMap);
   }
 
-  private MarkerView prepareViewMarker(BaseMarkerViewOptions markerViewOptions) {
-    MarkerView marker = markerViewOptions.getMarker();
-    iconManager.loadIconForMarkerView(marker);
-    return marker;
+  List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) {
+    return polygons.addBy(polygonOptionsList, mapboxMap);
   }
 
-  void updateMarker(@NonNull Marker updatedMarker) {
-    if (!isAddedToMap(updatedMarker)) {
-      Timber.w("Attempting to update non-added Marker with value %s", updatedMarker);
-      return;
-    }
+  void updatePolygon(Polygon polygon) {
+    polygons.update(polygon);
+  }
+
+  List<Polygon> getPolygons() {
+    return polygons.obtainAll();
+  }
+
+  //
+  // Polylines
+  //
 
-    ensureIconLoaded(updatedMarker);
-    nativeMapView.updateMarker(updatedMarker);
-    annotations.setValueAt(annotations.indexOfKey(updatedMarker.getId()), updatedMarker);
+  Polyline addPolyline(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) {
+    return polylines.addBy(polylineOptions, mapboxMap);
   }
 
-  private boolean isAddedToMap(Annotation annotation) {
-    return annotation != null && annotation.getId() != -1 && annotations.indexOfKey(annotation.getId()) != -1;
+  List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) {
+    return polylines.addBy(polylineOptionsList, mapboxMap);
   }
 
-  private void ensureIconLoaded(Marker marker) {
-    if (!(marker instanceof MarkerView)) {
-      iconManager.ensureIconLoaded(marker, mapboxMap);
-    }
+  void updatePolyline(Polyline polyline) {
+    polylines.update(polyline);
   }
 
-  List<Marker> getMarkers() {
-    List<Marker> markers = new ArrayList<>();
-    Annotation annotation;
-    for (int i = 0; i < annotations.size(); i++) {
-      annotation = annotations.get(annotations.keyAt(i));
-      if (annotation instanceof Marker) {
-        markers.add((Marker) annotation);
-      }
-    }
-    return markers;
+  List<Polyline> getPolylines() {
+    return polylines.obtainAll();
   }
 
+  // TODO Refactor from here still in progress
+
   void setOnMarkerClickListener(@Nullable MapboxMap.OnMarkerClickListener listener) {
     onMarkerClickListener = listener;
   }
@@ -361,207 +289,6 @@ List<Marker> getSelectedMarkers() {
     return selectedMarkers;
   }
 
-  @NonNull
-  List<Marker> getMarkersInRect(@NonNull RectF rectangle) {
-    // convert Rectangle to be density depedent
-    float pixelRatio = nativeMapView.getPixelRatio();
-    RectF rect = new RectF(rectangle.left / pixelRatio,
-      rectangle.top / pixelRatio,
-      rectangle.right / pixelRatio,
-      rectangle.bottom / pixelRatio);
-
-    long[] ids = nativeMapView.queryPointAnnotations(rect);
-
-    List<Long> idsList = new ArrayList<>(ids.length);
-    for (long id : ids) {
-      idsList.add(id);
-    }
-
-    List<Marker> annotations = new ArrayList<>(ids.length);
-    List<Annotation> annotationList = getAnnotations();
-    int count = annotationList.size();
-    for (int i = 0; i < count; i++) {
-      Annotation annotation = annotationList.get(i);
-      if (annotation instanceof com.mapbox.mapboxsdk.annotations.Marker && idsList.contains(annotation.getId())) {
-        annotations.add((com.mapbox.mapboxsdk.annotations.Marker) annotation);
-      }
-    }
-
-    return new ArrayList<>(annotations);
-  }
-
-  List<MarkerView> getMarkerViewsInRect(@NonNull RectF rectangle) {
-    float pixelRatio = nativeMapView.getPixelRatio();
-    RectF rect = new RectF(rectangle.left / pixelRatio,
-      rectangle.top / pixelRatio,
-      rectangle.right / pixelRatio,
-      rectangle.bottom / pixelRatio);
-
-    long[] ids = nativeMapView.queryPointAnnotations(rect);
-
-    List<Long> idsList = new ArrayList<>(ids.length);
-    for (long id : ids) {
-      idsList.add(id);
-    }
-
-    List<MarkerView> annotations = new ArrayList<>(ids.length);
-    List<Annotation> annotationList = getAnnotations();
-    int count = annotationList.size();
-    for (int i = 0; i < count; i++) {
-      Annotation annotation = annotationList.get(i);
-      if (annotation instanceof MarkerView && idsList.contains(annotation.getId())) {
-        annotations.add((MarkerView) annotation);
-      }
-    }
-
-    return new ArrayList<>(annotations);
-  }
-
-  //
-  // Polygons
-  //
-
-  Polygon addPolygon(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) {
-    Polygon polygon = polygonOptions.getPolygon();
-    if (!polygon.getPoints().isEmpty()) {
-      long id = nativeMapView != null ? nativeMapView.addPolygon(polygon) : 0;
-      polygon.setId(id);
-      polygon.setMapboxMap(mapboxMap);
-      annotations.put(id, polygon);
-    }
-    return polygon;
-  }
-
-  List<Polygon> addPolygons(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) {
-    int count = polygonOptionsList.size();
-
-    Polygon polygon;
-    List<Polygon> polygons = new ArrayList<>(count);
-    if (count > 0) {
-      for (PolygonOptions polygonOptions : polygonOptionsList) {
-        polygon = polygonOptions.getPolygon();
-        if (!polygon.getPoints().isEmpty()) {
-          polygons.add(polygon);
-        }
-      }
-
-      long[] ids = null;
-      if (nativeMapView != null) {
-        ids = nativeMapView.addPolygons(polygons);
-      }
-
-      long id = 0;
-      for (int i = 0; i < polygons.size(); i++) {
-        polygon = polygons.get(i);
-        polygon.setMapboxMap(mapboxMap);
-        if (ids != null) {
-          id = ids[i];
-        } else {
-          // unit test
-          id++;
-        }
-        polygon.setId(id);
-        annotations.put(id, polygon);
-      }
-    }
-    return polygons;
-  }
-
-  void updatePolygon(@NonNull Polygon polygon) {
-    if (!isAddedToMap(polygon)) {
-      Timber.w("Attempting to update non-added Polygon with value %s", polygon);
-      return;
-    }
-
-    nativeMapView.updatePolygon(polygon);
-    annotations.setValueAt(annotations.indexOfKey(polygon.getId()), polygon);
-  }
-
-  List<Polygon> getPolygons() {
-    List<Polygon> polygons = new ArrayList<>();
-    Annotation annotation;
-    for (int i = 0; i < annotations.size(); i++) {
-      annotation = annotations.get(annotations.keyAt(i));
-      if (annotation instanceof Polygon) {
-        polygons.add((Polygon) annotation);
-      }
-    }
-    return polygons;
-  }
-
-  //
-  // Polylines
-  //
-
-  Polyline addPolyline(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) {
-    Polyline polyline = polylineOptions.getPolyline();
-    if (!polyline.getPoints().isEmpty()) {
-      long id = nativeMapView != null ? nativeMapView.addPolyline(polyline) : 0;
-      polyline.setMapboxMap(mapboxMap);
-      polyline.setId(id);
-      annotations.put(id, polyline);
-    }
-    return polyline;
-  }
-
-  List<Polyline> addPolylines(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) {
-    int count = polylineOptionsList.size();
-    Polyline polyline;
-    List<Polyline> polylines = new ArrayList<>(count);
-
-    if (count > 0) {
-      for (PolylineOptions options : polylineOptionsList) {
-        polyline = options.getPolyline();
-        if (!polyline.getPoints().isEmpty()) {
-          polylines.add(polyline);
-        }
-      }
-
-      long[] ids = null;
-      if (nativeMapView != null) {
-        ids = nativeMapView.addPolylines(polylines);
-      }
-
-      long id = 0;
-      Polyline p;
-
-      for (int i = 0; i < polylines.size(); i++) {
-        p = polylines.get(i);
-        p.setMapboxMap(mapboxMap);
-        if (ids != null) {
-          id = ids[i];
-        } else {
-          // unit test
-          id++;
-        }
-        p.setId(id);
-        annotations.put(id, p);
-      }
-    }
-    return polylines;
-  }
-
-  void updatePolyline(@NonNull Polyline polyline) {
-    if (!isAddedToMap(polyline)) {
-      Timber.w("Attempting to update non-added Polyline with value %s", polyline);
-    }
-
-    nativeMapView.updatePolyline(polyline);
-    annotations.setValueAt(annotations.indexOfKey(polyline.getId()), polyline);
-  }
-
-  List<Polyline> getPolylines() {
-    List<Polyline> polylines = new ArrayList<>();
-    Annotation annotation;
-    for (int i = 0; i < annotations.size(); i++) {
-      annotation = annotations.get(annotations.keyAt(i));
-      if (annotation instanceof Polyline) {
-        polylines.add((Polyline) annotation);
-      }
-    }
-    return polylines;
-  }
-
   InfoWindowManager getInfoWindowManager() {
     return infoWindowManager;
   }
@@ -571,9 +298,9 @@ MarkerViewManager getMarkerViewManager() {
   }
 
   void adjustTopOffsetPixels(MapboxMap mapboxMap) {
-    int count = annotations.size();
+    int count = annotationsArray.size();
     for (int i = 0; i < count; i++) {
-      Annotation annotation = annotations.get(i);
+      Annotation annotation = annotationsArray.get(i);
       if (annotation instanceof Marker) {
         Marker marker = (Marker) annotation;
         marker.setTopOffsetPixels(
@@ -589,20 +316,6 @@ void adjustTopOffsetPixels(MapboxMap mapboxMap) {
     }
   }
 
-  void reloadMarkers() {
-    iconManager.reloadIcons();
-    int count = annotations.size();
-    for (int i = 0; i < count; i++) {
-      Annotation annotation = annotations.get(i);
-      if (annotation instanceof Marker) {
-        Marker marker = (Marker) annotation;
-        nativeMapView.removeAnnotation(annotation.getId());
-        long newId = nativeMapView.addMarker(marker);
-        marker.setId(newId);
-      }
-    }
-  }
-
   //
   // Click event
   //
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java
new file mode 100644
index 00000000000..ae41cbb0cbb
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java
@@ -0,0 +1,25 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.support.annotation.NonNull;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+
+import java.util.List;
+
+/**
+ * Interface that defines convenient methods for working with a {@link Annotation}'s collection.
+ */
+interface Annotations {
+  Annotation obtainBy(long id);
+
+  List<Annotation> obtainAll();
+
+  void removeBy(long id);
+
+  void removeBy(@NonNull Annotation annotation);
+
+  void removeBy(@NonNull List<? extends Annotation> annotationList);
+
+  void removeAll();
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
index 872802292c4..4f90b0a8fe0 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
@@ -13,6 +13,7 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.UiThread;
+import android.support.v4.util.LongSparseArray;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -29,6 +30,7 @@
 
 import com.mapbox.mapboxsdk.Mapbox;
 import com.mapbox.mapboxsdk.R;
+import com.mapbox.mapboxsdk.annotations.Annotation;
 import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
 import com.mapbox.mapboxsdk.constants.MapboxConstants;
 import com.mapbox.mapboxsdk.constants.Style;
@@ -134,14 +136,22 @@ private void initialise(@NonNull final Context context, @NonNull final MapboxMap
     UiSettings uiSettings = new UiSettings(proj, focalPoint, compassView, attrView, view.findViewById(R.id.logoView));
     TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPoint, zoomInvalidator);
     MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, proj, focalPoint);
+    LongSparseArray<Annotation> annotationsArray = new LongSparseArray<>();
     MarkerViewManager markerViewManager = new MarkerViewManager((ViewGroup) findViewById(R.id.markerViewContainer));
-    AnnotationManager annotations = new AnnotationManager(nativeMapView, this, markerViewManager);
-    Transform transform = new Transform(nativeMapView, annotations.getMarkerViewManager(), trackingSettings);
+    IconManager iconManager = new IconManager(nativeMapView);
+    Annotations annotations = new AnnotationContainer(nativeMapView, annotationsArray);
+    Markers markers = new MarkerContainer(nativeMapView, this, annotationsArray, iconManager, markerViewManager);
+    Polygons polygons = new PolygonContainer(nativeMapView, annotationsArray);
+    Polylines polylines = new PolylineContainer(nativeMapView, annotationsArray);
+    AnnotationManager annotationManager = new AnnotationManager(nativeMapView, this, annotationsArray,
+      markerViewManager, iconManager, annotations, markers, polygons, polylines);
+    Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(), trackingSettings);
     mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, trackingSettings, myLocationViewSettings, proj,
-      registerTouchListener, annotations);
+      registerTouchListener, annotationManager);
 
     // user input
-    mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings, annotations);
+    mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings,
+      annotationManager);
     mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings);
 
     MapZoomControllerListener zoomListener = new MapZoomControllerListener(mapGestureDetector, uiSettings, transform);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
index 1751dcf0428..79d36667ff3 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
@@ -1179,7 +1179,7 @@ public List<Marker> addMarkers(@NonNull List<? extends
    */
   @UiThread
   public void updateMarker(@NonNull Marker updatedMarker) {
-    annotationManager.updateMarker(updatedMarker);
+    annotationManager.updateMarker(updatedMarker, this);
   }
 
   /**
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java
new file mode 100644
index 00000000000..306ad59b8d2
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java
@@ -0,0 +1,267 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.graphics.RectF;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.util.LongSparseArray;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
+import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
+import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import timber.log.Timber;
+
+/**
+ * Encapsulates {@link Marker}'s functionality.
+ */
+class MarkerContainer implements Markers {
+
+  private final NativeMapView nativeMapView;
+  private final MapView mapView;
+  private final LongSparseArray<Annotation> annotations;
+  private final IconManager iconManager;
+  private final MarkerViewManager markerViewManager;
+
+  MarkerContainer(NativeMapView nativeMapView, MapView mapView, LongSparseArray<Annotation> annotations, IconManager
+    iconManager, MarkerViewManager markerViewManager) {
+    this.nativeMapView = nativeMapView;
+    this.mapView = mapView;
+    this.annotations = annotations;
+    this.iconManager = iconManager;
+    this.markerViewManager = markerViewManager;
+  }
+
+  @Override
+  public Marker addBy(@NonNull BaseMarkerOptions markerOptions, @NonNull MapboxMap mapboxMap) {
+    Marker marker = prepareMarker(markerOptions);
+    long id = nativeMapView != null ? nativeMapView.addMarker(marker) : 0;
+    marker.setMapboxMap(mapboxMap);
+    marker.setId(id);
+    annotations.put(id, marker);
+    return marker;
+  }
+
+  @Override
+  public List<Marker> addBy(@NonNull List<? extends BaseMarkerOptions> markerOptionsList, @NonNull MapboxMap
+    mapboxMap) {
+    int count = markerOptionsList.size();
+    List<Marker> markers = new ArrayList<>(count);
+    if (count > 0) {
+      BaseMarkerOptions markerOptions;
+      Marker marker;
+      for (int i = 0; i < count; i++) {
+        markerOptions = markerOptionsList.get(i);
+        marker = prepareMarker(markerOptions);
+        markers.add(marker);
+      }
+
+      if (markers.size() > 0) {
+        long[] ids = null;
+        if (nativeMapView != null) {
+          ids = nativeMapView.addMarkers(markers);
+        }
+
+        long id = 0;
+        Marker m;
+        for (int i = 0; i < markers.size(); i++) {
+          m = markers.get(i);
+          m.setMapboxMap(mapboxMap);
+          if (ids != null) {
+            id = ids[i];
+          } else {
+            // unit test
+            id++;
+          }
+          m.setId(id);
+          annotations.put(id, m);
+        }
+
+      }
+    }
+    return markers;
+  }
+
+  @Override
+  public void update(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap) {
+    if (!isAddedToMap(updatedMarker)) {
+      Timber.w("Attempting to update non-added Marker with value %s", updatedMarker);
+      return;
+    }
+
+    ensureIconLoaded(updatedMarker, mapboxMap);
+    nativeMapView.updateMarker(updatedMarker);
+    annotations.setValueAt(annotations.indexOfKey(updatedMarker.getId()), updatedMarker);
+  }
+
+  @Override
+  public List<Marker> obtainAll() {
+    List<Marker> markers = new ArrayList<>();
+    Annotation annotation;
+    for (int i = 0; i < annotations.size(); i++) {
+      annotation = annotations.get(annotations.keyAt(i));
+      if (annotation instanceof Marker) {
+        markers.add((Marker) annotation);
+      }
+    }
+    return markers;
+  }
+
+  @NonNull
+  @Override
+  public List<Marker> obtainAllIn(@NonNull RectF rectangle) {
+    // convert Rectangle to be density depedent
+    float pixelRatio = nativeMapView.getPixelRatio();
+    RectF rect = new RectF(rectangle.left / pixelRatio,
+      rectangle.top / pixelRatio,
+      rectangle.right / pixelRatio,
+      rectangle.bottom / pixelRatio);
+
+    long[] ids = nativeMapView.queryPointAnnotations(rect);
+
+    List<Long> idsList = new ArrayList<>(ids.length);
+    for (long id : ids) {
+      idsList.add(id);
+    }
+
+    List<Marker> annotations = new ArrayList<>(ids.length);
+    List<Annotation> annotationList = obtainAnnotations();
+    int count = annotationList.size();
+    for (int i = 0; i < count; i++) {
+      Annotation annotation = annotationList.get(i);
+      if (annotation instanceof com.mapbox.mapboxsdk.annotations.Marker && idsList.contains(annotation.getId())) {
+        annotations.add((com.mapbox.mapboxsdk.annotations.Marker) annotation);
+      }
+    }
+
+    return new ArrayList<>(annotations);
+  }
+
+  @Override
+  public MarkerView addViewBy(@NonNull BaseMarkerViewOptions markerOptions, @NonNull MapboxMap mapboxMap, @Nullable
+    MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener) {
+    final MarkerView marker = prepareViewMarker(markerOptions);
+
+    // add marker to map
+    marker.setMapboxMap(mapboxMap);
+    long id = nativeMapView.addMarker(marker);
+    marker.setId(id);
+    annotations.put(id, marker);
+
+    if (onMarkerViewAddedListener != null) {
+      markerViewManager.addOnMarkerViewAddedListener(marker, onMarkerViewAddedListener);
+    }
+    markerViewManager.setEnabled(true);
+    markerViewManager.setWaitingForRenderInvoke(true);
+    return marker;
+  }
+
+  @Override
+  public List<MarkerView> addViewsBy(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions, @NonNull
+    MapboxMap mapboxMap) {
+    List<MarkerView> markers = new ArrayList<>();
+    for (BaseMarkerViewOptions markerViewOption : markerViewOptions) {
+      // if last marker
+      if (markerViewOptions.indexOf(markerViewOption) == markerViewOptions.size() - 1) {
+        // get notified when render occurs to invalidate and draw MarkerViews
+        markerViewManager.setWaitingForRenderInvoke(true);
+      }
+      // add marker to map
+      MarkerView marker = prepareViewMarker(markerViewOption);
+      marker.setMapboxMap(mapboxMap);
+      long id = nativeMapView.addMarker(marker);
+      marker.setId(id);
+      annotations.put(id, marker);
+      markers.add(marker);
+    }
+    markerViewManager.setEnabled(true);
+    markerViewManager.update();
+    return markers;
+  }
+
+  @Override
+  public List<MarkerView> obtainViewsIn(@NonNull RectF rectangle) {
+    float pixelRatio = nativeMapView.getPixelRatio();
+    RectF rect = new RectF(rectangle.left / pixelRatio,
+      rectangle.top / pixelRatio,
+      rectangle.right / pixelRatio,
+      rectangle.bottom / pixelRatio);
+
+    long[] ids = nativeMapView.queryPointAnnotations(rect);
+
+    List<Long> idsList = new ArrayList<>(ids.length);
+    for (long id : ids) {
+      idsList.add(id);
+    }
+
+    List<MarkerView> annotations = new ArrayList<>(ids.length);
+    List<Annotation> annotationList = obtainAnnotations();
+    int count = annotationList.size();
+    for (int i = 0; i < count; i++) {
+      Annotation annotation = annotationList.get(i);
+      if (annotation instanceof MarkerView && idsList.contains(annotation.getId())) {
+        annotations.add((MarkerView) annotation);
+      }
+    }
+
+    return new ArrayList<>(annotations);
+  }
+
+  @Override
+  public void reload() {
+    iconManager.reloadIcons();
+    int count = annotations.size();
+    for (int i = 0; i < count; i++) {
+      Annotation annotation = annotations.get(i);
+      if (annotation instanceof Marker) {
+        Marker marker = (Marker) annotation;
+        nativeMapView.removeAnnotation(annotation.getId());
+        long newId = nativeMapView.addMarker(marker);
+        marker.setId(newId);
+      }
+    }
+  }
+
+  private Marker prepareMarker(BaseMarkerOptions markerOptions) {
+    Marker marker = markerOptions.getMarker();
+    Icon icon = iconManager.loadIconForMarker(marker);
+    marker.setTopOffsetPixels(iconManager.getTopOffsetPixelsForIcon(icon));
+    return marker;
+  }
+
+  private boolean isAddedToMap(Annotation annotation) {
+    return annotation != null && annotation.getId() != -1 && annotations.indexOfKey(annotation.getId()) != -1;
+  }
+
+  private void ensureIconLoaded(Marker marker, MapboxMap mapboxMap) {
+    if (!(marker instanceof MarkerView)) {
+      iconManager.ensureIconLoaded(marker, mapboxMap);
+    }
+  }
+
+  private List<Annotation> obtainAnnotations() {
+    List<Annotation> annotations = new ArrayList<>();
+    for (int i = 0; i < this.annotations.size(); i++) {
+      annotations.add(this.annotations.get(this.annotations.keyAt(i)));
+    }
+    return annotations;
+  }
+
+  private MarkerView prepareViewMarker(BaseMarkerViewOptions markerViewOptions) {
+    MarkerView marker = markerViewOptions.getMarker();
+    Icon icon = markerViewOptions.getIcon();
+    if (icon == null) {
+      icon = IconFactory.getInstance(mapView.getContext()).defaultMarkerView();
+    }
+    marker.setIcon(icon);
+    return marker;
+  }
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java
new file mode 100644
index 00000000000..d646e0ac499
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java
@@ -0,0 +1,39 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.graphics.RectF;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
+import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
+
+import java.util.List;
+
+/**
+ * Interface that defines convenient methods for working with a {@link Marker}'s collection.
+ */
+interface Markers {
+  Marker addBy(@NonNull BaseMarkerOptions markerOptions, @NonNull MapboxMap mapboxMap);
+
+  List<Marker> addBy(@NonNull List<? extends BaseMarkerOptions> markerOptionsList, @NonNull MapboxMap mapboxMap);
+
+  void update(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap);
+
+  List<Marker> obtainAll();
+
+  List<Marker> obtainAllIn(@NonNull RectF rectangle);
+
+  MarkerView addViewBy(@NonNull BaseMarkerViewOptions markerOptions, @NonNull MapboxMap mapboxMap,
+                       @Nullable MarkerViewManager.OnMarkerViewAddedListener onMarkerViewAddedListener);
+
+  List<MarkerView> addViewsBy(@NonNull List<? extends BaseMarkerViewOptions> markerViewOptions,
+                              @NonNull MapboxMap mapboxMap);
+
+  List<MarkerView> obtainViewsIn(@NonNull RectF rectangle);
+
+  void reload();
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
index cf8faa9012e..ae4a8ee8d23 100755
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
@@ -13,6 +13,7 @@
 import android.util.DisplayMetrics;
 import android.view.Surface;
 
+import com.mapbox.mapboxsdk.LibraryLoader;
 import com.mapbox.mapboxsdk.annotations.Icon;
 import com.mapbox.mapboxsdk.annotations.Marker;
 import com.mapbox.mapboxsdk.annotations.Polygon;
@@ -63,12 +64,8 @@ final class NativeMapView {
   // Listener invoked to return a bitmap of the map
   private MapboxMap.SnapshotReadyCallback snapshotReadyCallback;
 
-  //
-  // Static methods
-  //
-
   static {
-    System.loadLibrary("mapbox-gl");
+    LibraryLoader.load();
   }
 
   //
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java
new file mode 100644
index 00000000000..bcb94a975cf
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java
@@ -0,0 +1,104 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.support.annotation.NonNull;
+import android.support.v4.util.LongSparseArray;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+import com.mapbox.mapboxsdk.annotations.Polygon;
+import com.mapbox.mapboxsdk.annotations.PolygonOptions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import timber.log.Timber;
+
+/**
+ * Encapsulates {@link Polygon}'s functionality.
+ */
+class PolygonContainer implements Polygons {
+
+  private final NativeMapView nativeMapView;
+  private final LongSparseArray<Annotation> annotations;
+
+  PolygonContainer(NativeMapView nativeMapView, LongSparseArray<Annotation> annotations) {
+    this.nativeMapView = nativeMapView;
+    this.annotations = annotations;
+  }
+
+  @Override
+  public Polygon addBy(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) {
+    Polygon polygon = polygonOptions.getPolygon();
+    if (!polygon.getPoints().isEmpty()) {
+      long id = nativeMapView != null ? nativeMapView.addPolygon(polygon) : 0;
+      polygon.setId(id);
+      polygon.setMapboxMap(mapboxMap);
+      annotations.put(id, polygon);
+    }
+    return polygon;
+  }
+
+  @Override
+  public List<Polygon> addBy(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap) {
+    int count = polygonOptionsList.size();
+
+    Polygon polygon;
+    List<Polygon> polygons = new ArrayList<>(count);
+    if (count > 0) {
+      for (PolygonOptions polygonOptions : polygonOptionsList) {
+        polygon = polygonOptions.getPolygon();
+        if (!polygon.getPoints().isEmpty()) {
+          polygons.add(polygon);
+        }
+      }
+
+      long[] ids = null;
+      if (nativeMapView != null) {
+        ids = nativeMapView.addPolygons(polygons);
+      }
+
+      long id = 0;
+      for (int i = 0; i < polygons.size(); i++) {
+        polygon = polygons.get(i);
+        polygon.setMapboxMap(mapboxMap);
+        if (ids != null) {
+          id = ids[i];
+        } else {
+          // unit test
+          id++;
+        }
+        polygon.setId(id);
+        annotations.put(id, polygon);
+      }
+    }
+    return polygons;
+  }
+
+  @Override
+  public void update(Polygon polygon) {
+    if (!isAddedToMap(polygon)) {
+      Timber.w("Attempting to update non-added Polygon with value %s", polygon);
+      return;
+    }
+
+    nativeMapView.updatePolygon(polygon);
+    annotations.setValueAt(annotations.indexOfKey(polygon.getId()), polygon);
+  }
+
+  @Override
+  public List<Polygon> obtainAll() {
+    List<Polygon> polygons = new ArrayList<>();
+    Annotation annotation;
+    for (int i = 0; i < annotations.size(); i++) {
+      annotation = annotations.get(annotations.keyAt(i));
+      if (annotation instanceof Polygon) {
+        polygons.add((Polygon) annotation);
+      }
+    }
+    return polygons;
+  }
+
+  private boolean isAddedToMap(Annotation annotation) {
+    return annotation != null && annotation.getId() != -1 && annotations.indexOfKey(annotation.getId()) != -1;
+  }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java
new file mode 100644
index 00000000000..2a0190b5bad
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java
@@ -0,0 +1,22 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.support.annotation.NonNull;
+
+import com.mapbox.mapboxsdk.annotations.Polygon;
+import com.mapbox.mapboxsdk.annotations.PolygonOptions;
+
+import java.util.List;
+
+/**
+ * Interface that defines convenient methods for working with a {@link Polygon}'s collection.
+ */
+interface Polygons {
+  Polygon addBy(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap);
+
+  List<Polygon> addBy(@NonNull List<PolygonOptions> polygonOptionsList, @NonNull MapboxMap mapboxMap);
+
+  void update(Polygon polygon);
+
+  List<Polygon> obtainAll();
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java
new file mode 100644
index 00000000000..7483f1082be
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java
@@ -0,0 +1,105 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.support.annotation.NonNull;
+import android.support.v4.util.LongSparseArray;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+import com.mapbox.mapboxsdk.annotations.Polyline;
+import com.mapbox.mapboxsdk.annotations.PolylineOptions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import timber.log.Timber;
+
+/**
+ * Encapsulates {@link Polyline}'s functionality.
+ */
+class PolylineContainer implements Polylines {
+
+  private final NativeMapView nativeMapView;
+  private final LongSparseArray<Annotation> annotations;
+
+  PolylineContainer(NativeMapView nativeMapView, LongSparseArray<Annotation> annotations) {
+    this.nativeMapView = nativeMapView;
+    this.annotations = annotations;
+  }
+
+  @Override
+  public Polyline addBy(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) {
+    Polyline polyline = polylineOptions.getPolyline();
+    if (!polyline.getPoints().isEmpty()) {
+      long id = nativeMapView != null ? nativeMapView.addPolyline(polyline) : 0;
+      polyline.setMapboxMap(mapboxMap);
+      polyline.setId(id);
+      annotations.put(id, polyline);
+    }
+    return polyline;
+  }
+
+  @Override
+  public List<Polyline> addBy(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap) {
+    int count = polylineOptionsList.size();
+    Polyline polyline;
+    List<Polyline> polylines = new ArrayList<>(count);
+
+    if (count > 0) {
+      for (PolylineOptions options : polylineOptionsList) {
+        polyline = options.getPolyline();
+        if (!polyline.getPoints().isEmpty()) {
+          polylines.add(polyline);
+        }
+      }
+
+      long[] ids = null;
+      if (nativeMapView != null) {
+        ids = nativeMapView.addPolylines(polylines);
+      }
+
+      long id = 0;
+      Polyline p;
+
+      for (int i = 0; i < polylines.size(); i++) {
+        p = polylines.get(i);
+        p.setMapboxMap(mapboxMap);
+        if (ids != null) {
+          id = ids[i];
+        } else {
+          // unit test
+          id++;
+        }
+        p.setId(id);
+        annotations.put(id, p);
+      }
+    }
+    return polylines;
+  }
+
+  @Override
+  public void update(Polyline polyline) {
+    if (!isAddedToMap(polyline)) {
+      Timber.w("Attempting to update non-added Polyline with value %s", polyline);
+    }
+
+    nativeMapView.updatePolyline(polyline);
+    annotations.setValueAt(annotations.indexOfKey(polyline.getId()), polyline);
+  }
+
+  @Override
+  public List<Polyline> obtainAll() {
+    List<Polyline> polylines = new ArrayList<>();
+    Annotation annotation;
+    for (int i = 0; i < annotations.size(); i++) {
+      annotation = annotations.get(annotations.keyAt(i));
+      if (annotation instanceof Polyline) {
+        polylines.add((Polyline) annotation);
+      }
+    }
+    return polylines;
+  }
+
+  private boolean isAddedToMap(Annotation annotation) {
+    return annotation != null && annotation.getId() != -1 && annotations.indexOfKey(annotation.getId()) != -1;
+  }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java
new file mode 100644
index 00000000000..c9a865cdd01
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java
@@ -0,0 +1,22 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import android.support.annotation.NonNull;
+
+import com.mapbox.mapboxsdk.annotations.Polyline;
+import com.mapbox.mapboxsdk.annotations.PolylineOptions;
+
+import java.util.List;
+
+/**
+ * Interface that defines convenient methods for working with a {@link Polyline}'s collection.
+ */
+interface Polylines {
+  Polyline addBy(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap);
+
+  List<Polyline> addBy(@NonNull List<PolylineOptions> polylineOptionsList, @NonNull MapboxMap mapboxMap);
+
+  void update(Polyline polyline);
+
+  List<Polyline> obtainAll();
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/NativeConnectivityListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/NativeConnectivityListener.java
index 76ce1de9d73..ae748592285 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/NativeConnectivityListener.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/NativeConnectivityListener.java
@@ -1,12 +1,14 @@
 package com.mapbox.mapboxsdk.net;
 
+import com.mapbox.mapboxsdk.LibraryLoader;
+
 /**
  * Updates the native library's connectivity state
  */
 class NativeConnectivityListener implements ConnectivityListener {
 
   static {
-    System.loadLibrary("mapbox-gl");
+    LibraryLoader.load();
   }
 
   private long nativePtr;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
index 225278b17d9..50ae6716e6d 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java
@@ -5,6 +5,7 @@
 import android.os.Looper;
 import android.support.annotation.NonNull;
 
+import com.mapbox.mapboxsdk.LibraryLoader;
 import com.mapbox.mapboxsdk.R;
 import com.mapbox.mapboxsdk.geometry.LatLngBounds;
 import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
@@ -25,7 +26,7 @@ public class OfflineManager {
   //
 
   static {
-    System.loadLibrary("mapbox-gl");
+    LibraryLoader.load();
   }
 
   // Native peer pointer
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
index a55e8dd8485..5ed6579e1cc 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java
@@ -5,6 +5,7 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 
+import com.mapbox.mapboxsdk.LibraryLoader;
 import com.mapbox.mapboxsdk.storage.FileSource;
 
 import java.lang.annotation.Retention;
@@ -22,7 +23,7 @@ public class OfflineRegion {
   //
 
   static {
-    System.loadLibrary("mapbox-gl");
+    LibraryLoader.load();
   }
 
   // Members
@@ -205,7 +206,7 @@ private boolean deliverMessages() {
 
   /**
    * Constructor
-   *
+   * <p>
    * For JNI use only, to create a new offline region, use
    * {@link OfflineManager#createOfflineRegion} instead.
    */
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/constants/AppConstant.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/constants/AppConstant.java
similarity index 58%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/constants/AppConstant.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/constants/AppConstant.java
index aaef7f8a514..cb654aa5567 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/constants/AppConstant.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/constants/AppConstant.java
@@ -1,4 +1,4 @@
-package com.mapbox.mapboxsdk.testapp.model.constants;
+package com.mapbox.mapboxsdk.constants;
 
 public class AppConstant {
 
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java
new file mode 100644
index 00000000000..0d592f9bb37
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java
@@ -0,0 +1,81 @@
+package com.mapbox.mapboxsdk.maps;
+
+import android.support.v4.util.LongSparseArray;
+
+import com.mapbox.mapboxsdk.annotations.Annotation;
+import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static junit.framework.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class AnnotationManagerTest {
+
+  @Test
+  public void checksAddAMarker() throws Exception {
+    NativeMapView aNativeMapView = mock(NativeMapView.class);
+    MapView aMapView = mock(MapView.class);
+    LongSparseArray<Annotation> annotationsArray = new LongSparseArray<>();
+    MarkerViewManager aMarkerViewManager = mock(MarkerViewManager.class);
+    IconManager aIconManager = mock(IconManager.class);
+    Annotations annotations = new AnnotationContainer(aNativeMapView, annotationsArray);
+    Markers markers = new MarkerContainer(aNativeMapView, aMapView, annotationsArray, aIconManager, aMarkerViewManager);
+    Polygons polygons = new PolygonContainer(aNativeMapView, annotationsArray);
+    Polylines polylines = new PolylineContainer(aNativeMapView, annotationsArray);
+    AnnotationManager annotationManager = new AnnotationManager(aNativeMapView, aMapView, annotationsArray,
+      aMarkerViewManager, aIconManager, annotations, markers, polygons, polylines);
+    Marker aMarker = mock(Marker.class);
+    long aId = 5L;
+    when(aNativeMapView.addMarker(aMarker)).thenReturn(aId);
+    BaseMarkerOptions aMarkerOptions = mock(BaseMarkerOptions.class);
+    MapboxMap aMapboxMap = mock(MapboxMap.class);
+    when(aMarkerOptions.getMarker()).thenReturn(aMarker);
+
+    annotationManager.addMarker(aMarkerOptions, aMapboxMap);
+
+    assertEquals(aMarker, annotationManager.getAnnotations().get(0));
+    assertEquals(aMarker, annotationManager.getAnnotation(aId));
+  }
+
+  @Test
+  public void checksAddMarkers() throws Exception {
+    NativeMapView aNativeMapView = mock(NativeMapView.class);
+    MapView aMapView = mock(MapView.class);
+    LongSparseArray<Annotation> annotationsArray = new LongSparseArray<>();
+    MarkerViewManager aMarkerViewManager = mock(MarkerViewManager.class);
+    IconManager aIconManager = mock(IconManager.class);
+    Annotations annotations = new AnnotationContainer(aNativeMapView, annotationsArray);
+    Markers markers = new MarkerContainer(aNativeMapView, aMapView, annotationsArray, aIconManager, aMarkerViewManager);
+    Polygons polygons = new PolygonContainer(aNativeMapView, annotationsArray);
+    Polylines polylines = new PolylineContainer(aNativeMapView, annotationsArray);
+    AnnotationManager annotationManager = new AnnotationManager(aNativeMapView, aMapView, annotationsArray,
+      aMarkerViewManager, aIconManager, annotations, markers, polygons, polylines);
+    long firstId = 1L;
+    long secondId = 2L;
+    List<BaseMarkerOptions> markerList = new ArrayList<>();
+    MarkerOptions firstMarkerOption = new MarkerOptions().position(new LatLng()).title("first");
+    MarkerOptions secondMarkerOption = new MarkerOptions().position(new LatLng()).title("second");
+    markerList.add(firstMarkerOption);
+    markerList.add(secondMarkerOption);
+    MapboxMap aMapboxMap = mock(MapboxMap.class);
+    when(aNativeMapView.addMarker(any(Marker.class))).thenReturn(firstId, secondId);
+
+    annotationManager.addMarkers(markerList, aMapboxMap);
+
+    assertEquals(2, annotationManager.getAnnotations().size());
+    assertEquals("first", ((Marker) annotationManager.getAnnotations().get(0)).getTitle());
+    assertEquals("second", ((Marker) annotationManager.getAnnotations().get(1)).getTitle());
+    assertEquals("first", ((Marker) annotationManager.getAnnotation(firstId)).getTitle());
+    assertEquals("second", ((Marker) annotationManager.getAnnotation(secondId)).getTitle());
+  }
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java
rename to platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/platform/android/MapboxGLAndroidSDK/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
similarity index 100%
rename from platform/android/MapboxGLAndroidSDKTestApp/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
rename to platform/android/MapboxGLAndroidSDK/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
index 3dc20c9f5b5..7c263652af7 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
+++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
@@ -32,10 +32,6 @@ android {
         disable 'InvalidPackage'
     }
 
-    testOptions {
-        unitTests.returnDefaultValues = true
-    }
-
     buildTypes {
         debug {
             testCoverageEnabled = true
@@ -78,8 +74,6 @@ dependencies {
     }
 
     // Testing dependencies
-    testCompile rootProject.ext.dep.junit
-    testCompile rootProject.ext.dep.mockito
     androidTestCompile rootProject.ext.dep.testSpoonRunner
     androidTestCompile rootProject.ext.dep.supportAnnotations
     androidTestCompile rootProject.ext.dep.testRunner