setHasOptionsMenu во Фрагменте вызывает УТЕЧКУ ПАМЯТИ - PullRequest
0 голосов
/ 19 марта 2020

Я не знаю, почему, но вызов 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
    ====================================

...