question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. ItΒ collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Possible memory leak when GoogleMap component is disposed but Activity is not destroyed

See original GitHub issue

It seems like there could be a possible memory leak when the GoogleMap is disposed without the activity being destroyed. When it is disposed, the lifecycle observer is removed and onDestroy seems to not be called on the map. Does that seem accurate or is onDestroy being called somewhere else as well?

Steps to reproduce:

  1. Modify the onCreate method in MapSampleActivity to conditionally add the map to the composition a. See sample code below
  2. launch the sample application
  3. optionally place a breakpoint in the onDispose block of MapLifecycle in GoogleMap.kt
  4. optionally place a breakpoint at the start of the when inside the lifecycleObserver extension function in GoogleMap.kt
  5. optionally Attach the debugger
  6. press the β€œHide map” button at the bottom of the screen
  7. Notice that onDispose is called but onDestroy was not called for the MapView since the activity is still alive
  8. Backing out of the app at this point destroys the activity but does not result in any calls to onDestroy for the MapView as far as I can tell
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            var isMapLoaded by remember { mutableStateOf(false) }

            var shouldShowMap by remember { mutableStateOf(true) }

            Box(Modifier.fillMaxSize()) {
                if (shouldShowMap) {
                    GoogleMapView(
                        modifier = Modifier.matchParentSize(),
                        onMapLoaded = {
                            isMapLoaded = true
                        }
                    )
                }
                if (!isMapLoaded) {
                    AnimatedVisibility(
                        modifier = Modifier
                            .matchParentSize(),
                        visible = !isMapLoaded,
                        enter = EnterTransition.None,
                        exit = fadeOut()
                    ) {
                        CircularProgressIndicator(
                            modifier = Modifier
                                .background(MaterialTheme.colors.background)
                                .wrapContentSize()
                        )
                    }
                }
                Button(
                    onClick = { shouldShowMap = false },
                    modifier = Modifier.align(Alignment.BottomCenter)
                        .padding(bottom = 16.dp)
                ) {
                    Text(text = "Hide map")
                }
            }
        }
    }

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
mcasper3commented, Feb 11, 2022

I have confirmed that is the case for the sample app. I am seeing a memory leak in my app but I will need to do more digging to see what the cause of that is. My current theory is that it has something to do without housing the map component inside an AbstractComposeView, but I’ll know more when I have a chance to dig into it more

0reactions
polivmi1commented, Apr 25, 2022

@arriolac I am getting a leak if the map component is cleared and then the activity is destroyed.

Previously when implementing it through AndroidView, to get rid of the ma[ps leak, I had to use:

DisposableEffect(lifecycle) {
        lifecycle.addObserver(lifecycleObserver)
        onDispose {
            lifecycle.removeObserver(lifecycleObserver)
            mapView.onDestroy()
            mapView.removeAllViews()
        }
    }

And

remember(mapView) {
        LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_CREATE -> mapView.onCreate(Bundle())
                Lifecycle.Event.ON_START -> mapView.onStart()
                Lifecycle.Event.ON_RESUME -> mapView.onResume()
                Lifecycle.Event.ON_PAUSE -> mapView.onPause()
                Lifecycle.Event.ON_STOP -> mapView.onStop()
                Lifecycle.Event.ON_DESTROY -> {
                    //handled in onDispose
                }
                else -> throw IllegalStateException()
            }
        }
    }

OnDestroy might not be called and this way you would always dispose of the view in onDispose, which will always be called. Would you be able to add this?

`1 APPLICATION LEAKS

References underlined with "~~~" are likely causes.
Learn more at https://squ.re/leaks.

56446 bytes retained by leaking objects
Signature: 34a030bd011ab1ccd655bbeac3fbc8465c53ce5f
┬───
β”‚ GC Root: Thread object
β”‚
β”œβ”€ com.google.maps.api.android.lib6.gmm6.vector.n instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 3.5 MB in 26933 objects
β”‚    Thread name: 'RenderDrive'
β”‚    ↓ n.e
β”‚        ~
β”œβ”€ com.google.maps.api.android.lib6.gmm6.vector.p instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 3.5 MB in 26930 objects
β”‚    ↓ p.f
β”‚        ~
β”œβ”€ com.google.maps.api.android.lib6.gmm6.api.ac instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 3.5 MB in 26929 objects
β”‚    View not part of a window view hierarchy
β”‚    View.mAttachInfo is null (view detached)
β”‚    View.mWindowAttachCount = 1
β”‚    mContext instance of ExampleApplication
β”‚    ↓ ac.N
β”‚         ~
β”œβ”€ com.google.maps.api.android.lib6.impl.ax instance
β”‚    Leaking: UNKNOWN
β”‚    Retaining 20 B in 1 objects
β”‚    a instance of ExampleApplication
β”‚    b instance of ExampleActivity with mDestroyed = true
β”‚    ↓ ax.b
β”‚         ~
β•°β†’ ExampleActivity instance`
Read more comments on GitHub >

github_iconTop Results From Across the Web

Memory leak #138 - googlemaps/android-maps-compose
I am getting a leak if the map component is cleared and then the activity is destroyed. ... The reason is that Lifecycle.Event.ON_DESTROY...
Read more >
Google Map View is leaking very much
Huge memory leaking. I've 'destroyed' the MapView like I should when exiting the activity and it still leaks. The MapView is in an...
Read more >
How to get rid of memory leaks? A practical approach using ...
LeakCanary hooks into the Android lifecycle and detects when activities and fragments are destroyed and should be garbage collected. Then these objects areΒ ......
Read more >
Bug: OutOfMemory after update to Play Services 6.1.09 ...
We are getting the same issue. Basically we have an activity with a Mapfragment. Even when the activity is destroyed, MapView seems to...
Read more >
Everything you need to know about Memory Leaks in ...
A memory leak can easily occur in Android when AsyncTasks, Handlers, Singletons, Threads, and other components are used incorrectly. I'll use threads,Β ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found