Android – Room Database enter loop on data update

  Kiến thức lập trình

I’m developing an app using Jetpack Compose, Room, Flow, Kotlin, and following the MVVM architecture. My app includes the following screens:

HomeScreen: Displays the budget and a list of products.
NewItemScreen: Allows adding new products.
ItemDetailsScreen: Shows details of a selected product.
EditItemScreen: Allows editing an existing product.
The app uses a ViewModel to manage UI-related data, and I’m facing an issue with the EditItemScreen.

Problem Description:

When I try to update an item’s details in the EditItemScreen, the values seem to enter a loop, alternating between the new and old values. Sometimes it does not update at all or needs some time between updates.

Code:

Here is the relevant code:

EditItemViewModel

class EditItemViewModel(
    savedStateHandle: SavedStateHandle,
    private val itemsRepository: ItemsRepository
): ViewModel() {

    companion object {
        private const val TAG = "Edit Item"
    }

    var editItemUiState by mutableStateOf(ItemUiState())
        private set

    private val itemId: Int = checkNotNull(savedStateHandle[EditItemDestination.itemIdArg])

    init {
        viewModelScope.launch {
            val item = itemsRepository.getItemStream(itemId)
                .filterNotNull()
                .first()
            editItemUiState = item.toItemUiState(isEntryValid = true)
        }
    }

    fun updateEditUiState(itemDetails: ItemDetails) {
        editItemUiState =
            editItemUiState.copy(
                itemDetails = itemDetails,
                isEntryValid = validateInput(itemDetails)
            )
    }

    fun updateEditTag(tag: String) {
        editItemUiState =
            editItemUiState.copy(tag = tag)
    }

    fun updateItem() {
        val item = editItemUiState.itemDetails.toItem()
        if (validateInput()) {
            viewModelScope.launch {
                try {
                    itemsRepository.updateItem(item)
                    // Añadir un pequeño retraso para asegurar que la actualización se complete
                    delay(100)
                    // Recargar el item después de la actualización
                    val updatedItem = itemsRepository.getItemStream(item.id).filterNotNull().first()
                    editItemUiState = updatedItem.toItemUiState(isEntryValid = true)
                } catch (e: Exception) {
                    Log.e(TAG, "Item could not be updated", e)
                }
            }
        }
    }

    private fun validateInput(itemDetails: ItemDetails = editItemUiState.itemDetails): Boolean {
        return with(itemDetails) {
            name.isNotBlank() && price.isNotBlank()
        }
    }

}

ItemDao

@Dao
interface ItemDao {

    @Insert(onConflict = OnConflictStrategy.ABORT)
    suspend fun insert(item: Item)
    
    @Update
    suspend fun update(item: Item)

    @Delete
    suspend fun delete(item: Item)

    @Query("SELECT * FROM items")
    fun getAll(): Flow<List<Item>>

    @Query("SELECT * FROM items WHERE id = :id")
    fun getItem(id: Int): Flow<Item>

    @Query("SELECT * FROM items WHERE price < :price")
    fun getByPrice(price: Double): Flow<List<Item>>

}

ItemsRepository

interface ItemsRepository {

    fun getAllItemsStream(): Flow<List<Item>>

    fun getItemStream(id: Int): Flow<Item?>

    fun getByPrice(price: Double): Flow<List<Item>>

    suspend fun insertItem(item: Item)

    suspend fun updateItem(item: Item)

    suspend fun deleteItem(item: Item)

}

class OfflineItemsRepository(
    private val itemDao: ItemDao
): ItemsRepository {

    override fun getAllItemsStream(): Flow<List<Item>> = itemDao.getAll()

    override fun getItemStream(id: Int): Flow<Item?> = itemDao.getItem(id)

    override fun getByPrice(price: Double): Flow<List<Item>> = itemDao.getByPrice(price)

    override suspend fun insertItem(item: Item) = itemDao.insert(item)

    override suspend fun updateItem(item: Item) {
        withContext(Dispatchers.IO) {
            itemDao.update(item)
        }
    }
    override suspend fun deleteItem(item: Item) = itemDao.delete(item)
}

I have tried changing the way to handle the state in EditItemViewModel, I have changed the updateItem() function, I have changed OnConflictStrategy in ItemDao and I have tried WithContext in the repository.

No changes

I would appreciate some help 🙂

New contributor

Jesús Rodríguez Segura is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Theme wordpress giá rẻ Theme wordpress giá rẻ Thiết kế website

LEAVE A COMMENT