When the app is ran, no errors occur however, the databases folder in device file explorer is empty. I have tried to fix the issue myself but since I'm new to android studio and kotlin I haven't been able to I just want to ensure the database is working before I carry on with the rest of the code.

Pic of databases folder

任何帮助都将不胜感激.

Code:

全部的

package com.example.todoit.data

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "todo_data")
data class 全部的 (
    @PrimaryKey val id: Int,
    val title: String,
    var isChecked: Boolean = false
)

全部的Dao

package com.example.todoit.data

import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query

@Dao
interface 全部的Dao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun add全部的(todo: 全部的)

    @Query("SELECT * FROM todo_data ORDER BY id ASC")
    fun readAllData(): LiveData<List<全部的>>
}

全部的DataBase

package com.example.todoit.data

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(entities = [全部的::class],version = 1, exportSchema = false)
abstract class 全部的DataBase: RoomDatabase() {

    abstract fun todoDao(): 全部的Dao

    companion object{
        @Volatile
        private var INSTANCE: 全部的DataBase? = null

        fun getDataBase(context: Context):全部的DataBase{
            val tempInstance = INSTANCE
            if(tempInstance != null){
                return tempInstance
            }
            synchronized(this){
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    全部的DataBase::class.java,
                    "todo_database"
                ).build()
                INSTANCE = instance
                return instance
            }
        }


    }
}

全部的Repository

package com.example.todoit.data

import androidx.lifecycle.LiveData

class 全部的Repository(private val todoDao:全部的Dao) {
    val readAllData: LiveData<List<全部的>> = todoDao.readAllData()

    suspend fun add全部的(todo:全部的) {
        todoDao.add全部的(todo)
    }
}

全部的ViewModel


package com.example.todoit.data

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch




class 全部的ViewModel(application: Application) : AndroidViewModel(application) {

    private val readAllData: LiveData<List<全部的>>
    private val repository: 全部的Repository




    init {
        val todoDao = 全部的DataBase.getDataBase(application).todoDao()
        repository = 全部的Repository(todoDao)
        readAllData = repository.readAllData
    }


    fun add全部的ToDataBase(todo: 全部的) {
        viewModelScope.launch(Dispatchers.IO) {
            repository.add全部的(todo)
        }
    }
}

主要活动

package com.example.todoit

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.todoit.data.全部的
import com.example.todoit.data.全部的ViewModel
import kotlinx.android.synthetic.main.activity_main.*


class 主要活动 : AppCompatActivity() {
    private lateinit var todoAdapter: 全部的Adapter
    private lateinit var todoViewModel: 全部的ViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        todoViewModel = ViewModelProvider(this).get(全部的ViewModel::class.java)
        todoAdapter = 全部的Adapter(mutableListOf())
        rv全部的Items.layoutManager = LinearLayoutManager(this)
        rv全部的Items.adapter = todoAdapter

        btnAdd全部的.setOnClickListener {
            val todoTitle = et全部的Title.text.toString()
            if (todoTitle.isNotEmpty()) {
                val todo = 全部的(0,todoTitle,false)
                et全部的Title.text.clear()
                insertDataToDataBase(todo)
                todoAdapter.add全部的(todo)
        }
        btnDelete全部的.setOnClickListener {
            todoAdapter.deleteDone全部的s()
        }
    }}

    private fun insertDataToDataBase(todo: 全部的) {
        val todoTitle = et全部的Title.text.toString()

        if(todoTitle.isNotEmpty()) {
            //Add data to database
            todoViewModel.add全部的ToDataBase(todo)
        }
    }
}

全部的Adapter

package com.example.todoit

import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.todoit.data.全部的
import kotlinx.android.synthetic.main.item_todo.view.*

class 全部的Adapter(
    private val todos: MutableList<全部的>,
    ) : RecyclerView.Adapter<全部的Adapter.全部的ViewHolder>() {

    class 全部的ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): 全部的ViewHolder {
        return 全部的ViewHolder(
            LayoutInflater.from(parent.context).inflate(
                R.layout.item_todo,
                parent,
                false
            )
        )
    }

    fun add全部的(todo: 全部的) {
        todos.add(todo)
        notifyItemInserted(todos.size - 1)
    }

    fun deleteDone全部的s() {
        todos.removeAll { todo ->
            todo.isChecked
        }
        notifyDataSetChanged()
    }

    private fun toggleStrikeThrough(tv全部的Title: TextView, isChecked: Boolean) {
        if (isChecked) {
            tv全部的Title.paintFlags = tv全部的Title.paintFlags or STRIKE_THRU_TEXT_FLAG
        } else {
            tv全部的Title.paintFlags = tv全部的Title.paintFlags and STRIKE_THRU_TEXT_FLAG.inv()
        }
    }

    override fun onBindViewHolder(holder: 全部的ViewHolder, position: Int) {
        val cur全部的 = todos[position]
        holder.itemView.apply {
            tv全部的Title.text = cur全部的.title
            cbDone.isChecked = cur全部的.isChecked
            toggleStrikeThrough(tv全部的Title, cur全部的.isChecked)
            cbDone.setOnCheckedChangeListener { _, isChecked ->
                toggleStrikeThrough(tv全部的Title, isChecked)
                cur全部的.isChecked = !cur全部的.isChecked
            }
        }
    }

    override fun getItemCount(): Int {
        return todos.size
    }
}

Gradle(模块)

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id "kotlin-android-extensions"
}
apply plugin: 'kotlin-kapt'

