安装配置
请注意我配置的详尽步骤,不然后续出错别怪我
官网提供了详尽的可安装依赖:https://developer.android.google.cn/training/data-storage/room?hl=zh-cn
下面所有操作均在 build.gradle(module:app)
中!
第一步,添加 kapt 插件
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
// 添加kapt插件
id 'kotlin-kapt'
}
第二步,添加完整的 room 依赖
请注意,特别是对于 compose 开发,依赖第二条是必须的!否则直接无法编译并报错!
dependencies {
def room_version = "2.2.0"
implementation("androidx.room:room-runtime:$room_version")
kapt "androidx.room:room-compiler:$room_version"
}
room 版本不要选的太高,譬如你当前项目支持的最低 android 版本为 8.0,那么你直接上最新版 2.5.0 的 room,那直接就报错没什么可说的
概念速通
官方教程:https://developer.android.com/codelabs/kotlin-android-training-room-database?hl=zh_cn#0
创建并调用数据库我们只需要三步
- 创建实体类 entity
- 创建数据访问对象 DAO
- 创建数据库单例
Entity
实体使用一个数据类定义
现定义实体类文件 User.kt
请直接看注释
// 实体类均需使用@Entity注解
// 按照官方建议必须定义表名tableName
@Entity(tableName = "user_table")
data class User(
// 主键注解@PrimaryKey
// autoGenerate自增长
@PrimaryKey(autoGenerate = true)
val uid: Int=0,
// 其他键注解@ColumnInfo
// username是存储到数据表的键名
// userName是我们开发中使用的变量名
@ColumnInfo(name = "username")
val userName: String
)
易错点:
如果我们设置了主键且使其自增长,则必须为其添加一个初始值,否则我们调用该实体类时依然需要填写主键的值
DAO
数据访问对象是一个接口
Room 已经为我们准备了 insert 和 delete 接口,我们仅需传入对应的实体类即可让 Room 自动生成对应的 SQL 语法并执行
而其他的语法则全部使用 Query 接口自己写
定义 DAO 文件 UserDao.kt
// DAO注解不可少
@Dao
interface UserDao{
// 插入数据
@Insert
fun insert(user:User)
// 删除数据
@Delete
fun delete(user: User)
// 查询数据
// 这里是查询所有的数据,所以使用List
@Query("select * from user_table")
fun getAll():List<User>
}
Database
下方给出的初始化数据库的抽象类模板是基本固定的,可以直接 cv 拿去用!
数据库目前需要两个关键点:
- 保证单例,即数据库仅实例化一次
- 保证数据库存在时可以拿到数据,不存在时可以自行创建
// 第一步,定义database注解
// 1. entities用到的实体类
// 2. version数据库版本,必须定义,且每次更新数据库都需要修改版本号
// 3. exportSchema是否记录数据库版本
@Database(
entities = [User::class],
version = 1,
exportSchema = false
)
// 继承RoomDatabase
abstract class UserDatabase : RoomDatabase() {
// 第二步:抽象方法userDao
abstract fun userDao(): UserDao
// 第三步:享元实现,保证数据库必须是单例!
// 保证数据库在被实例化时必须是存在的(若不存在就创建后在返回)
companion object {
@Volatile
private var INSTANCE: UserDatabase? = null
fun getInstance(context: Context) =
INSTANCE?: synchronized(this){
// 创建数据库(若数据库不存在)
INSTANCE?:Room.databaseBuilder(
// 上下文
context,
// 欲创建的数据库类
UserDatabase::class.java,
// 数据库名字
"user_database"
)
.fallbackToDestructiveMigration()
.allowMainThreadQueries() // 保证可以在主线程被调用
.build()
}
}
}
开发踩坑
保证仅一次初始化
如果你不愿意拆分出一个单文件用来初始化数据库,而直接在主类中使用它,那么请保证仅初始化一次数据库!
// 全局设置MainActivity上下文
lateinit var mainContext: MainActivity
// 设置全局DAO
lateinit var db: UserDao
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 指定上下文
mainContext = this
// 通过当前主类上下文实例化对应的数据库,并获取DAO
db = UserDatabase.getInstance(mainContext).userDao()
setContent {
...
}
}
}
操作在线程内
利用 kotlin 语法糖 thread,保证所有的数据库操作都写在里面
thread {
db.insert(User(userName = content))
}