I was working on some issues related to memory leaks. When I was removing a fragment using a fragment transaction(fragmentManager.beginTransaction().remove(fragment).commit()
), I noticed memory leaks. But when I use a pop back stack (fragmentManager.popBackStack()
) I didn't saw any memory leak. This scenario made me curious to find which way is better to remove a fragment. Using Fragment Transaction or using a pop back stack.
EDIT 1 I am adding a fragment dynamically.
fun AppCompatActivity.addFragment(fragment: Fragment, frameId: Int, tag: String) {
supportFragmentManager.inTransaction {
setPrimaryNavigationFragment(fragment)
addToBackStack(tag)
add(frameId, fragment, tag)
}
}
EDIT 2 Memory leak output
┬───
│ GC Root: Local variable in native code
│
├─ com.esri.arcgisruntime.mapping.view.GeoView$RenderingThread instance
│ Leaking: NO (MapView↓ is not leaking)
│ Thread name: 'Rendering thread'
│ ↓ GeoView$RenderingThread.mGeoView
├─ com.esri.arcgisruntime.mapping.view.MapView instance
│ Leaking: NO (MainActivity↓ is not leaking and View attached)
│ mContext instance of main.MainActivity with mDestroyed = false
│ View.parent android.widget.LinearLayout attached as well
│ View#mParent is set
│ View#mAttachInfo is not null (view attached)
│ View.mID = R.id.mapView
│ View.mWindowAttachCount = 1
│ ↓ MapView.mContext
├─ MainActivity instance
│ Leaking: NO (SearchContainerFragment↓ is not leaking and Activity#mDestroyed is false)
│ ↓ MainActivity.navigationHandler
├─ BottomNavigationHandler instance
│ Leaking: NO (SearchContainerFragment↓ is not leaking)
│ ↓ BottomNavigationHandler.fragmentManager
├─ androidx.fragment.app.FragmentManagerImpl instance
│ Leaking: NO (SearchContainerFragment↓ is not leaking)
│ ↓ FragmentManagerImpl.mFragmentStore
├─ androidx.fragment.app.FragmentStore instance
│ Leaking: NO (SearchContainerFragment↓ is not leaking)
│ ↓ FragmentStore.mActive
├─ java.util.HashMap instance
│ Leaking: NO (SearchContainerFragment↓ is not leaking)
│ ↓ HashMap.table
├─ java.util.HashMap$Node[] array
│ Leaking: NO (SearchContainerFragment↓ is not leaking)
│ ↓ HashMap$Node[].[0]
├─ java.util.HashMap$Node instance
│ Leaking: NO (SearchContainerFragment↓ is not leaking)
│ ↓ HashMap$Node.value
├─ androidx.fragment.app.FragmentStateManager instance
│ Leaking: NO (SearchContainerFragment↓ is not leaking)
│ ↓ FragmentStateManager.mFragment
├─ SearchContainerFragment instance
│ Leaking: NO (Fragment#mFragmentManager is not null)
│ Fragment.mTag=SearchContainerFragment
│ ↓ SearchContainerFragment.adapter
│ ~~~~~~~
├─ SearchContainerAdapter instance
│ Leaking: UNKNOWN
│ ↓ SearchContainerAdapter.mViewPagerObserver
│ ~~~~~~~~~~~~~~~~~~
├─ androidx.viewpager.widget.ViewPager$PagerObserver instance
│ Leaking: UNKNOWN
│ ↓ ViewPager$PagerObserver.this$0
│ ~~~~~~
├─ NonSwipeableViewPager instance
│ Leaking: YES (View detached and has parent)
│ mContext instance of MainActivity with mDestroyed = false
│ View#mParent is set
│ View#mAttachInfo is null (view detached)
│ View.mID = R.id.viewPager
│ View.mWindowAttachCount = 1
│ ↓ NonSwipeableViewPager.mParent
╰→ android.widget.RelativeLayout instance
Leaking: YES (ObjectWatcher was watching this because SearchContainerFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks))
key = 6941f63f-0f30-45b3-b62e-958477398b5b
watchDurationMillis = 13855
retainedDurationMillis = 8854
key = 3b8b4ecb-d3fe-4db0-b46b-2e4ab6e64bb8
watchDurationMillis = 13856
retainedDurationMillis = 8856
mContext instance of MainActivity with mDestroyed = false
View#mParent is null
View#mAttachInfo is null (view detached)
View.mWindowAttachCount = 1
METADATA
Build.VERSION.SDK_INT: 29
Build.MANUFACTURER: OnePlus
LeakCanary version: 2.2
App process name: com.dev
Analysis duration: 18920 ms```