Я не знаю, почему, но вызов setHasMenuOption (true) in onCreate во фрагменте вызывает утечку памяти.
Я пытался вызвать setHasMenuOption (false) в onDestroyView но я не работал.
Не вызывается setHasMenuOption () не вызывает утечки памяти ..
Как избежать этой утечки ??
Я использую Фрагмент в качестве контейнера для размещения других фрагментов.
Класс Host Fragment -
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_nested_fragments_container, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val actionBarDrawerToggle = ActionBarDrawerToggle(
activity,
drawerLayout,
toolbar,
R.string.open_drawer,
R.string.close_drawer
)
drawerLayout.addDrawerListener(actionBarDrawerToggle)
actionBarDrawerToggle.syncState()
setToolbarTitle("Restaurants")
setNavigationItemClicks()
//calling fragment HOME_FRAGMENT within container fragment
childFragmentManager.beginTransaction()
.replace(R.id.nestedFragmentsContainer, HomeFragment(), FragmentsTag.HOME_FRAGMENT)
.commit()
// setting container fragment as primary navigation fragment
fragmentManager?.beginTransaction()?.setPrimaryNavigationFragment(this)?.commit()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == android.R.id.home) {
drawerLayout.openDrawer(GravityCompat.START)
true
}else{
false
}
}
private fun setNavigationItemClicks() {
navigationView.setNavigationItemSelectedListener {
when (it.itemId) {
R.id.home -> {
changeFragment(HomeFragment(), FragmentsTag.HOME_FRAGMENT)
setToolbarTitle("Restaurants")
}
R.id.profile -> {
changeFragment(
ProfileFragment(),
FragmentsTag.PROFILE_FRAGMENT
)
setToolbarTitle("Profile")
}
R.id.favorites -> {
changeFragment(
FavoritesFragment(),
FragmentsTag.FAVORITES_FRAGMENT
)
setToolbarTitle("Favorites")
}
R.id.faq -> {
changeFragment(FaqFragment(), FragmentsTag.FAQ_FRAGMENT)
setToolbarTitle("FAQs")
}
R.id.logout -> showLogoutConfirmationDialog()
}
return@setNavigationItemSelectedListener true
}
}
private fun setToolbarTitle(title: String) {
val activity = (activity as AppCompatActivity)
activity.setSupportActionBar(toolbar)
activity.supportActionBar?.title = title
}
private fun changeFragment(fragment: Fragment, tag: String) {
drawerLayout.closeDrawer(GravityCompat.START)
childFragmentManager.beginTransaction()
.replace(R.id.nestedFragmentsContainer, fragment, tag)
.commit()
}
private fun showLogoutConfirmationDialog() {
drawerLayout.closeDrawer(GravityCompat.START)
MaterialAlertDialogBuilder(activity as Context, R.style.AlertDialogTheme).apply {
setTitle("Logout")
setMessage("Are you sure?")
setPositiveButton("Ok") { _, _ ->
logout()
}
setNegativeButton("Cancel") { dialog: DialogInterface?, _: Int -> dialog?.dismiss() }
}.show()
}
private fun logout() {
clearSharedPref()
fragmentManager?.apply {
popBackStack(
findFragmentById(R.id.fragmentContainer)?.tag,
FragmentManager.POP_BACK_STACK_INCLUSIVE
)
beginTransaction().apply {
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE)
replace(R.id.fragmentContainer, LoginFragment(), FragmentsTag.LOGIN_FRAGMENT)
addToBackStack(FragmentsTag.LOGIN_FRAGMENT)
}.commit()
}
}
private fun clearSharedPref() {
activity?.getSharedPreferences(BuildConfig.APPLICATION_ID, Context.MODE_PRIVATE)?.apply {
edit().clear().apply()
}
}
override fun onDestroyView() {
super.onDestroyView()
clearFindViewByIdCache()
setHasOptionsMenu(false)
fragmentManager?.apply {
beginTransaction().setPrimaryNavigationFragment(null).commit()
}
(activity as AppCompatActivity).setSupportActionBar(null)
}
Трассировка утечки LeakCanary -
┬───
│ 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 (Activity#mDestroyed is false)
│ ↓ MainActivity.mFragments
│ ~~~~~~~~~~
├─ androidx.fragment.app.FragmentController instance
│ Leaking: UNKNOWN
│ ↓ FragmentController.mHost
│ ~~~~~
├─ androidx.fragment.app.FragmentActivity$HostCallbacks instance
│ Leaking: UNKNOWN
│ ↓ FragmentActivity$HostCallbacks.mFragmentManager
│ ~~~~~~~~~~~~~~~~
├─ androidx.fragment.app.FragmentManagerImpl instance
│ Leaking: UNKNOWN
│ ↓ FragmentManagerImpl.mCreatedMenus
│ ~~~~~~~~~~~~~
├─ java.util.ArrayList instance
│ Leaking: UNKNOWN
│ ↓ ArrayList.elementData
│ ~~~~~~~~~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ ↓ Object[].[0]
│ ~~~
╰→ com.example.foodrunner.fragments.NestedFragmentsContainerFragment instance
Leaking: YES (ObjectWatcher was watching this because com.example.foodrunner.fragments.NestedFragmentsContainerFragment received Fragment#onDestroy() callback and Fragment#mFragmentManager is null)
key = 22ecd71d-dbcd-470f-9ca3-0c992c1aada3
watchDurationMillis = 11383
retainedDurationMillis = 6382
key = adac2a58-8e2a-45f4-850b-f87ed815c9ce
watchDurationMillis = 11384
retainedDurationMillis = 6383
====================================