I'm using architecture-components-samples to learn how multiple back stacks work. Initially, the example works well. I'm interested in the Leaderboard tab, which contains a list of users. In this example, I added Koin and created a LeaderboardViewModel for long-term state storage.
LeaderboardViewModel
class LeaderboardViewModel : ViewModel() {
private val _users = MutableSharedFlow<Array<String>>(1)
val users: SharedFlow<Array<String>> = _users
init {
Log.d("test_t", "Init VM")
initUserList()
}
private fun initUserList() {
viewModelScope.launch(Dispatchers.IO) {
_users.emit(Array(100) { "Person ${it + 1}" })
}
}
override fun onCleared() {
super.onCleared()
Log.d("test_t", "LeaderboardViewModel onCleared")
}
}
LeaderboardFragment
/**
* Shows a static leaderboard with multiple users.
*/
class Leaderboard : Fragment() {
private val viewModel: LeaderboardViewModel by koinNavGraphViewModel(R.id.list)
private lateinit var viewAdapter: MyAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_leaderboard, container, false)
lifecycleScope.launchWhenCreated {
viewModel.users.collect {
viewAdapter = MyAdapter(it)
view.findViewById<RecyclerView>(R.id.leaderboard_list).run {
adapter = viewAdapter
}
}
}
return view
}
}
class MyAdapter(private val myDataset: Array<String>) :
RecyclerView.Adapter<MyAdapter.ViewHolder>() {
class ViewHolder(val item: View) : RecyclerView.ViewHolder(item)
// Create new views (invoked by the layout manager)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ViewHolder {
// create a new view
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.list_view_item, parent, false)
return ViewHolder(itemView)
}
// Replace the contents of a view (invoked by the layout manager)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.item.findViewById<TextView>(R.id.user_name_text).text = myDataset[position]
holder.item.findViewById<ImageView>(R.id.user_avatar_image)
.setImageResource(listOfAvatars[position % listOfAvatars.size])
holder.item.setOnClickListener {
val bundle = bundleOf(USERNAME_KEY to myDataset[position])
holder.item.findNavController().navigate(
R.id.action_leaderboard_to_userProfile,
bundle
)
}
}
// Return the size of your dataset (invoked by the layout manager)
override fun getItemCount() = myDataset.size
companion object {
const val USERNAME_KEY = "userName"
}
}
private val listOfAvatars = listOf(
R.drawable.avatar_1_raster,
R.drawable.avatar_2_raster,
R.drawable.avatar_3_raster,
R.drawable.avatar_4_raster,
R.drawable.avatar_5_raster,
R.drawable.avatar_6_raster
)
Navigation.xml
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph"
app:startDestination="@+id/home">
<include app:graph="@navigation/home"/>
<include app:graph="@navigation/list"/>
<include app:graph="@navigation/form"/>
</navigation>
At first glance, this works well when switching tabs.
But when I am on this tab and click on the BackButton, then my ViewModel is destroyed (call onCleared), and as a result of which the whole state returns to its original state.
Please tell me how can I fix this? I need that when I click on the BackButton and the next switching to other tabs, the ViewModel is not destroyed. What are some ideas? Thanks