Skarlet
Skarlet

Reputation: 51

Updating LiveData + Room in inactive model

I'm trying to observe changes in DB made from another fragment.

I have a fragment A (contains a recyclerView with items) with a ViewModel that has inside a LiveData property from the Room database.

Like this: val allItems: LiveData<List <Item>> = repo.getAll()

If I open a new fragment (let's call it B) from fragment A and do repo.insert(item) there, I expect the LiveData's observer to fire on allItems when returning back to fragment A. But that doesn't happen.

How a can fix it nicely?

Of course I can get data in onViewCreated() every time I open the fragment A, but I believe there must be a better way.

class CharactersViewModel : BaseViewModel() {
    private val db get() = AppDatabase.getDB(MyApplication.application)

    private val repo = CharacterRepository(viewModelScope, db.characterDao())

    val characters: LiveData<List<Character>> = repo.getAll()
}
class CharacterRepository(
    private val scope: CoroutineScope,
    private val dao: CharacterDao
) {
    fun getAll() = dao.getAll()

    fun getById(itemId: Int) = dao.getById(itemId)

    fun insert(item: Character) = scope.launch {
        dao.insert(item)
    }

    fun update(item: Character) = scope.launch {
        dao.update(item)
    }

    fun delete(item: Character) = scope.launch {
        dao.delete(item)
    }
}
@Dao
interface CharacterDao {
    fun getAll(): LiveData<List<Character>>

    fun getById(itemId: Int) : LiveData<Character>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(item: Character)

    @Update(onConflict = OnConflictStrategy.REPLACE)
    suspend fun update(item: Character)

    @Delete
    suspend fun delete(item: Character)
}

Note: It looks like this is because the ViewModel of Fragment A is currently inactive. And the issue is not due to viewLifecycleOwner, since the observeForever is also not notified.

Upd: Just found the issue, answer attached.

Upvotes: 1

Views: 150

Answers (1)

Skarlet
Skarlet

Reputation: 51

There was when getting the database instance.

fun getDB(appContext: Context) = Room.databaseBuilder(
    appContext,
    AppDatabase::class.java, DB_NAME
).build()

I solved the problem by making it a singleton, so now it returns the same instance of the DB.

companion object {
    @Volatile
    private var INSTANCE: AppDatabase? = null

    @Synchronized
    fun getDB(context: Context): AppDatabase {
        // if the INSTANCE is not null, then return it, otherwise create the database
        return INSTANCE ?: run {
            val instance = Room.databaseBuilder(
                context.applicationContext,
                AppDatabase::class.java,
                DB_NAME
            ).build()
            INSTANCE = instance

            instance
        }
    }

}

Upvotes: 2

Related Questions