Bon Echo
Bon Echo

Reputation: 67

RecyclerView loses state on rotation

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

Answers (2)

user1713450
user1713450

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

Boubacar Bah
Boubacar Bah

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

Related Questions