Skip to content

Commit a98bc81

Browse files
committed
[google_maps_flutter] Clean up google_maps_flutter plugin (flutter#3206)
1. A few minor formatting changes and additions of @nullable annotations 2. Removed pass-through of activityHashCode. In the legacy plugin use case, the value is always -1 (which has been inlined). In the new plugin use case, the value should never be used because it uses DefaultLifecycleCallbacks instead of ActivityLifecycleCallbacks. 3. Replaced custom lifecycle state ints with androidx.lifecycle.Lifecycle.State enum. Also simplify paused/stopped states, which don't need their own dedicated states. Paused == started and stopped == created. 4. Fixed a bug where the Lifecycle object was being leaked onDetachFromActivity by nulling out the field. 5. Moved GoogleMapListener to its own file. Declaring multiple top level classes in the same file is bad practice.
1 parent c5ca20e commit a98bc81

File tree

5 files changed

+266
-114
lines changed

5 files changed

+266
-114
lines changed

packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapBuilder.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@
44

55
package io.flutter.plugins.googlemaps;
66

7+
import android.app.Application;
78
import android.content.Context;
89
import android.graphics.Rect;
10+
import androidx.annotation.Nullable;
11+
import androidx.lifecycle.Lifecycle;
12+
import androidx.lifecycle.Lifecycle.State;
913
import com.google.android.gms.maps.GoogleMapOptions;
1014
import com.google.android.gms.maps.model.CameraPosition;
1115
import com.google.android.gms.maps.model.LatLngBounds;
1216
import io.flutter.plugin.common.BinaryMessenger;
17+
import io.flutter.plugin.common.PluginRegistry;
1318

1419
class GoogleMapBuilder implements GoogleMapOptionsSink {
1520
private final GoogleMapOptions options = new GoogleMapOptions();
@@ -28,11 +33,15 @@ class GoogleMapBuilder implements GoogleMapOptionsSink {
2833
GoogleMapController build(
2934
int id,
3035
Context context,
36+
State lifecycleState,
3137
BinaryMessenger binaryMessenger,
32-
LifecycleProvider lifecycleProvider) {
38+
@Nullable Application application,
39+
@Nullable Lifecycle lifecycle,
40+
@Nullable PluginRegistry.Registrar registrar) {
3341
final GoogleMapController controller =
34-
new GoogleMapController(id, context, binaryMessenger, lifecycleProvider, options);
35-
controller.init();
42+
new GoogleMapController(
43+
id, context, binaryMessenger, application, lifecycle, registrar, options);
44+
controller.init(lifecycleState);
3645
controller.setMyLocationEnabled(myLocationEnabled);
3746
controller.setMyLocationButtonEnabled(myLocationButtonEnabled);
3847
controller.setIndoorEnabled(indoorEnabled);

packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java

Lines changed: 118 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
import android.Manifest;
88
import android.annotation.SuppressLint;
9+
import android.app.Activity;
10+
import android.app.Application;
911
import android.content.Context;
1012
import android.content.pm.PackageManager;
1113
import android.graphics.Bitmap;
@@ -17,6 +19,7 @@
1719
import androidx.annotation.Nullable;
1820
import androidx.lifecycle.DefaultLifecycleObserver;
1921
import androidx.lifecycle.Lifecycle;
22+
import androidx.lifecycle.Lifecycle.State;
2023
import androidx.lifecycle.LifecycleOwner;
2124
import com.google.android.gms.maps.CameraUpdate;
2225
import com.google.android.gms.maps.GoogleMap;
@@ -36,6 +39,7 @@
3639
import io.flutter.plugin.common.BinaryMessenger;
3740
import io.flutter.plugin.common.MethodCall;
3841
import io.flutter.plugin.common.MethodChannel;
42+
import io.flutter.plugin.common.PluginRegistry;
3943
import io.flutter.plugin.platform.PlatformView;
4044
import java.io.ByteArrayOutputStream;
4145
import java.util.ArrayList;
@@ -46,7 +50,8 @@
4650

4751
/** Controller of a single GoogleMaps MapView instance. */
4852
final class GoogleMapController
49-
implements DefaultLifecycleObserver,
53+
implements Application.ActivityLifecycleCallbacks,
54+
DefaultLifecycleObserver,
5055
ActivityPluginBinding.OnSaveInstanceStateListener,
5156
GoogleMapOptionsSink,
5257
MethodChannel.MethodCallHandler,
@@ -70,8 +75,12 @@ final class GoogleMapController
7075
private boolean disposed = false;
7176
private final float density;
7277
private MethodChannel.Result mapReadyResult;
78+
@Nullable private final Lifecycle lifecycle;
7379
private final Context context;
74-
private final LifecycleProvider lifecycleProvider;
80+
// Do not use directly, use getApplication() instead to get correct application object for both v1
81+
// and v2 embedding.
82+
@Nullable private final Application mApplication;
83+
@Nullable private final PluginRegistry.Registrar registrar; // For v1 embedding only.
7584
private final MarkersController markersController;
7685
private final PolygonsController polygonsController;
7786
private final PolylinesController polylinesController;
@@ -85,7 +94,9 @@ final class GoogleMapController
8594
int id,
8695
Context context,
8796
BinaryMessenger binaryMessenger,
88-
LifecycleProvider lifecycleProvider,
97+
@Nullable Application application,
98+
@Nullable Lifecycle lifecycle,
99+
@Nullable PluginRegistry.Registrar registrar,
89100
GoogleMapOptions options) {
90101
this.id = id;
91102
this.context = context;
@@ -94,7 +105,9 @@ final class GoogleMapController
94105
this.density = context.getResources().getDisplayMetrics().density;
95106
methodChannel = new MethodChannel(binaryMessenger, "plugins.flutter.io/google_maps_" + id);
96107
methodChannel.setMethodCallHandler(this);
97-
this.lifecycleProvider = lifecycleProvider;
108+
mApplication = application;
109+
this.lifecycle = lifecycle;
110+
this.registrar = registrar;
98111
this.markersController = new MarkersController(methodChannel);
99112
this.polygonsController = new PolygonsController(methodChannel, density);
100113
this.polylinesController = new PolylinesController(methodChannel, density);
@@ -106,8 +119,30 @@ public View getView() {
106119
return mapView;
107120
}
108121

109-
void init() {
110-
lifecycleProvider.getLifecycle().addObserver(this);
122+
void init(State lifecycleState) {
123+
switch (lifecycleState) {
124+
case RESUMED:
125+
mapView.onCreate(null);
126+
mapView.onStart();
127+
mapView.onResume();
128+
break;
129+
case STARTED:
130+
mapView.onCreate(null);
131+
mapView.onStart();
132+
break;
133+
case CREATED:
134+
mapView.onCreate(null);
135+
break;
136+
case DESTROYED:
137+
case INITIALIZED:
138+
// Nothing to do, the activity has been completely destroyed or not yet created.
139+
break;
140+
}
141+
if (lifecycle != null) {
142+
lifecycle.addObserver(this);
143+
} else {
144+
getApplication().registerActivityLifecycleCallbacks(this);
145+
}
111146
mapView.getMapAsync(this);
112147
}
113148

@@ -472,10 +507,7 @@ public void dispose() {
472507
methodChannel.setMethodCallHandler(null);
473508
setGoogleMapListener(null);
474509
destroyMapViewIfNecessary();
475-
Lifecycle lifecycle = lifecycleProvider.getLifecycle();
476-
if (lifecycle != null) {
477-
lifecycle.removeObserver(this);
478-
}
510+
getApplication().unregisterActivityLifecycleCallbacks(this);
479511
}
480512

481513
private void setGoogleMapListener(@Nullable GoogleMapListener listener) {
@@ -505,7 +537,64 @@ public void onInputConnectionUnlocked() {
505537
// TODO(mklim): Remove this empty override once https://github.com/flutter/flutter/issues/40126 is fixed in stable.
506538
}
507539

508-
// DefaultLifecycleObserver
540+
// Application.ActivityLifecycleCallbacks methods
541+
@Override
542+
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
543+
if (disposed || activity.hashCode() != getActivityHashCode()) {
544+
return;
545+
}
546+
mapView.onCreate(savedInstanceState);
547+
}
548+
549+
@Override
550+
public void onActivityStarted(Activity activity) {
551+
if (disposed || activity.hashCode() != getActivityHashCode()) {
552+
return;
553+
}
554+
mapView.onStart();
555+
}
556+
557+
@Override
558+
public void onActivityResumed(Activity activity) {
559+
if (disposed || activity.hashCode() != getActivityHashCode()) {
560+
return;
561+
}
562+
mapView.onResume();
563+
}
564+
565+
@Override
566+
public void onActivityPaused(Activity activity) {
567+
if (disposed || activity.hashCode() != getActivityHashCode()) {
568+
return;
569+
}
570+
mapView.onPause();
571+
}
572+
573+
@Override
574+
public void onActivityStopped(Activity activity) {
575+
if (disposed || activity.hashCode() != getActivityHashCode()) {
576+
return;
577+
}
578+
mapView.onStop();
579+
}
580+
581+
@Override
582+
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
583+
if (disposed || activity.hashCode() != getActivityHashCode()) {
584+
return;
585+
}
586+
mapView.onSaveInstanceState(outState);
587+
}
588+
589+
@Override
590+
public void onActivityDestroyed(Activity activity) {
591+
if (disposed || activity.hashCode() != getActivityHashCode()) {
592+
return;
593+
}
594+
destroyMapViewIfNecessary();
595+
}
596+
597+
// DefaultLifecycleObserver and OnSaveInstanceStateListener
509598

510599
@Override
511600
public void onCreate(@NonNull LifecycleOwner owner) {
@@ -549,7 +638,6 @@ public void onStop(@NonNull LifecycleOwner owner) {
549638

550639
@Override
551640
public void onDestroy(@NonNull LifecycleOwner owner) {
552-
owner.getLifecycle().removeObserver(this);
553641
if (disposed) {
554642
return;
555643
}
@@ -760,6 +848,24 @@ private int checkSelfPermission(String permission) {
760848
permission, android.os.Process.myPid(), android.os.Process.myUid());
761849
}
762850

851+
private int getActivityHashCode() {
852+
if (registrar != null && registrar.activity() != null) {
853+
return registrar.activity().hashCode();
854+
} else {
855+
// TODO(cyanglaz): Remove `getActivityHashCode()` and use a cached hashCode when creating the view for V1 embedding.
856+
// https://github.com/flutter/flutter/issues/69128
857+
return -1;
858+
}
859+
}
860+
861+
private Application getApplication() {
862+
if (registrar != null && registrar.activity() != null) {
863+
return registrar.activity().getApplication();
864+
} else {
865+
return mApplication;
866+
}
867+
}
868+
763869
private void destroyMapViewIfNecessary() {
764870
if (mapView == null) {
765871
return;

packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapFactory.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,40 @@
44

55
package io.flutter.plugins.googlemaps;
66

7+
import android.app.Application;
78
import android.content.Context;
9+
import androidx.annotation.Nullable;
10+
import androidx.lifecycle.Lifecycle;
11+
import androidx.lifecycle.Lifecycle.State;
812
import com.google.android.gms.maps.model.CameraPosition;
913
import io.flutter.plugin.common.BinaryMessenger;
14+
import io.flutter.plugin.common.PluginRegistry;
1015
import io.flutter.plugin.common.StandardMessageCodec;
1116
import io.flutter.plugin.platform.PlatformView;
1217
import io.flutter.plugin.platform.PlatformViewFactory;
1318
import java.util.Map;
19+
import java.util.concurrent.atomic.AtomicReference;
1420

1521
public class GoogleMapFactory extends PlatformViewFactory {
1622

23+
private final AtomicReference<State> lifecycleState;
1724
private final BinaryMessenger binaryMessenger;
18-
private final LifecycleProvider lifecycleProvider;
25+
@Nullable private final Application application;
26+
@Nullable private final Lifecycle lifecycle;
27+
@Nullable private final PluginRegistry.Registrar registrar; // V1 embedding only.
1928

20-
GoogleMapFactory(BinaryMessenger binaryMessenger, LifecycleProvider lifecycleProvider) {
29+
GoogleMapFactory(
30+
AtomicReference<State> lifecycleState,
31+
BinaryMessenger binaryMessenger,
32+
@Nullable Application application,
33+
@Nullable Lifecycle lifecycle,
34+
@Nullable PluginRegistry.Registrar registrar) {
2135
super(StandardMessageCodec.INSTANCE);
36+
this.lifecycleState = lifecycleState;
2237
this.binaryMessenger = binaryMessenger;
23-
this.lifecycleProvider = lifecycleProvider;
38+
this.application = application;
39+
this.lifecycle = lifecycle;
40+
this.registrar = registrar;
2441
}
2542

2643
@SuppressWarnings("unchecked")
@@ -46,6 +63,7 @@ public PlatformView create(Context context, int id, Object args) {
4663
if (params.containsKey("circlesToAdd")) {
4764
builder.setInitialCircles(params.get("circlesToAdd"));
4865
}
49-
return builder.build(id, context, binaryMessenger, lifecycleProvider);
66+
return builder.build(
67+
id, context, lifecycleState.get(), binaryMessenger, application, lifecycle, registrar);
5068
}
5169
}

0 commit comments

Comments
 (0)