Skip to content

Commit e11c919

Browse files
fix: MainFragment pager memory leaks
1 parent b6405a0 commit e11c919

File tree

3 files changed

+52
-30
lines changed

3 files changed

+52
-30
lines changed

app/src/main/java/org/openedx/app/MainFragment.kt

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import org.openedx.core.presentation.global.viewBinding
2929
import org.openedx.core.system.notifier.app.AppUpgradeEvent
3030
import org.openedx.discovery.presentation.DiscoveryRouter
3131
import org.openedx.downloads.presentation.download.DownloadsFragment
32+
import org.openedx.foundation.system.PreviewFragmentManager.clearFragmentResultListener
3233
import org.openedx.learn.presentation.LearnFragment
3334
import org.openedx.learn.presentation.LearnTab
3435
import org.openedx.profile.presentation.profile.ProfileFragment
@@ -39,8 +40,6 @@ class MainFragment : Fragment(R.layout.fragment_main) {
3940
private val viewModel by viewModel<MainViewModel>()
4041
private val router by inject<DiscoveryRouter>()
4142

42-
private lateinit var adapter: NavigationFragmentAdapter
43-
4443
override fun onCreate(savedInstanceState: Bundle?) {
4544
super.onCreate(savedInstanceState)
4645
lifecycle.addObserver(viewModel)
@@ -50,6 +49,19 @@ class MainFragment : Fragment(R.layout.fragment_main) {
5049
}
5150
}
5251

52+
override fun onDestroyView() {
53+
try {
54+
binding.viewPager.adapter = null
55+
} catch (_: Exception) {
56+
}
57+
super.onDestroyView()
58+
}
59+
60+
override fun onDestroy() {
61+
clearFragmentResultListener(UpgradeRequiredFragment.REQUEST_KEY)
62+
super.onDestroy()
63+
}
64+
5365
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
5466
super.onViewCreated(view, savedInstanceState)
5567
handleArguments()
@@ -89,26 +101,28 @@ class MainFragment : Fragment(R.layout.fragment_main) {
89101
requireArguments().remove(ARG_OPEN_TAB)
90102
}
91103

92-
private fun createTabList(openTabArg: String): List<Pair<Int, Fragment>> {
93-
val learnFragment = LearnFragment.newInstance(
94-
openTab = if (openTabArg == HomeTab.PROGRAMS.name) {
95-
LearnTab.PROGRAMS.name
96-
} else {
97-
LearnTab.COURSES.name
98-
}
99-
)
104+
private fun createTabList(openTabArg: String): List<Pair<Int, () -> Fragment>> {
105+
val learnFragmentFactory = {
106+
LearnFragment.newInstance(
107+
openTab = if (openTabArg == HomeTab.PROGRAMS.name) {
108+
LearnTab.PROGRAMS.name
109+
} else {
110+
LearnTab.COURSES.name
111+
}
112+
)
113+
}
100114

101-
return mutableListOf<Pair<Int, Fragment>>().apply {
102-
add(R.id.fragmentLearn to learnFragment)
103-
add(R.id.fragmentDiscover to viewModel.getDiscoveryFragment)
115+
return mutableListOf<Pair<Int, () -> Fragment>>().apply {
116+
add(R.id.fragmentLearn to learnFragmentFactory)
117+
add(R.id.fragmentDiscover to { viewModel.getDiscoveryFragment })
104118
if (viewModel.isDownloadsFragmentEnabled) {
105-
add(R.id.fragmentDownloads to DownloadsFragment())
119+
add(R.id.fragmentDownloads to { DownloadsFragment() })
106120
}
107-
add(R.id.fragmentProfile to ProfileFragment())
121+
add(R.id.fragmentProfile to { ProfileFragment() })
108122
}
109123
}
110124

111-
private fun addMenuItems(menu: Menu, tabList: List<Pair<Int, Fragment>>) {
125+
private fun addMenuItems(menu: Menu, tabList: List<Pair<Int, () -> Fragment>>) {
112126
val tabTitles = mapOf(
113127
R.id.fragmentLearn to resources.getString(R.string.app_navigation_learn),
114128
R.id.fragmentDiscover to resources.getString(R.string.app_navigation_discovery),
@@ -128,7 +142,7 @@ class MainFragment : Fragment(R.layout.fragment_main) {
128142
}
129143
}
130144

131-
private fun setupBottomNavListener(tabList: List<Pair<Int, Fragment>>) {
145+
private fun setupBottomNavListener(tabList: List<Pair<Int, () -> Fragment>>) {
132146
val menuIdToIndex = tabList.mapIndexed { index, pair -> pair.first to index }.toMap()
133147

134148
binding.bottomNavView.setOnItemSelectedListener { menuItem ->
@@ -173,21 +187,21 @@ class MainFragment : Fragment(R.layout.fragment_main) {
173187
} else {
174188
R.id.fragmentLearn
175189
}
190+
176191
HomeTab.PROFILE.name -> R.id.fragmentProfile
177192
else -> R.id.fragmentLearn
178193
}
179194
}
180195

181-
private fun initViewPager(tabList: List<Pair<Int, Fragment>>) {
196+
private fun initViewPager(tabList: List<Pair<Int, () -> Fragment>>) {
182197
binding.viewPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
183198
binding.viewPager.offscreenPageLimit = tabList.size
184-
185-
adapter = NavigationFragmentAdapter(this).apply {
186-
tabList.forEach { (_, fragment) ->
187-
addFragment(fragment)
199+
binding.viewPager.adapter = NavigationFragmentAdapter(this).apply {
200+
tabList.forEach { (_, fragmentFactory) ->
201+
// Use fragment factory to prevent memory leaks
202+
addFragment { fragmentFactory() }
188203
}
189204
}
190-
binding.viewPager.adapter = adapter
191205
binding.viewPager.isUserInputEnabled = false
192206
}
193207

core/src/main/java/org/openedx/core/adapter/NavigationFragmentAdapter.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ import androidx.viewpager2.adapter.FragmentStateAdapter
55

66
class NavigationFragmentAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
77

8-
private val fragments = ArrayList<Fragment>()
8+
private val fragmentFactories = ArrayList<() -> Fragment>()
99

10-
override fun getItemCount(): Int = fragments.size
10+
override fun getItemCount(): Int = fragmentFactories.size
1111

12-
override fun createFragment(position: Int): Fragment = fragments[position]
12+
override fun createFragment(position: Int): Fragment = fragmentFactories[position].invoke()
1313

14-
fun addFragment(fragment: Fragment) {
15-
fragments.add(fragment)
14+
fun addFragment(fragmentFactory: () -> Fragment) {
15+
fragmentFactories.add(fragmentFactory)
1616
}
1717
}

dashboard/src/main/java/org/openedx/learn/presentation/LearnFragment.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,21 @@ class LearnFragment : Fragment(R.layout.fragment_learn) {
8686
}
8787
}
8888

89+
override fun onDestroyView() {
90+
try {
91+
binding.viewPager.adapter = null
92+
} catch (_: Exception) {
93+
}
94+
super.onDestroyView()
95+
}
96+
8997
private fun initViewPager() {
9098
binding.viewPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
9199
binding.viewPager.offscreenPageLimit = 2
92100

93101
adapter = NavigationFragmentAdapter(this).apply {
94-
addFragment(viewModel.getDashboardFragment)
95-
addFragment(viewModel.getProgramFragment)
102+
addFragment { viewModel.getDashboardFragment }
103+
addFragment { viewModel.getProgramFragment }
96104
}
97105
binding.viewPager.adapter = adapter
98106
binding.viewPager.setUserInputEnabled(false)

0 commit comments

Comments
 (0)