Skip to content
Advertisement

LeakCanary detecting a leak in my Java Android app – Google Maps

I am relatively new to the scene with little programming background, therefore I apologise in advance if I ask something silly. I am show a google map using SupportMapFragment. I am doing this to prevent a leak in onDestroyView(), however no clue whether this is right or wrong?

@Override
protected void onDestroy() {
    super.onDestroy();
    mMap.clear();
    mapView.onDestroy();
}

Full code is here:

https://github.com/warfo09/warforepo/blob/main/MainActivity.java

This is what I get when I exit my application:

  │ GC Root: Local variable in native code
  │
  ├─ com.google.maps.api.android.lib6.gmm6.vector.n instance
  │    Leaking: UNKNOWN
  │    Retaining 1391172 bytes in 9076 objects
  │    Thread name: 'RenderDrive'
  │    ↓ n.e
  │        ~
  ├─ com.google.maps.api.android.lib6.gmm6.vector.p instance
  │    Leaking: UNKNOWN
  │    Retaining 1391005 bytes in 9073 objects
  │    ↓ p.k
  │        ~
  ├─ com.google.maps.api.android.lib6.gmm6.api.ac instance
  │    Leaking: UNKNOWN
  │    Retaining 1390953 bytes in 9072 objects
  │    View not part of a window view hierarchy
  │    View.mAttachInfo is null (view detached)
  │    View.mWindowAttachCount = 1
  │    mContext instance of android.app.Application
  │    ↓ ac.mParent
  │         ~~~~~~~
  ├─ android.widget.FrameLayout instance
  │    Leaking: UNKNOWN
  │    Retaining 405651 bytes in 5497 objects
  │    View not part of a window view hierarchy
  │    View.mAttachInfo is null (view detached)
  │    View.mWindowAttachCount = 1
  │    mContext instance of android.app.Application
  │    ↓ FrameLayout.mParent
  │                  ~~~~~~~
  ├─ com.google.android.gms.maps.MapView instance
  │    Leaking: YES (View.mContext references a destroyed activity)
  │    Retaining 403455 bytes in 5482 objects
  │    View not part of a window view hierarchy
  │    View.mAttachInfo is null (view detached)
  │    View.mID = R.id.mapsView
  │    View.mWindowAttachCount = 1
  │    mContext instance of com.app.trikojis.MainActivity with mDestroyed = true
  │    ↓ MapView.mContext
  ╰→ com.app.trikojis.MainActivity instance
  ​     Leaking: YES (ObjectWatcher was watching this because com.app.trikojis.MainActivity received Activity#onDestroy()
  ​     callback and Activity#mDestroyed is true)
  ​     Retaining 60455 bytes in 956 objects
  ​     key = f3613896-cce8-4aac-a73a-d4a79700c719
  ​     watchDurationMillis = 56451
  ​     retainedDurationMillis = 51448
  ​     mApplication instance of android.app.Application
  ​     mBase instance of androidx.appcompat.view.ContextThemeWrapper, not wrapping known Android context
  
  METADATA
  
  Build.VERSION.SDK_INT: 28
  Build.MANUFACTURER: samsung
  LeakCanary version: 2.5
  App process name: com.app.trikojis
  Stats: LruCache[maxSize=3000,hits=5749,misses=110455,hitRate=4%]
  RandomAccess[bytes=5714581,reads=110455,travel=48839777691,range=35924253,size=41590935]
  Analysis duration: 11845 ms```



   

Advertisement

Answer

The leaktrace indicates that MainActivity was destroyed but is held in memory by MapView which has the activity as its context.

Now, what’s really weird here is that the MapView is the parent of a FrameLayout that has the Application as its context, which itself is the parent of a view called ‘ac’ (obfuscated name) which is held by a field of the “RenderDrive” thread instance.

This definitely is a bug in the Google Maps library. You should report the issue on their bug tracker, feel free to also use this answer that I just posted.

I don’t know what bug exactly is causing this leak, but you could fix it by removing all children of MapView when the activity is destroyed.

@Override
protected void onDestroy() {
    super.onDestroy();
    mMap.clear();
    mapView.onDestroy();
    mapView.removeAllViews();
}
Advertisement