Как правильно реализовать и обновить базу данных Room?Android

Форум для тех, кто программирует под Android
Anonymous
Как правильно реализовать и обновить базу данных Room?

Сообщение Anonymous »

Я программирую приложение-счетчик, в котором счетчик хранится в базе данных. Я использую Kotlin, Jetpack Compose, Room и Hilt. Когда я нажимаю кнопку для увеличения, счетчик не меняется. Я пытался отлаживать с помощью Log.d(...) и всегда получал это в Logcat, независимо от того, как часто я нажимал кнопку увеличения:
  • Счетчик приращения...
  • Код: Выделить всё

    incrementCount()
    вызывается с id=0
  • Счетчик загружен из БД: 0
Entity

Код: Выделить всё

@Entity
data class CounterEntity(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val count: Int)
Объект доступа к данным

Код: Выделить всё

@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(
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
}
}
}
}
Источник данных

Код: Выделить всё

package com.example.counter.app.III_data

import com.example.counter.app.IV_database.CounterDataAccessObject
import javax.inject.Inject

class CounterLocalDataSource @Inject constructor(
private val counterDatabaseAccessObject: CounterDataAccessObject
) {

// liest aktuellen count
suspend fun getCount(): Int {
//ensureCounterExists()
return counterDatabaseAccessObject.getCount(1) ?: 0
}

// erhöht den aktuelle count um 1
suspend fun incrementCount() {
//ensureCounterExists()
counterDatabaseAccessObject.incrementCount(1)
}

suspend fun decrementCount() {
//ensureCounterExists()
counterDatabaseAccessObject.decrementCount(1)
}

suspend fun resetCount() {
counterDatabaseAccessObject.resetCount(1)
}
}
Репозиторий

Код: Выделить всё

package com.example.counter.app.III_data

import javax.inject.Inject

class CounterRepository @Inject constructor(
private val counterLocalDataSource: CounterLocalDataSource
) {
suspend fun getCount(): Int {
return counterLocalDataSource.getCount()
}

suspend fun incrementCount() {
counterLocalDataSource.incrementCount()
}

suspend fun decrementCount() {
counterLocalDataSource.decrementCount()
}

suspend fun resetCount() {
counterLocalDataSource.resetCount()
}
}
Модуль приложения DI

Код: Выделить всё

@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)
}

}
Просмотр модели

Код: Выделить всё

@HiltViewModel
class CounterViewModel @Inject constructor(
private val counterRepository: CounterRepository
) :  ViewModel() {

// interner veränderbarer Zustand
private val _uiState: MutableStateFlow = MutableStateFlow(CounterUiState())

// öffentlicher unveränderlicher Zugriff
val uiState: StateFlow = _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()
}
}
}
Состояние пользовательского интерфейса

Код: Выделить всё

data class CounterUiState(
val count: Int = 0
)
Экран

Код: Выделить всё

@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("+")
}
}
}
}
Основное действие

Код: Выделить всё

@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,
)
}
}
}
}
}
Мое приложение

Код: Выделить всё

@HiltAndroidApp
class MyApp : Application() {}
Почему возникает эта проблема и как ее решить?

Вернуться в «Android»