Room数据库运行后设备文件资源管理器databases文件夹为空
Room数据库未生成、databases文件夹为空问题
问题表现
应用运行无报错,Device File Explorer中应用包名下的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
相关产品推荐
相关产品推荐

