[google_maps_flutter] Android: only destroy mapView once (#2772)

diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md
index 66ca6fe..0be2d8f 100644
--- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md
+++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.5.28+1
+
+* Android: Make sure map view only calls onDestroy once.
+* Android: Fix a memory leak regression caused in `0.5.26+4`.
+
 ## 0.5.28
 
 * Android: Add liteModeEnabled option.
diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java b/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java
index 40cce49..58e8bb0 100644
--- a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java
+++ b/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java
@@ -71,7 +71,7 @@
   private final AtomicInteger activityState;
   private final MethodChannel methodChannel;
   private final GoogleMapOptions options;
-  private final MapView mapView;
+  @Nullable private MapView mapView;
   private GoogleMap googleMap;
   private boolean trackCameraPosition = false;
   private boolean myLocationEnabled = false;
@@ -534,6 +534,7 @@
     disposed = true;
     methodChannel.setMethodCallHandler(null);
     setGoogleMapListener(null);
+    destroyMapViewIfNecessary();
     getApplication().unregisterActivityLifecycleCallbacks(this);
   }
 
@@ -618,7 +619,7 @@
     if (disposed || activity.hashCode() != getActivityHashCode()) {
       return;
     }
-    mapView.onDestroy();
+    destroyMapViewIfNecessary();
   }
 
   // DefaultLifecycleObserver and OnSaveInstanceStateListener
@@ -668,7 +669,7 @@
     if (disposed) {
       return;
     }
-    mapView.onDestroy();
+    destroyMapViewIfNecessary();
   }
 
   @Override
@@ -891,6 +892,14 @@
     }
   }
 
+  private void destroyMapViewIfNecessary() {
+    if (mapView == null) {
+      return;
+    }
+    mapView.onDestroy();
+    mapView = null;
+  }
+
   public void setIndoorEnabled(boolean indoorEnabled) {
     this.indoorEnabled = indoorEnabled;
   }
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle b/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle
index 16e93d9..786c784 100644
--- a/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle
+++ b/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle
@@ -51,14 +51,24 @@
             signingConfig signingConfigs.debug
         }
     }
+
+    testOptions {
+        unitTests {
+        includeAndroidResources = true
+        }
+    }
+
+    dependencies {
+        testImplementation 'junit:junit:4.12'
+        androidTestImplementation 'androidx.test:runner:1.1.1'
+        androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
+        testImplementation 'androidx.test:core:1.2.0'
+        testImplementation "org.robolectric:robolectric:4.3.1"
+        testImplementation 'org.mockito:mockito-core:3.2.4'
+        testImplementation 'com.google.android.gms:play-services-maps:17.0.0'
+    }
 }
 
 flutter {
     source '../..'
 }
-
-dependencies {
-    testImplementation 'junit:junit:4.12'
-    androidTestImplementation 'androidx.test:runner:1.1.1'
-    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
-}
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/app/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java b/packages/google_maps_flutter/google_maps_flutter/example/android/app/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java
new file mode 100644
index 0000000..8401a38
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter/example/android/app/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java
@@ -0,0 +1,64 @@
+package io.flutter.plugins.googlemaps;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Application;
+import android.content.Context;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.test.core.app.ApplicationProvider;
+import com.google.android.gms.maps.GoogleMap;
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.view.FlutterMain;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class GoogleMapControllerTest {
+
+  private Context context;
+  private Application application;
+  private GoogleMapController googleMapController;
+
+  @Mock BinaryMessenger mockMessenger;
+  @Mock GoogleMap mockGoogleMap;
+  @Mock LifecycleOwner lifecycleOwner;
+
+  @BeforeClass()
+  public static void BeforeClass() {
+    FlutterMain.setIsRunningInRobolectricTest(true);
+  }
+
+  @Before
+  public void before() {
+    MockitoAnnotations.initMocks(this);
+    context = ApplicationProvider.getApplicationContext();
+    application = ApplicationProvider.getApplicationContext();
+    googleMapController =
+        new GoogleMapController(
+            0, context, new AtomicInteger(1), mockMessenger, application, null, null, 0, null);
+    googleMapController.init();
+  }
+
+  @Test
+  public void DisposeReleaseTheMap() throws InterruptedException {
+    googleMapController.onMapReady(mockGoogleMap);
+    assertTrue(googleMapController != null);
+    googleMapController.dispose();
+    assertNull(googleMapController.getView());
+  }
+
+  @Test
+  public void OnDestroyReleaseTheMap() throws InterruptedException {
+    googleMapController.onMapReady(mockGoogleMap);
+    assertTrue(googleMapController != null);
+    googleMapController.onDestroy(lifecycleOwner);
+    assertNull(googleMapController.getView());
+  }
+}
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/packages/google_maps_flutter/google_maps_flutter/example/android/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..1f0955d
--- /dev/null
+++ b/packages/google_maps_flutter/google_maps_flutter/example/android/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml
index 4b30226..6214086 100644
--- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml
+++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml
@@ -1,7 +1,7 @@
 name: google_maps_flutter
 description: A Flutter plugin for integrating Google Maps in iOS and Android applications.
 homepage: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter
-version: 0.5.28
+version: 0.5.28+1
 
 dependencies:
   flutter: