简体   繁体   中英

LiveData not updating observer after Room database query

I have a room database where I have songs associated with artists and when I change artist from the Main Activity overflow menu, the fragment with the recyclerview showing a list of songs doesn't update unless I navigate away from the fragment and back again. I thought my observing of the list was sufficient because it worked for other changes being made but not this time.

How do I get it to update with the new artist's songs when data changes?

songViewModel

 //default artist name for this phase of production
    var artistName = "Ear Kitty"
    private val _artistNameLive = MutableLiveData<String>(artistName)
    val artistNameLive: LiveData<String>
        get() = _artistNameLive


    private var _allSongs : MutableLiveData<List<SongWithRatings>> = repository.getArtistSongsWithRatings(artistNameLive.value.toString()).asLiveData() as MutableLiveData<List<SongWithRatings>>

    val allSongs: LiveData<List<SongWithRatings>>
        get() = _allSongs

 fun changeArtist(artist: String){
        _artistNameLive.value = artist
        artistName = artist
        updateAllSongs()

    }
    fun updateAllSongs() = viewModelScope.launch {
        run {
            _allSongs = repository.getArtistSongsWithRatings(artistNameLive.value.toString())
                .asLiveData() as MutableLiveData<List<SongWithRatings>>
        }
    }

MainFragment The observer worked fin when changes were made to all songs but not when it was updated entirely with a different artist.

 songViewModel.allSongs.observe(viewLifecycleOwner) { song ->
            // Update the cached copy of the songs in the adapter.
           Log.d("LiveDataDebug","Main Fragment Observer called")
           Log.d("LiveDataDebug",song[0].song.songTitle)
            song.let { adapter.submitList(it) }
        }

I find a bunch of answers saying to make a function in the Fragment that I call from Main Activity but I don't know where that function would go since I can't make the adapter a member outside of the onViewCreated. At first I'd get an error saying the adapter may have changed then I tried this below and got a null pointer exception.

MainFragment

lateinit var adapter: ItemAdapter
    fun notifyThisFragment(){
        adapter.notifyDataSetChanged()
    }

MainActivity

songViewModel.changeArtist(artistList[which])
val navHostController = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment

//I get the null pointer exception on the line below                            
val mainFrag: MainFragment = navHostController.childFragmentManager.findFragmentById(R.id.mainFragment) as MainFragment
                            
mainFrag.notifyThisFragment()

As I understand it, my main activity hosts the navHostFragment which is the parent fragment to my MainFragment. Am I getting something wrong there? Is there a different way I should be doing this? I'm trying to follow the suggested architecture rather than do a bunch of weird work abounds. Am I supposed to only get allSongs from the db one time and filter it in songViewModel? I don't understand why allSongs.observe isn't getting the change.

I ended up solving the problem by making an allArtistSongsWithRatings that I use for my list adapter. I filter the list from allSongsWithRatings to avoid extra db queries. This was unsuccessful at first because I was returning null when I'd try and filter allSongsWithRatings . It turns out I needed to observe allSongsWithRatings BEFORE filtering it because of the way the LiveData works. I observe allSongsWithRatings in MainActivity and initializeArtist .

Main Activity

//initialize viewModel
        songViewModel.allSongsWithRatings.observe(this){
            songViewModel.initializeWithArtist()
        }

Main Fragment

songViewModel.allArtistSongsWithRatings.observe(viewLifecycleOwner) { song ->
            // Update the cached copy of the songs in the adapter.
           Log.d("LiveDataDebug","Main Fragment Observer called")
           //Log.d("LiveDataDebug",song[0].song.songTitle)
            song.let { adapter.submitList(it) }
        }

SongViewModel

var artistName = "Ear Kitty"
    private val _artistNameLive = MutableLiveData<String>(artistName)
    val artistNameLive: LiveData<String>
        get() = _artistNameLive


    private val _allSongsWithRatings : MutableLiveData<List<SongWithRatings>> = repository.allSongsWithRatings.asLiveData() as MutableLiveData<List<SongWithRatings>>

    val allSongsWithRatings: LiveData<List<SongWithRatings>>
        get() = _allSongsWithRatings

    private val _allArtistSongsWithRatings = (artistNameLive.value?.let {
        repository.getArtistSongsWithRatings(
            it
        ).asLiveData()
    } )as MutableLiveData<List<SongWithRatings>>

    val allArtistSongsWithRatings: LiveData<List<SongWithRatings>>
        get() = _allArtistSongsWithRatings

   
fun changeArtist(artist: String){
        _artistNameLive.value = artist
        artistName = artist
        initializeWithArtist()

    }

fun initializeWithArtist(){

        var newList = mutableListOf<SongWithRatings>()
        newList = allSongsWithRatings.value as MutableList<SongWithRatings>

        //make sure the list isn't empty
        if(newList.isNullOrEmpty()){
            //handle empty list error
        }else{
            //list sorted by performance rating
            _allArtistSongsWithRatings.value = newList.filter { it.song.artistName == artistNameLive.value  } as MutableList<SongWithRatings>
            allArtistSongsWithRatings.value

        }

    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM