-
Notifications
You must be signed in to change notification settings - Fork 9.8k
[google_maps_flutter] Overhaul lifecycle management in GoogleMapsPlugin #3213
Conversation
…fecycleObserver. That observer is registered to a lifecycle passed via the new LifecycleProvider interface, which has 3 implementations: 1. For v2 plugin registration, GoogleMapsPlugin implements the interface and holds the lifecycle in a field, which is itself controlled by ActivityAware methods. 2. For v1 plugin registration, if the activity implements LifecycleOwner, it's lifecycle is used directly. 3. For v1 plugin registration, if the activity does not implement LifecycleOwner, a proxy lifecycle is created via ActivityLifecycleCallbacks. I'm suspicious that there may still be a bug where a GoogleMapController instance can be shared across multiple activities and therefore leak the activity and lifecycle.
…Event() rather than moveToState().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great! thanks!
Left a few minor comments.
@@ -1,3 +1,13 @@ | |||
## 1.1.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't introduce new features so should be a patch bump (1.0.5)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
pluginBinding = binding; | ||
binding | ||
.getPlatformViewRegistry() | ||
.registerViewFactory(VIEW_TYPE, new GoogleMapFactory(binding.getBinaryMessenger(), this)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about creating a separate LifecycleProvider
object which is just the lifecycle provider and passing that here (instead of having GoogleMapsPlugin implement LifecycleProvider). My worry is that future developers adding members to GoogleMapsPlugins will miss the fact that a reference to it is being retained by GoogleMapFactory...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, done
getApplication().registerActivityLifecycleCallbacks(this); | ||
} | ||
void init() { | ||
lifecycleProvider.getLifecycle().addObserver(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure so double checking - will we get the initial notification for the current state?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, from addObserver
's documentation:
The given observer will be brought to the current state of the LifecycleOwner. For example, if the LifecycleOwner is in
State#STARTED
state, the given observer will receiveEvent#ON_CREATE
,Event#ON_START
events
getApplication().unregisterActivityLifecycleCallbacks(this); | ||
Lifecycle lifecycle = lifecycleProvider.getLifecycle(); | ||
if (lifecycle != null) { | ||
lifecycle.removeObserver(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(not sure) do we need to protect against calling removeObserver
twice? (here and from onDestroy)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, operation is idempotent
@@ -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: 1.0.4 | |||
version: 1.1.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't forget to update the version here as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
public void onStop(@NonNull LifecycleOwner owner) { | ||
state.set(CREATED); | ||
} | ||
private static final class ProxyLifecycleProvider |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Can you add some comments here describing that this is used make a lifecycle our of the application lifecycle calls?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
})); | ||
} else { | ||
ProxyLifecycleProvider proxyLifecycleProvider = new ProxyLifecycleProvider(activity); | ||
activity.getApplication().registerActivityLifecycleCallbacks(proxyLifecycleProvider); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
optional nit: I'd consider moving this into ProxyLifecycleProvider
to keep all the hackery in one class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
…ycleCallbacks registration inside its constructor.
…ead create an anonymous inner class.
VIEW_TYPE, | ||
new GoogleMapFactory( | ||
binding.getBinaryMessenger(), | ||
new LifecycleProvider() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this going to keep an implicit reference to the outer class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It actually keeps an explicit reference to the outer class, because lifecycle
is a field in the outer class. I don't think that's avoidable because only the plugin itself can implement ActivityAware
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm probably missing something obvious... what fails if:
GoogleMapsPlugin holds a reference to the provider (an implementation with a lifecycle field) and sets the lifecycle when attached/detached to the activity, and we pass that provider reference to GoogleMapFactory, won't we avoid a reference from GoogleMapFactory to GoogleMapsPlugin in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could do that, but it doesn't really matter and ends up being more boiler plate. My assumption is that the scope of GoogleMapsPlugin is greater than the scope of GoogleMapFactory, so it doesn't matter if GoogleMapFactory transitively captures the GooglemapsPlugin instance. If that assumption is violated, all sorts of worse things happen than this particular leak.
I guess my question is what problem are you trying to solve?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are correct, thanks for pushing back and keeping the code simple!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
"Windows Plugins" was fixed and the re-run is green (the presubmit indicator is stale). |
* master: (48 commits) [video_player] Add toString() to Caption (flutter#3233) [google_maps_flutter_web] Show one InfoWindow at a time. (flutter#3224) [in_app_purchase] Bump version (flutter#3227) [google_maps_flutter] Overhaul lifecycle management in GoogleMapsPlugin (flutter#3213) [in_app_purchase] Remove the custom analysis options, fix failing lints. (flutter#3220) [video_player]Fixes Playing video from asset on Android (flutter#3123) [camera] Added documentation about video not working correctly on Android emulators (flutter#3180) Revert "update api" update api [wifi_info_flutter] Method channel name fixed for android (flutter#3207) [share] Fix bug on iPad where `origin` is null and enable XCUITests in the repo (flutter#3210) [google_maps_flutter] Clean up google_maps_flutter plugin (flutter#3206) Exclude generated_plugin_registrant.cc (flutter#3198) broaden the constraint on package:vm_service (flutter#3208) Remove unnecessary work around from test in prep for vm_service migration (flutter#3209) Add windows directory to examples (flutter#3149) [video_player] Upgrade ExoPlayer (flutter#3204) [android_alarm_manager] Removed deprecated display1 (flutter#3200) [Connectivity] wifi removal (flutter#3173) [wifi_info_flutter] make it ready for initial 1.0.0 release (flutter#3191) ...
@math1man Is there a reason you didn't use this? https://github.com/flutter/plugins/tree/master/packages/flutter_plugin_android_lifecycle |
I do exactly that here. |
My point was your suppose to import that package and use its adapter. |
Lines 90-93 of GoogleMapsPlugin are this:
Could you elaborate on what you mean? |
…in (flutter#3213) GoogleMapController is now uniformly driven by implementing DefaultLifecycleObserver. That observer is registered to a lifecycle passed via the new LifecycleProvider interface, which has 3 implementations: 1. For v2 plugin registration, GoogleMapsPlugin implements the interface and holds the lifecycle in a field, which is itself controlled by ActivityAware methods. 2. For v1 plugin registration, if the activity implements LifecycleOwner, it's lifecycle is used directly. 3. For v1 plugin registration, if the activity does not implement LifecycleOwner, a proxy lifecycle is created via ActivityLifecycleCallbacks.
Description
Overhaul lifecycle management in GoogleMapsPlugin.
GoogleMapController is now uniformly driven by implementing DefaultLifecycleObserver. That observer is registered to a lifecycle passed via the new LifecycleProvider interface, which has 3 implementations:
I'm suspicious that there may still be a bug where a GoogleMapController instance can be shared across multiple activities and therefore leak the activity and lifecycle.
Related Issues
getActivityHashCode
inGoogleMapController.java
might return -1 if the activity is in the background flutter#69128Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]
). This will ensure a smooth and quick review process.///
).flutter analyze
) does not report any problems on my PR.Breaking Change
Does your PR require plugin users to manually update their apps to accommodate your change?