New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Use AndroidView overload which re-uses MapView to improve performance in lazy layouts #436
base: main
Are you sure you want to change the base?
feat: Use AndroidView overload which re-uses MapView to improve performance in lazy layouts #436
Conversation
merge latest
This will make sure that the underlying MapView is re-used in a LazyColumn (and elsewhere) which will significantly improve scrolling performance in LazyColumn. If this overload isn't used the MapView will be destroyed every time it leaves composition. The MapView will still be destroyed when the parent node leaves the composition.
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
Hi @philip-segerfast. As far as I read in the documentation, we need to provide a non-null value in the |
Hi @kikoso, it is enough to make Cheers |
Upon further examination, it appears that executing a reset within Some other notes:
The PR is quite ready except for the |
We'll wait until this feature moves out of alpha and doesn't cause crashes before we merge. |
maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt
Outdated
Show resolved
Hide resolved
maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt
Outdated
Show resolved
Hide resolved
Thanks for the review and everything in between, Uli @bubenheimer! It was an honor. |
Looks great! LGT @kikoso |
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
@dkhawk Would you look at the PR and include it in the plans please? |
This is a great change! I spent some time with the demo activity and it works pretty well, except I can't scrolling the maps vertically. That might be nice to look into. Barring that we should be able to merge in this change into the 5.1.x release which should be soon. |
fun CoroutineScope.launchSubcomposition(mapView: MapView): Job { | ||
val mapCompositionContent: @Composable () -> Unit = { | ||
MapUpdater( | ||
mergeDescendants = mergeDescendants, | ||
contentDescription = currentContentDescription, | ||
cameraPositionState = currentCameraPositionState, | ||
contentPadding = currentContentPadding, | ||
locationSource = currentLocationSource, | ||
mapProperties = currentMapProperties, | ||
mapUiSettings = currentUiSettings, | ||
) |
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 would be useful to move launchSubcomposition()
to the top level, to help avoid direct references to GoogleMap()
parameters from the long-running lambda closure. There have been subtle bugs in this project caused by such references, they're easy to miss.
This should incorporate the changes from recently approved #522, grouping together all MapUpdater()
parameters.
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.
Alright 👍
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've incorporated the changes from #522. Please tell me if there's anything you'd like to change.
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.
Looks great, thank you! The MapUpdater()
changes are excellent.
return launch(start = CoroutineStart.UNDISPATCHED) { | ||
val map = mapView.awaitMap() | ||
val composition = Composition( | ||
applier = MapApplier(map, mapView, mapClickListeners), |
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.
mapClickListeners
should be moved out of MapApplier
, and passed as a parameter to MapClickListenerUpdater()
. @philip-segerfast determined earlier in the review process that MapClickListeners
does not actually have to be a singleton. (I verified the finding.)
It's a trivial change that will also prepare MapApplier
for potential future use in ReusableComposition
.
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've implemented this. Please tell me what you think.
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.
@philip-segerfast I've realized that the MapClickListeners change that I proposed is not valid, sadly. It will work in this case, because MapClickListeners actually is a singleton within the scope of a non-reused composition anyway. However, from the perspective of MapClickListenerUpdater(MapClickListeners)
as a standalone Composable function it is not correct, as passing a different MapClickListener
on recomposition will not do the right thing. The initial listener is too baked into the lambdas there.
Let's undo this part of the commit and leave it for the future, it does not look trivial. My sincere apologies!
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.
No worries. This has been reverted.
Hi, @dkhawk! I made the maps pannable in the demo if you first pan them horizontally so that you still can easily scroll the list. Is this enough for you or would you like the maps to completely intercept the touch events from the |
…operty from MapApplier [see desc.] - Wrap MapUpdater parameters inside a MapUpdaterState data class - Remove MapClickListeners property from MapApplier - Pass MapClickListeners directly to MapClickListenerUpdater
mapUpdaterState: MapUpdaterState, | ||
parentComposition: CompositionContext, | ||
clickListeners: MapClickListeners, | ||
content: (@Composable @GoogleMapComposable () -> Unit)?, | ||
mapView: MapView |
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: the parameter order is non-standard. The content
lambda should be the last parameter.
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.
Also, content
is now a regular non-nullable lambda after the latest changes from main. This will also slightly simplify its usage below.
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.
Good point. fixed.
event.targetState | ||
when (event) { | ||
Lifecycle.Event.ON_CREATE -> { | ||
// Skip calling mapView.onCreate if the lifecycle did not go through onDestroy - in | ||
// this case the GoogleMap composable also doesn't leave the composition. So, | ||
// recreating the map does not restore state properly which must be avoided. | ||
if (previousState.value != Lifecycle.Event.ON_STOP) { | ||
this.onCreate(Bundle()) | ||
CompositionLocalProvider( | ||
LocalCameraPositionState provides currentCameraPositionState | ||
) { | ||
content?.invoke() |
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.
currentCameraPositionState
is not valid here. After the content
type change, this whole expression can change to simply:
CompositionLocalProvider(
LocalCameraPositionState provides mapUpdaterState.cameraPositionState,
content
)
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.
Fixed 👍
This PR uses an
AndroidView
overload which allows re-using of the underlying view. This improves performance substantially when usingGoogleMap
in a Lazy layout, likeLazyColumn
.This PR also incorporates @bubenheimer's changes from #522.
Fixes #285, #492 🦕