Best practice to access a Activity View from multiples Fragments and avoid memory leaks

I have a FAB in the main activity and more than 20 fragments access it, changing the icon and the onClickListener.

So i created a extension function for Fragments, the problem i found is that it's leaking memory using this method.

fun Fragment.setupFab(showFab: Boolean = false,
                 @DrawableRes fabIcon: Int = R.drawable.ic_baseline_add_24,
                 fabListener: View.OnClickListener? = null
){
    (requireActivity() as MainActivity)
    .findViewById<FloatingActionButton>(R.id.fab)
    .apply {
        if (showFab) {
            show()
            setImageDrawable(ContextCompat.getDrawable(context, fabIcon))
            setOnClickListener(fabListener) 
        } else hide() 
    }
}

So i created an interface that MainActivity extends:

interface FabListener{
    fun setupFab(showFab: Boolean = false,
                @DrawableRes fabIcon: Int = R.drawable.ic_baseline_add_24,
                fabListener: View.OnClickListener? = null)
}

And then from my fragments i use:

(requireActivity() as FabListener).setupFab(showFab = false){
    //do stuff
}

But it keeps leaking memory.

How can i solve this or what is the best way to do this?

Would it be best if i create a BottomAppBar and FAB inside each fragment? But this seems a waste to inflate the same think over and over again for each fragment.

Leak canary log:

├─ com.puntogris.blint.ui.main.MainActivity instance
│    Leaking: NO (FloatingActionButton↓ is not leaking and 
Activity#mDestroyed
│    is false)
│    mApplication instance of com.puntogris.blint.di.App
│    mBase instance of         
     androidx.appcompat.view.ContextThemeWrapper
│    ↓ BaseActivity._binding
├─ com.puntogris.blint.databinding.ActivityMainBindingImpl     
   instance
│    Leaking: NO (FloatingActionButton↓ is not leaking)
│    ↓ ActivityMainBinding.mainFab
├─ 

  com.google.android.material.floatingactionbutton
 .FloatingActionButton
│  instance
│    Leaking: NO (View attached)
│    View is part of a window view hierarchy
│    View.mAttachInfo is not null (view attached)
│    View.mID = R.id.mainFab
│    View.mWindowAttachCount = 1
│    mContext instance of 
     com.puntogris.blint.ui.main.MainActivity with
│    mDestroyed = false
│    ↓ View.mListenerInfo
│           ~~~~~~~~~~~~~
├─ android.view.View$ListenerInfo instance
│    Leaking: UNKNOWN
│    Retaining 8,7 kB in 318 objects
│    ↓ View$ListenerInfo.mOnClickListener
│                        ~~~~~~~~~~~~~~~~
├─ com.puntogris.blint.ui.product.manage.
│  -$$Lambda$ManageProductsFragment$2GI1C37xvlkTJomTwGgPILZJDyM     
    instance
│    Leaking: UNKNOWN
│    Retaining 8,6 kB in 315 objects
│    ↓     

 -$$Lambda$ManageProductsFragment
 $2GI1C37xvlkTJomTwGgPILZJDyM.f$0
│                                                                       
~~~
╰→ com.puntogris.blint.ui.product
   .manage.ManageProductsFragment instance
Leaking: YES (ObjectWatcher was watching this because com.puntogris.blint.ui.product.manage.ManageProductsFragment received 
Fragment#onDestroy()
callback and Fragment#mFragmentManager is null)
Retaining 8,6 kB in 314 objects
key = c3306747-0b3c-4334-84de-65c4d6eea601
watchDurationMillis = 9493
retainedDurationMillis = 4493
componentContext instance of
dagger.hilt.android.internal.managers.
ViewComponentManager$FragmentContextWrapper, wrapping activity 
com.
puntogris.blint.ui.main.MainActivity with mDestroyed = false


Read more here: https://stackoverflow.com/questions/68473195/best-practice-to-access-a-activity-view-from-multiples-fragments-and-avoid-memor

Content Attribution

This content was originally published by Joaco at Recent Questions - Stack Overflow, and is syndicated here via their RSS feed. You can read the original post over there.

%d bloggers like this: