即使在设置为 null 后片段绑定仍在泄漏
Fragment binding is leaking even after setting null
根据 android docs 我已经实现了简单的片段,但是当我移动到下一个片段时,它开始在 _binding 中泄漏,即使我已经设置了绑定 null。
这是我的泄漏堆栈跟踪
37740 bytes retained by leaking objects
Signature: 7162f0f053a181182714235f17d7e8c36154eb81
┬───
│ GC Root: Global variable in native code
│
├─ android.database.ContentObserver$Transport instance
│ Leaking: NO (LoginFragment↓ is not leaking)
│ ↓ ContentObserver$Transport.mContentObserver
├─ android.widget.Editor$HandleViewShowObserver instance
│ Leaking: NO (LoginFragment↓ is not leaking)
│ ↓ Editor$HandleViewShowObserver.mContext
├─ dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapper instance
│ Leaking: NO (LoginFragment↓ is not leaking)
│ ViewComponentManager$FragmentContextWrapper wraps an Activity with Activity.mDestroyed false
│ ↓ ViewComponentManager$FragmentContextWrapper.fragment
├─ com.ics.homework.ui.auth.LoginFragment instance
│ Leaking: NO (WelcomeFragment↓ is not leaking and Fragment#mFragmentManager is not null)
│ ↓ LoginFragment.mFragmentManager
├─ androidx.fragment.app.FragmentManagerImpl instance
│ Leaking: NO (WelcomeFragment↓ is not leaking)
│ ↓ FragmentManagerImpl.mFragmentStore
├─ androidx.fragment.app.FragmentStore instance
│ Leaking: NO (WelcomeFragment↓ is not leaking)
│ ↓ FragmentStore.mActive
├─ java.util.HashMap instance
│ Leaking: NO (WelcomeFragment↓ is not leaking)
│ ↓ HashMap.table
├─ java.util.HashMap$Node[] array
│ Leaking: NO (WelcomeFragment↓ is not leaking)
│ ↓ HashMap$Node[].[1]
├─ java.util.HashMap$Node instance
│ Leaking: NO (WelcomeFragment↓ is not leaking)
│ ↓ HashMap$Node.value
├─ androidx.fragment.app.FragmentStateManager instance
│ Leaking: NO (WelcomeFragment↓ is not leaking)
│ ↓ FragmentStateManager.mFragment
├─ com.ics.homework.ui.auth.WelcomeFragment instance
│ Leaking: NO (Fragment#mFragmentManager is not null)
│ ↓ WelcomeFragment._binding
│ ~~~~~~~~
├─ com.ics.homework.databinding.FragmentWelcomeBindingImpl instance
│ Leaking: UNKNOWN
│ ↓ FragmentWelcomeBindingImpl.mRoot
│ ~~~~~
╰→ androidx.constraintlayout.widget.ConstraintLayout instance
Leaking: YES (ObjectWatcher was watching this because com.ics.homework.ui.auth.WelcomeFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks))
key = a1bab73c-de04-4e36-bda1-b29a54da8348
watchDurationMillis = 22798
retainedDurationMillis = 17792
mContext instance of com.ics.homework.ui.auth.AuthActivity with mDestroyed = false
View#mParent is null
View#mAttachInfo is null (view detached)
View.mWindowAttachCount = 1
====================================
0 LIBRARY LEAKS
这是我的实现
class WelcomeFragment : Fragment() {
private var _binding: FragmentWelcomeBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentWelcomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.handler = onClickListener
}
private val onClickListener = View.OnClickListener { view ->
with(binding) {
when (view.id) {
btnLoginSignup.id -> {
val action = WelcomeFragmentDirections.actionWelcomeFragmentToLoginFragment()
findNavController().navigate(action)
}
tvHelpline.id,
tvHelplineNumber.id -> {
val intent = Intent(Intent.ACTION_DIAL)
intent.data = Uri.parse("tel:0123456789")
startActivity(intent)
}
}
}
}
override fun onDestroy() {
super.onDestroy()
binding.handler = null
_binding = null
}
}
在来自文档的示例中,绑定在 OnDestroyView() 方法中设置为 null,而不是在 OnDestroy() 中。据我所知,当您将 Fragment A 替换为 Fragment B 并将 Fragment A 添加到 backstack 时,不会调用 Fragment A 的 OnDestroy() 方法。这就是为什么您的绑定不为空。
根据 android docs 我已经实现了简单的片段,但是当我移动到下一个片段时,它开始在 _binding 中泄漏,即使我已经设置了绑定 null。
这是我的泄漏堆栈跟踪
37740 bytes retained by leaking objects
Signature: 7162f0f053a181182714235f17d7e8c36154eb81
┬───
│ GC Root: Global variable in native code
│
├─ android.database.ContentObserver$Transport instance
│ Leaking: NO (LoginFragment↓ is not leaking)
│ ↓ ContentObserver$Transport.mContentObserver
├─ android.widget.Editor$HandleViewShowObserver instance
│ Leaking: NO (LoginFragment↓ is not leaking)
│ ↓ Editor$HandleViewShowObserver.mContext
├─ dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapper instance
│ Leaking: NO (LoginFragment↓ is not leaking)
│ ViewComponentManager$FragmentContextWrapper wraps an Activity with Activity.mDestroyed false
│ ↓ ViewComponentManager$FragmentContextWrapper.fragment
├─ com.ics.homework.ui.auth.LoginFragment instance
│ Leaking: NO (WelcomeFragment↓ is not leaking and Fragment#mFragmentManager is not null)
│ ↓ LoginFragment.mFragmentManager
├─ androidx.fragment.app.FragmentManagerImpl instance
│ Leaking: NO (WelcomeFragment↓ is not leaking)
│ ↓ FragmentManagerImpl.mFragmentStore
├─ androidx.fragment.app.FragmentStore instance
│ Leaking: NO (WelcomeFragment↓ is not leaking)
│ ↓ FragmentStore.mActive
├─ java.util.HashMap instance
│ Leaking: NO (WelcomeFragment↓ is not leaking)
│ ↓ HashMap.table
├─ java.util.HashMap$Node[] array
│ Leaking: NO (WelcomeFragment↓ is not leaking)
│ ↓ HashMap$Node[].[1]
├─ java.util.HashMap$Node instance
│ Leaking: NO (WelcomeFragment↓ is not leaking)
│ ↓ HashMap$Node.value
├─ androidx.fragment.app.FragmentStateManager instance
│ Leaking: NO (WelcomeFragment↓ is not leaking)
│ ↓ FragmentStateManager.mFragment
├─ com.ics.homework.ui.auth.WelcomeFragment instance
│ Leaking: NO (Fragment#mFragmentManager is not null)
│ ↓ WelcomeFragment._binding
│ ~~~~~~~~
├─ com.ics.homework.databinding.FragmentWelcomeBindingImpl instance
│ Leaking: UNKNOWN
│ ↓ FragmentWelcomeBindingImpl.mRoot
│ ~~~~~
╰→ androidx.constraintlayout.widget.ConstraintLayout instance
Leaking: YES (ObjectWatcher was watching this because com.ics.homework.ui.auth.WelcomeFragment received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks))
key = a1bab73c-de04-4e36-bda1-b29a54da8348
watchDurationMillis = 22798
retainedDurationMillis = 17792
mContext instance of com.ics.homework.ui.auth.AuthActivity with mDestroyed = false
View#mParent is null
View#mAttachInfo is null (view detached)
View.mWindowAttachCount = 1
====================================
0 LIBRARY LEAKS
这是我的实现
class WelcomeFragment : Fragment() {
private var _binding: FragmentWelcomeBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentWelcomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.handler = onClickListener
}
private val onClickListener = View.OnClickListener { view ->
with(binding) {
when (view.id) {
btnLoginSignup.id -> {
val action = WelcomeFragmentDirections.actionWelcomeFragmentToLoginFragment()
findNavController().navigate(action)
}
tvHelpline.id,
tvHelplineNumber.id -> {
val intent = Intent(Intent.ACTION_DIAL)
intent.data = Uri.parse("tel:0123456789")
startActivity(intent)
}
}
}
}
override fun onDestroy() {
super.onDestroy()
binding.handler = null
_binding = null
}
}
在来自文档的示例中,绑定在 OnDestroyView() 方法中设置为 null,而不是在 OnDestroy() 中。据我所知,当您将 Fragment A 替换为 Fragment B 并将 Fragment A 添加到 backstack 时,不会调用 Fragment A 的 OnDestroy() 方法。这就是为什么您的绑定不为空。