android - 尽管在 onDestroyView 中将父 View 设置为 null,但 fragment View 内存泄漏

标签 android android-studio android-fragments memory-leaks leakcanary

我知道将 fragment 事务添加到 backstack然后从那个 fragment 移动到另一个 fragment ,reference of the previous fragment's view is still available并且只有在按下后退按钮时才会被破坏。
为了避免这种情况,I have set the view to null in onDestroyView但问题是,leakcanary still shows view is not null and the view reference is still available而记录 View 说它是空的。

为什么会这样?
另外,如果我错了或遗漏了什么,请纠正我。

fragment 类-


private var mView: View? = null
private lateinit var btnSignUp: Button

 override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        mView = inflater.inflate(R.layout.fragment_login, container, false)
        return mView
    }

 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        btnSignUp = view.findViewById(R.id.btnSignUp)

        btnSignUp.setOnClickListener {
            // calling function changeFragment()
            changeFragment(SignUpFragment(), FragmentsTag.SIGNUP_FRAGMENT)
        }
    }

override fun onDestroyView() {
        super.onDestroyView()      
         mView=null
    }


LeakCanary 分析日志 --
  HEAP ANALYSIS RESULT
    ====================================
    1 APPLICATION LEAKS

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

    43817 bytes retained by leaking objects
    Signature: 6e77557c8a679dd41391c1c5badaac98217366ad
    ┬───
    │ GC Root: System class
    │
    ├─ leakcanary.internal.InternalLeakCanary class
    │    Leaking: NO (MainActivity↓ is not leaking and a class is never leaking)
    │    ↓ static InternalLeakCanary.resumedActivity
    ├─ com.example.foodrunner.activities.MainActivity instance
    │    Leaking: NO (LoginFragment↓ is not leaking and Activity#mDestroyed is false)
    │    ↓ MainActivity.mFragments
    ├─ androidx.fragment.app.FragmentController instance
    │    Leaking: NO (LoginFragment↓ is not leaking)
    │    ↓ FragmentController.mHost
    ├─ androidx.fragment.app.FragmentActivity$HostCallbacks instance
    │    Leaking: NO (LoginFragment↓ is not leaking)
    │    ↓ FragmentActivity$HostCallbacks.mFragmentManager
    ├─ androidx.fragment.app.FragmentManagerImpl instance
    │    Leaking: NO (LoginFragment↓ is not leaking)
    │    ↓ FragmentManagerImpl.mActive
    ├─ java.util.HashMap instance
    │    Leaking: NO (LoginFragment↓ is not leaking)
    │    ↓ HashMap.table
    ├─ java.util.HashMap$HashMapEntry[] array
    │    Leaking: NO (LoginFragment↓ is not leaking)
    │    ↓ HashMap$HashMapEntry[].[0]
    ├─ java.util.HashMap$HashMapEntry instance
    │    Leaking: NO (LoginFragment↓ is not leaking)
    │    ↓ HashMap$HashMapEntry.value
    ├─ com.example.foodrunner.fragments.LoginFragment instance
    │    Leaking: NO (Fragment#mFragmentManager is not null)
    │    Fragment.mTag=Login Fragment
    │    ↓ LoginFragment.btnLogin
    │                    ~~~~~~~~
    ├─ com.google.android.material.button.MaterialButton instance
    │    Leaking: YES (View detached and has parent)
    │    mContext instance of com.example.foodrunner.activities.MainActivity with mDestroyed = false
    │    View#mParent is set
    │    View#mAttachInfo is null (view detached)
    │    View.mID = R.id.btnLogin
    │    View.mWindowAttachCount = 1
    │    ↓ MaterialButton.mParent
    ╰→ androidx.constraintlayout.widget.ConstraintLayout instance
    ​     Leaking: YES (ObjectWatcher was watching this because com.example.foodrunner.fragments.LoginFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks))
    ​     key = b72a82a6-b9dd-46c6-afb2-0ea6c7025001
    ​     watchDurationMillis = 9582
    ​     retainedDurationMillis = 4582
    ​     key = 0554b63a-c700-4c86-a451-b0daae06607a
    ​     watchDurationMillis = 9581
    ​     retainedDurationMillis = 4580
    ​     mContext instance of com.example.foodrunner.activities.MainActivity with mDestroyed = false
    ​     View#mParent is null
    ​     View#mAttachInfo is null (view detached)
    ​     View.mWindowAttachCount = 1
    ====================================

最佳答案

您仍然保留对 btnSignUp 的引用在 onDestroyView 之后- 是什么泄漏。你必须放弃全部 引用 全部 刚刚被破坏的 View 中的 View 。

因此,您应该使用相同的方法(使其可以为空 var )或不保留对 btnSignUp 的引用完全在您的 Fragment 中-至少在您的代码示例中,它很容易成为局部变量。 (事实上​​,这同样适用于你的 mView - 你得到 View 作为 onViewCreated() 的输入,没有理由在 fragment 级别保留它)。

关于android - 尽管在 onDestroyView 中将父 View 设置为 null,但 fragment View 内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60672013/

相关文章:

android - 三星s4 zoom不支持平滑缩放isSmoothZoomSupported()==false

Android - PX 到 DP - 布局高度错误

android - 向操作栏添加操作

java - 使用较新版本的 Android Studio 构建应用程序一年后广告停止运行

android - 在android studio中有没有办法在不删除模块的情况下禁用它?

java - Fragments 中的 commit() 和 commitAllowingStateLoss() 有什么区别

android - 相对布局中多个图像的缩放控件

java - Android Studio - 编辑器中的 FileNotFound 异常

android - 删除 fragment 后内存未释放

android - 显示 fragment 调用 onResume?