I'm looking to implement a SearchView feature in an app that utilizes the ROOM library. During my online research, I noticed that many examples follow a pattern similar to the one below:
override fun onQueryTextChange(newText: String?): Boolean {
if (newText != null) {
mViewModel.searchAllDatabase(newText).observe(viewLifecycleOwner){
...
}
}
return true
}
Now, I'm curious to know if I'm getting the whole thing right. Could it be the case that with this approach, a new observer is created each time a user enters a new letter and the onQueryTextChange method is triggered? If that's the case, does this potentially lead to memory leaks? Or, on the other hand, does the viewLifecycleOwner automatically handle these observers, ensuring they are disposed of when they're no longer needed?
Edit:what if we follow this approach:
override fun onQueryTextChange(newText: String?): Boolean {
if (newText != null) viewModel.searchQuery.value = newText
return true
}
Inside onViewCreated:
viewModel.allData.observe(viewLifecycleOwner) {
adapter.setData(it)
}
viewModel.searchQuery.observe(viewLifecycleOwner) { query ->
if (viewModel.allData.value != null) {
adapter.setData(viewModel.allData.value!!.filter { it.title.contains(query) })
}
}
From what you’ve shown, this does leak observers. There will be obsolete observers sitting in memory for almost every character typed or deleted until the Fragment’s view is destroyed. Even worse, if these observers come from Room, they will all be running queries every time the database is modified until they are released.
One way to avoid this would be to use a switch map operation based on the search query. The switch map operation does exactly what we want…when there is new data, it cancels observing the previous LiveData and starts a new one. This is done under its own hood, so to an outside observer, it’s just a single LiveData stream.
In the ViewModel, something like this:
Now in your UI classes you only observe
viewModel.searchedDataa single time inonViewCreated()and do nothing but set the new value ofViewModel.queryTextinonQueryTextChange().Regarding your proposed solution, there are a couple of drawbacks.
First, it is really hacky to be trying read the
valueof a LiveData directly instead of observing it. In this case it leaves open the possibility of the user typing a letter before theallDatagets its first value set, leaving the screen blank until the user types again.Second, you are manually sifting through the data to perform the search yourself, which is likely less efficient than letting the repository do it. Databases are optimized for that kind of thing.