android - 尽管在 onDestroyView 中将父视图设置为 null,但片段视图内存泄漏
问题描述
我知道将片段事务添加到backstack
然后从该片段移动到另一个片段,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
虽然记录视图说它是空的。
为什么会这样?另外,如果我错了或遗漏了什么,请纠正我。
片段类-
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
====================================
解决方案
You're still holding onto a reference to btnSignUp
after onDestroyView
- that is what is leaking. You have to drop all reference to all Views within the view that was just destroyed.
Therefore you should either use the same approach (make it a nullable var
) or not hold onto a reference to btnSignUp
in your Fragment at all - at least in your code sample, it could easily be a local variable. (In fact, the same applies to your mView
- you get the View
as an input to onViewCreated()
, there's no reason to hold onto it at the Fragment level).
推荐阅读
- sql - SQL:如何查找出现的字符串,按键随机排序并将它们分配给新属性?
- reactjs - 在主要区域周围设置灰色背景
- java - 如何在不重新启动 JVM 的情况下将外部 JAR 添加到 Spring 应用程序?
- java - Spring Security 默认登录表单不会被自定义登录页面替换
- flutter - 滑块小部件在 iOS 和 Web 上不起作用
- flutter - Flutter 过滤 json 数据为唯一数据
- flutter - Flutter + Firebase:“QuerySnapshot”类没有实例获取器“文档”
- regex - 正则表达式禁止字符串中任何具有捕获组的字符
- selenium - 无法从“TO”下拉列表中选择值
- android - 关于通过 FCM 向其他应用发送通知