-3

I used Jetpack's navigation to manage Fragment, which uses databinding in Fragment.

Did not add other code. The memory leak is HomeFragment.databinding.root, which is a LinearLayout, and LinearLayout does not put anything. The LeakCanary message is shown below:

I used Jetpack's navigation to manage Fragment, which uses databinding in Fragment.

Did not add other code. The memory leak is HomeFragment.databinding.root, which is a LinearLayout, and LinearLayout does not put anything. The LeakCanary message is shown below:

ApplicationLeak(className=android.widget.LinearLayout, leakTrace=
┬
├─ android.app.ActivityThread
│    Leaking: NO (ActivityThread↓ is not leaking and a class is never leaking)
│    GC Root: System class
│    ↓ static ActivityThread.sCurrentActivityThread
├─ android.app.ActivityThread
│    Leaking: NO (ArrayMap↓ is not leaking)
│    ↓ ActivityThread.mActivities
├─ android.util.ArrayMap
│    Leaking: NO (Object[]↓ is not leaking)
│    ↓ ArrayMap.mArray
├─ java.lang.Object[]
│    Leaking: NO (ActivityThread$ActivityClientRecord↓ is not leaking)
│    ↓ array Object[].[1]
├─ android.app.ActivityThread$ActivityClientRecord
│    Leaking: NO (MainActivity↓ is not leaking)
│    ↓ ActivityThread$ActivityClientRecord.activity
├─ com.ukex.module.index.ui.MainActivity
│    Leaking: NO (FragmentController↓ is not leaking and Activity#mDestroyed is false)
│    ↓ MainActivity.mFragments
├─ androidx.fragment.app.FragmentController
│    Leaking: NO (FragmentActivity$HostCallbacks↓ is not leaking)
│    ↓ FragmentController.mHost
├─ androidx.fragment.app.FragmentActivity$HostCallbacks
│    Leaking: NO (FragmentManagerImpl↓ is not leaking)
│    ↓ FragmentActivity$HostCallbacks.mFragmentManager
├─ androidx.fragment.app.FragmentManagerImpl
│    Leaking: NO (NavHostFragment↓ is not leaking)
│    ↓ FragmentManagerImpl.mPrimaryNav
├─ androidx.navigation.fragment.NavHostFragment
│    Leaking: NO (FragmentManagerImpl↓ is not leaking and Fragment#mFragmentManager is not null)
│    ↓ NavHostFragment.mChildFragmentManager
├─ androidx.fragment.app.FragmentManagerImpl
│    Leaking: NO (HashMap↓ is not leaking)
│    ↓ FragmentManagerImpl.mActive
├─ java.util.HashMap
│    Leaking: NO (HashMap$Node[]↓ is not leaking)
│    ↓ HashMap.table
├─ java.util.HashMap$Node[]
│    Leaking: NO (HashMap$Node↓ is not leaking)
│    ↓ array HashMap$Node[].[0]
├─ java.util.HashMap$Node
│    Leaking: NO (HomeFragment↓ is not leaking)
│    ↓ HashMap$Node.value
├─ com.ukex.module.index.ui.HomeFragment
│    Leaking: NO (Fragment#mFragmentManager is not null)
│    ↓ HomeFragment.dataBinding
│                   ~~~~~~~~~~~
├─ com.ukex.databinding.HomeFragmentBindingImpl
│    Leaking: UNKNOWN
│    ↓ HomeFragmentBindingImpl.mRoot
│                              ~~~~~
╰→ android.widget.LinearLayout
​     Leaking: YES (ObjectWatcher was watching this)
​     mContext instance of com.ukex.module.index.ui.MainActivity with mDestroyed = false
​     View#mParent is null
​     View#mAttachInfo is null (view detached)
​     View.mWindowAttachCount = 1
​     key = 965e1901-f293-454b-b8c2-80b869d64f9a
​     watchDurationMillis = 21809
​     retainedDurationMillis = 16807
, retainedHeapByteSize=6255)
class HomeFragment : BaseVMFragment<HomeViewModel>() {
    private lateinit var dataBinding: HomeFragmentBinding


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {


            dataBinding =
            DataBindingUtil.inflate(inflater, R.layout.home_fragment, container, false)
        return dataBinding.root
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        dataBinding.apply {
            vm = mViewModel
            lifecycleOwner = this@HomeFragment
        }
        btnLogin.setOnClickListener {

            startActivity(Intent(context, LoginAct::class.java))
        }
    }


    override fun bindObserve() {
        super.bindObserve()
        mViewModel?.user?.observe(this, Observer {
            if (it != null)
                ToastUtils.showLong(it.username)
        })

    }

    override fun providerVMClass(): Class<HomeViewModel>? {
        return HomeViewModel::class.java
    }



}
代洪伦
  • 19
  • 3

1 Answers1

1

A view should be released from memory after Fragment.onDestroyView() is called, even if the fragment is not destroyed yet.

You need to override HomeFragment.onDestroyView() and set dataBinding (or the view it wraps) to null

Pierre-Yves Ricau
  • 8,209
  • 2
  • 27
  • 43