android {
    compileSdk 32

    defaultConfig {
        applicationId "com.example.todoit"
        minSdk 21
        targetSdk 32
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}



dependencies {
    //ROOM
    def roomVersion = "2.4.2"
    implementation "androidx.room:room-ktx:$roomVersion"
    kapt "androidx.room:room-compiler:$roomVersion"

    // Navigation Component
    implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
    implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'


    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.6.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

    // Lifecycle components
    implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
    implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"

    // Kotlin components
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72"
    api "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5"
    api "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5"
}

Gradle(项目)

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    id 'com.android.application' version '7.2.0' apply false
    id 'com.android.library' version '7.2.0' apply false
    id 'org.jetbrains.kotlin.android' version '1.5.30' apply false
}



task clean(type: Delete) {
    delete rootProject.buildDir
}

推荐答案

我只想在继续编写其余代码之前确保数据库正常工作.

然后我建议单独进行测试(存在一些问题).

我建议暂时在TodoDataBase类的databaseBuilder中添加.allowMainThreadQueries,例如.

        synchronized(this){
            val instance = Room.databaseBuilder(
                context.applicationContext,
                TodoDataBase::class.java,
                "todo_database"
            )
                .allowMainThreadQueries() // ADDED <<<<<<<<<<
                .build()
            INSTANCE = instance
            return instance
        }

现在,您可以避免在另一个线程上运行.

然后,您可以在TodoDao中添加另一个功能,例如:.

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun altInsert(todo: Todo): Long

然后,您可以再次临时添加/修改MainActivity,通过以下方式调用主线程上的插入:-

a) 为VAR添加2个LateInit以容纳TodoDataBaseTodoDao实例,例如:-

lateinit var db: TodoDataBase
lateinit var dao: TodoDao

b) 实例化新的LateInit(在setContentView之后立即建议):-

    db = TodoDataBase.getDataBase(this)
    dao = db.todoDao()

c) 在Add按钮的on click listener中,使用altInsert调用替换insertDataToDatabase,可能需要判断(例如Toast),例如:-

            /* ADDED */
            //insertDataToDataBase(todo)
            if (dao.altInsert(todo) > 0) {
                Toast.makeText(this,"Added",Toast.LENGTH_SHORT).show()
            } else {
                Toast.makeText(this,"Oooops",Toast.LENGTH_SHORT).show()
            }
            /* END OF ADDED */

现在,您可以通过运行应用程序然后进行测试来专注于数据库方面,例如添加3个Todo(例如Test001、Test002和Test003)和:-

因此,当应用程序运行时,在标题中输入Test001,然后单击添加按钮:-

enter image description here

  • 现在,祝wine 词说哎呀.但是,使用App Inspection(比Device Explorer好得多):-

enter image description here

现在开始测试002

enter image description here

很明显,Test001已经消失,这是一个问题.

这是一个双重问题,Todo的id列是使用@PrimaryKey val id: Int,定义的,而insert将id设置为0.因此会发生冲突,因为id 0已经存在,因此Test001被Test002替换,而我认为您需要2个Todos Test001和Test002.

我建议改用@PrimaryKey val id: Long?=null,.这不允许提供值/null,因此SQLite将生成唯一的id(通常是1、2、3……).id可以是64位有符号整数,因此Long而不是Int更合适(就SQLite而言,它不需要更多存储,因为它将以尽可能少的字节存储值).

  • 当0被房间转换为无值/空时,另一种方法是代码@PrimaryKey(autoGenerate = true).然而,这是以包含自动增量关键字为代价的,这不太可能是必要的,而且效率低下.

    • SQLite documentation表示The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and disk I/O overhead and should be avoided if not strictly needed. It is usually not needed.

因此,Todo可能成为:-

@Entity(tableName = "todo_data")
data class Todo (
    @PrimaryKey val id: Long?=null,
    val title: String,
    var isChecked: Boolean = false
)

为了不使用0,通过上述更改,插入可以更改为val todo = Todo(title = todoTitle, isChecked = false)

卸载应用程序(因此无需增加版本)并运行后,添加3个测试:-

enter image description here

然而,应用程序本身并没有反映这一点,它显示:-

enter image description here

这是TodoAdapter中的一个问题,这将是另一个问题.

当然,使用Device Explorer,然后:-

enter image description here

Android相关问答推荐

ArrayList.remove()(Kotlin中的removeAt())导致奇怪的IndexOutOfBoundsResponse异常

如何在点击按钮时将字符串插入到文本字段中的光标位置?

如何使TextField的背景透明?

如何将我的Android应用程序(Kotlin)中的图像分享给其他应用程序?

使用Kotline绑定时,ViewHolder无法识别文本视图

解决失败:Landroidx/compose/runtime/PrimitiveSnapshotStateKt

使用 List 和 LazyColumn 重新组合所有项目

如何仅使用您的 Android 应用程序定位平板电脑?

在 MVVM Jetpack Compose 上添加依赖项时出现重复类错误

如何知道我的应用程序的新版本是否显示广告?

浏览器未命中断点判断 USB 连接设备

在Android RoomDB中使用Kotlin Flow和删除数据时如何解决错误?

修复错误 Invariant Violation: requireNativeComponent: "RNSScreenStackHeaderConfig" was not found in the UIManager

为片段设置主题

如何从日期 Select 器计算年龄?

在 Kotlin 中打开新片段时如何对当前片段应用更改?

如何修复 api 调用在浏览器中工作但在 android studio 中为 403

如何从我的 android 应用程序中删除 QUERY_ALL_PACKAGES 权限?

如何使用jetpack compose实现布局,其中图标在列布局上是绝对位置

使用 Android 字符串数组在 Room 中迁移