Reputation: 67
I am making a note-taking app using MVVM design pattern, Room for persisting data and paging
The problem is when I rotate my device it appears like it keeps its state for less than one second, then the RecyclerView scrolls up
I've debugged my code and found that onChanged()
is called multiple times
here is my code
NoteRepository
override fun loadPagedNotes() : LiveData<PagedList<Note>> {
val factory : DataSource.Factory<Int, Note> = mNotesDao.getNotes()
val mNotesList = MutableLiveData<PagedList<Note>>()
val notesList = RxPagedListBuilder(
factory, PagedList.Config
.Builder()
.setPageSize(20)
.setEnablePlaceholders(true)
.build()
).buildFlowable(BackpressureStrategy.LATEST)
mDisposables.add(
notesList.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
mNotesList.value = it
}
)
return mNotesList
}
NoteViewModel
fun loadPagedNotes() : LiveData<PagedList<Note>> {
return mNoteRepository.loadPagedNotes()
}
HomeActivity
class HomeActivity : AppCompatActivity() {
private lateinit var mBinding : ActivityHomeBinding
private val mViewModel : NoteViewModel by viewModel()
override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState)
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_home)
loadNotes()
}
private fun loadNotes() {
mViewModel.loadPagedNotes()
.observe(this,
Observer {
if (it.isNotEmpty()) {
addNotesToRecyclerView(it)
}
})
}
private fun addNotesToRecyclerView(list : PagedList<Note>?) {
showRecyclerView()
val adapter = PagedNoteListAdapter(this@HomeActivity)
adapter.submitList(list)
mBinding.notesRecyclerView.adapter = adapter
}
}
Upvotes: 2
Views: 1411
Reputation: 1429
When you rotate your device, the view (fragment) is destroyed and re-created, but the ViewModel remains. You're running into trouble because your ViewModel never leverages this feature: instead, on (re-)creation of your view, it causes the ViewModel to re-fetch the data all over again. Each invocation of the VM's loadPagedNotes
will create a new LiveData and return it. You really want to have just one and return a reference to that one every single time.
Why don't you change your code to be something like this
class NoteViewModel: ViewModel() {
val pagedNotes = mNoteRepository.loadPagedNotes()
}
and in your activity
override fun onCreate(/*...*/) {
mViewModel.pagedNotes.observe(this, Observer { addNotesToRecyclerView(it)
})
Also, consider moving this val adapter = PagedNoteListAdapter(this@HomeActivity)
to an instance member of the Activity: val mAdapter = ...
. That way, your addNotesToRecyclerView
code will not create and bind a new adapter every time there is an update from your repository.
class HomeActivity: /*...*/ {
val mAdapter = PagedNoteListAdapter(this)
override fun onCreate(...) {
...
mBinding.notesRecyclerView.adapter = mAdapter
}
private fun addNotesToRecyclerView(list: PagedList<Note>) // You don't need the question mark since you're never calling the function except when you already know `list` isn't null) {
mAdapter.submitList(list)
}
That could be another reason you're experiencing this.
Also if you still have undesirable refreshing, look into DiffUtil. It enables you to update items of an adapter if they've individually changed, so even if the overall list changes but some elements stay the same, you'll only redraw the changed elements without redrawing the ones that haven't changed (e.g., if you've added one new item it won't redraw everything but just draw the one new item)
Upvotes: 1
Reputation: 1
I'm not a RxJava expert so I can't speak to that too much but have you tried getting your ViewModel from the ViewModelProviders class instead:
myViewModel = ViewModelProviders.of(this)[MyViewModel::class.java]
I believe creating a ViewModel instance this way allows your ViewModel to live outside of your Activities lifecycle and may help with your state issue.
Upvotes: 0