How to implement and update a Room database correctly?

1 day ago 3
ARTICLE AD BOX

I am programming a Counter App where the count is stored in a database. I am using Kotlin, Jetpack Compose, Room and Hilt. When I press the Button for increment, the count do not change. I tried to debug with Log.d(...) and always get this at Logcat no matter how often I press the increment Button:

Incrementing counter...

incrementCount() called with id=0

Loaded count from DB: 0

Entity

@Entity data class CounterEntity( @PrimaryKey(autoGenerate = true) val id: Int = 0, val count: Int)

Data Access Object

@Dao interface CounterDataAccessObject { @Query("SELECT count FROM CounterEntity WHERE id = :id") suspend fun getCount(id: Int): Int? @Query("UPDATE CounterEntity SET count = count + 1 WHERE id = :id") suspend fun incrementCount(id: Int) { Log.d("CounterDAO", "incrementCount() called with id=$id") } @Query("UPDATE CounterEntity SET count = count - 1 WHERE id = :id") suspend fun decrementCount(id: Int) }

Database

@Database( entities = [CounterEntity::class], version = 1, exportSchema = false ) abstract class CounterDatabase : RoomDatabase() { abstract fun counterDataAccessObject() : CounterDataAccessObject companion object { @Volatile private var INSTANCE: CounterDatabase? = null fun getDatabase(context: Context): CounterDatabase { return INSTANCE ?: synchronized(this) { val instance = Room.databaseBuilder( context.applicationContext, CounterDatabase::class.java, "CounterDatabase" ).build() INSTANCE = instance instance } } } }

DI App Module

@Module @InstallIn(SingletonComponent::class) object AppModule { @Provides @Singleton fun provideCounterDatabase(@ApplicationContext context: Context): CounterDatabase { return CounterDatabase.getDatabase(context) } @Provides @Singleton fun provideCounterDataAccessObject(counterDatabase: CounterDatabase): CounterDataAccessObject { return counterDatabase.counterDataAccessObject() } @Provides @Singleton fun provideCounterLocalDataSource(counterDataAccessObject: CounterDataAccessObject): CounterLocalDataSource { return CounterLocalDataSource(counterDataAccessObject) } @Provides @Singleton fun provideCounterRepository(counterLocalDataSource: CounterLocalDataSource): CounterRepository { return CounterRepository(counterLocalDataSource) } }

View Model

@HiltViewModel class CounterViewModel @Inject constructor( private val counterRepository: CounterRepository ) : ViewModel() { // interner veränderbarer Zustand private val _uiState: MutableStateFlow<CounterUiState> = MutableStateFlow(CounterUiState()) // öffentlicher unveränderlicher Zugriff val uiState: StateFlow<CounterUiState> = _uiState.asStateFlow() // initialen Zählstand aus dem Repository laden init { loadCount() } // Zählstand aus dem Repository laden fun loadCount() { viewModelScope.launch{ val count: Int = counterRepository.getCount() Log.d("CounterViewModel", "Loaded count from DB: $count") _uiState.update { it.copy(count = count) } } } fun increment() { viewModelScope.launch { Log.d("CounterViewModel", "Incrementing counter...") counterRepository.incrementCount() loadCount() } } fun decrement() { viewModelScope.launch { counterRepository.decrementCount() loadCount() } } }

Ui State

data class CounterUiState( val count: Int = 0 )

Screen

@Composable fun CounterScreen( modifier: Modifier = Modifier, count: Int, onIncrement: () -> Unit, onDecrement: () -> Unit, ) { Column( modifier = modifier .fillMaxSize() .background(MaterialTheme.colorScheme.background), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { Text( text = "Counter: $count", color = MaterialTheme.colorScheme.onBackground, fontSize = 20.sp, fontWeight = FontWeight.Bold ) Spacer(modifier = Modifier.height(10.dp)) Row( horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { Button( modifier = Modifier.width(80.dp), colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.primary, // contentColor = MaterialTheme.colorScheme.onPrimary), // onClick = onDecrement ) { Text("-") } Spacer(modifier = Modifier.width(10.dp)) Button( modifier = Modifier.width(80.dp), colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.primary, // contentColor = MaterialTheme.colorScheme.onPrimary), // onClick = onIncrement ) { Text("+") } } } }

Main Activity

@AndroidEntryPoint class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { CounterTheme( darkTheme = false ) { val counterViewModel: CounterViewModel by viewModels() val state by counterViewModel.uiState.collectAsStateWithLifecycle() LaunchedEffect(state) { Log.d("MainActivity", "UI State updated: $state") } Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> CounterScreen( modifier = Modifier.padding(innerPadding), count = state.count, onIncrement = counterViewModel::increment, onDecrement = counterViewModel::decrement, ) } } } } }

My App

@HiltAndroidApp class MyApp : Application() {}

Why does this problem occur and how can I solve it?

Read Entire Article