You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Room数据库运行后设备文件资源管理器databases文件夹为空

Room数据库未生成、databases文件夹为空问题

问题表现

应用运行无报错,Device File Explorer中应用包名下的databases文件夹为空,无法找到数据库文件。
databases文件夹截图

根因分析

Room不会在初始化数据库实例时立刻创建数据库文件,只有第一次执行实际增删改查操作时才会生成文件。当前代码存在逻辑错误导致插入操作从未执行:

  • 点击添加按钮时,先清空了输入框内容,再调用插入方法,插入方法内部重新读取输入框做非空判断,此时输入框为空,插入逻辑被跳过
  • 删除按钮点击监听被错误嵌套在添加按钮的点击监听内部,存在逻辑隐患
  • 额外存在两个设计问题:主键未设置自增导致数据被覆盖、列表未观察数据库数据导致重启后数据丢失

修复方案

1. 修正Todo实体主键配置

给主键添加自增属性,无需手动传入id:

package com.example.todoit.data

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

@Entity(tableName = "todo_data")
data class Todo (
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    val title: String,
    var isChecked: Boolean = false
)

2. 修正ViewModel访问权限

将TodoViewModel中的readAllData从私有改为公开,供Activity观察数据:

// 原代码是private val readAllData,删掉private修饰符即可
val readAllData: LiveData<List<Todo>>

3. 修正插入逻辑与点击事件嵌套问题

调整MainActivity中点击事件的代码顺序与大括号位置,删除插入方法中冗余的输入框判空:

// 修正后的onCreate中点击事件部分
btnAddTodo.setOnClickListener {
    val todoTitle = etTodoTitle.text.toString()
    if (todoTitle.isNotEmpty()) {
        val todo = Todo(title = todoTitle)
        etTodoTitle.text.clear()
        insertDataToDataBase(todo)
        todoAdapter.addTodo(todo)
    }
}
btnDeleteTodo.setOnClickListener {
    todoAdapter.deleteDoneTodos()
}

// 修正后的插入方法
private fun insertDataToDataBase(todo: Todo) {
    todoViewModel.addTodoToDataBase(todo)
}

4. 给Adapter添加数据刷新方法

在TodoAdapter中增加setData方法,用于数据库数据变更时全量刷新列表:

fun setData(newTodos: List<Todo>) {
    todos.clear()
    todos.addAll(newTodos)
    notifyDataSetChanged()
}

5. 增加数据库数据观察

在MainActivity的onCreate中给数据库LiveData添加观察者,数据变化时自动更新列表:

// 放在RecyclerView初始化代码之后
todoViewModel.readAllData.observe(this) { todoList ->
    todoAdapter.setData(todoList)
}

6. 验证结果

重新运行应用,添加至少一条待办事项,点击Device File Explorer右上角的同步刷新按钮,进入/data/data/com.example.todoit/databases/目录即可看到生成的todo_database相关文件。


附:原始提交代码

TodoDao

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 TodoDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun addTodo(todo: Todo)

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

TodoDataBase

package com.example.todoit.data

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

@Database(entities = [Todo::class],version = 1, exportSchema = false)
abstract class TodoDataBase: RoomDatabase() {

    abstract fun todoDao(): TodoDao

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

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

TodoRepository

package com.example.todoit.data

import androidx.lifecycle.LiveData

class TodoRepository(private val todoDao:TodoDao) {
    val readAllData: LiveData<List<Todo>> = todoDao.readAllData()

    suspend fun addTodo(todo:Todo) {
        todoDao.addTodo(todo)
    }
}

TodoViewModel(原始版本)

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 TodoViewModel(application: Application) : AndroidViewModel(application) {

    private val readAllData: LiveData<List<Todo>>
    private val repository: TodoRepository

    init {
        val todoDao = TodoDataBase.getDataBase(application).todoDao()
        repository = TodoRepository(todoDao)
        readAllData = repository.readAllData
    }

    fun addTodoToDataBase(todo: Todo) {
        viewModelScope.launch(Dispatchers.IO) {
            repository.addTodo(todo)
        }
    }
}

MainActivity(原始版本)

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.Todo
import com.example.todoit.data.TodoViewModel
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    private lateinit var todoAdapter: TodoAdapter
    private lateinit var todoViewModel: TodoViewModel

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

        todoViewModel = ViewModelProvider(this).get(TodoViewModel::class.java)
        todoAdapter = TodoAdapter(mutableListOf())
        rvTodoItems.layoutManager = LinearLayoutManager(this)
        rvTodoItems.adapter = todoAdapter

        btnAddTodo.setOnClickListener {
            val todoTitle = etTodoTitle.text.toString()
            if (todoTitle.isNotEmpty()) {
                val todo = Todo(0,todoTitle,false)
                etTodoTitle.text.clear()
                insertDataToDataBase(todo)
                todoAdapter.addTodo(todo)
        }
        btnDeleteTodo.setOnClickListener {
            todoAdapter.deleteDoneTodos()
        }
    }}

    private fun insertDataToDataBase(todo: Todo) {
        val todoTitle = etTodoTitle.text.toString()

        if(todoTitle.isNotEmpty()) {
            todoViewModel.addTodoToDataBase(todo)
        }
    }
}

TodoAdapter(原始版本)

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.Todo
import kotlinx.android.synthetic.main.item_todo.view.*

class TodoAdapter(
    private val todos: MutableList<Todo>,
    ) : RecyclerView.Adapter<TodoAdapter.TodoViewHolder>() {

    class TodoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)

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

    fun addTodo(todo: Todo) {
        todos.add(todo)
        notifyItemInserted(todos.size - 1)
    }

    fun deleteDoneTodos() {
        todos.removeAll { todo ->
            todo.isChecked
        }
        notifyDataSetChanged()
    }

    private fun toggleStrikeThrough(tvTodoTitle: TextView, isChecked: Boolean) {
        if (isChecked) {
            tvTodoTitle.paintFlags = tvTodoTitle.paintFlags or STRIKE_THRU_TEXT_FLAG
        } else {
            tvTodoTitle.paintFlags = tvTodoTitle.paintFlags and STRIKE_THRU_TEXT_FLAG.inv()
        }
    }

    override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
        val curTodo = todos[position]
        holder.itemView.apply {
            tvTodoTitle.text = curTodo.title
            cbDone.isChecked = curTodo.isChecked
            toggleStrikeThrough(tvTodoTitle, curTodo.isChecked)
            cbDone.setOnCheckedChangeListener { _, isChecked ->
                toggleStrikeThrough(tvTodoTitle, isChecked)
                curTodo.isChecked = !curTodo.isChecked
            }
        }
    }

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

Module级build.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 {
    def roomVersion = "2.4.2"
    implementation "androidx.room:room-ktx:$roomVersion"
    kapt "androidx.room:room-compiler:$roomVersion"

    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'

    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"

    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"
}

Project级build.gradle

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
}

内容的提问来源于stack exchange,提问作者Clivityy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 03:57:19