一、什么是Room
1.1 Room简介
Room是Android Jetpack组件库中的一部分,对SQLite进行了封装,简化了对SQLite数据库的操作,让开发者使用面向对象的方式(通过Java/Kotlin)来操作SQLite数据库,从而避免了编写大量繁琐的SQL代码和解析数据。
1.2 三大核心组件
- Entity(实体类):用于表示数据库表的数据结构。
- DAO(Data Access Object):用于定义数据库操作的方法。
- Database(数据库类):用于创建数据库实例,并将 Entity 和 DAO 关联起来
二、集成Room
在app下的build.gradle下添加:
Kotlin
//数据库创建成功,会有类似以下目录,以自己的为准\build\generated\source\kapt\release\com\xxx\xxx\drive\database\AppDatabase_Impl
def room_version = "2.5.1"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
//这里注意:如果是某一个moudle中要使用room数据库,建议直接将库集成到moudle中的build.gradle中,博主这里遇到将root相关库集成到基类的build.gradle中,一直报错,提示生成数据库失败,上面的AppDatabase_Impl文件无法生成
三、使用示例
3.1 创建实体类 (Entity)
Kotlin
/**
* @Description : LocalUploadRecord 数据库实体类,根据项目需求,定义自己的字段即可
*/
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "upload_records")
data class LocalUploadRecord(
@PrimaryKey(autoGenerate = true)
val id: Int? = null,
@ColumnInfo(name = "phone_id")
val phoneId: String,//云机ID
@ColumnInfo(name = "phone_biz_id")
val phoneBizId: String,//云机业务ID
@ColumnInfo(name = "user_id", index = true)
val userId: String,//登录返回的用户id
@ColumnInfo(name = "file_name")
val fileName: String,//文件名(含后缀)
@ColumnInfo(name = "file_path")
val filePath: String,//文件路径
@ColumnInfo(name = "phone_name")
val phoneName: String,//云机名称
@ColumnInfo(name = "file_status")
var fileStatus: FileStatus,//WAITING, UPLOADING, SUCCESS, FAILED,INSTALLING,INSTALL_FAIL,INSTALL_SUCCESS
@ColumnInfo(name = "file_time")
val fileTime: Long,//文件操作时间-时间戳:毫秒
@ColumnInfo(name = "file_size")
val fileSize: Long = 0,//文件大小,字节
@ColumnInfo(name = "err_message")
var errMessage: String = "",
@ColumnInfo(name = "reserved_field1")
var reservedField1: String = "",//预留字段1
@ColumnInfo(name = "reserved_field2")
var reservedField2: String = "",//预留字段2
@ColumnInfo(name = "reserved_field3")
var reservedField3: String = "",//预留字段3
)
各注解作用:
- @Entity(tableName = "user-room"):标记这是一个数据库表,同时指定表名
- @PrimaryKey(autoGenerate = true):设置主键并启用自动生成
- @ColumnInfo(name = "user_age"):设置列名
3.2 创建数据访问对象(DAO)
创建一个接口定义数据库操作: @Dao
Kotlin
/**
* @Description : UploadRecordDao 创建数据访问对象(DAO)
* @Dao:标记这是一个数据访问对象接口
* @Insert、@Update、@Delete:由Room 提供的便捷注解,可以自动生成对应 SQL
* @Query:自定义 SQL 查询,:参数名表示方法参数
*/
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import kotlinx.coroutines.flow.Flow
@Dao
interface UploadRecordDao {
/**
* 插入一条数据
*/
@Insert
fun insert(record: LocalUploadRecord): Long
/**
* 删除最老的数据(按时间戳升序)
*/
@Query("DELETE FROM upload_records WHERE id IN (SELECT id FROM upload_records WHERE user_id = :userId ORDER BY file_time ASC LIMIT :count)")
fun deleteOldest(userId: String,count:Int)
/**
* 删除一条记录
*/
@Delete
fun delete(record: LocalUploadRecord)
/**
* 更新一条记录
*/
@Update
fun update(record: LocalUploadRecord)
/**
* 根据userId获取所有记录
*/
@Query("SELECT * FROM upload_records WHERE user_id = :userId ORDER BY file_time DESC")
fun getRecordsByUser(userId: String): Flow<List<LocalUploadRecord>>
/**
* 根据userId和FileStatus获取记录
*/
@Query("SELECT * FROM upload_records WHERE user_id = :userId AND file_status IN (:statusList) ORDER BY file_time DESC")
fun getRecordsByUser(userId: String,statusList: List<FileStatus>):List<LocalUploadRecord>
/**
* 分页获取记录
*/
@Query("SELECT * FROM upload_records WHERE user_id = :userId ORDER BY file_time DESC LIMIT :pageSize OFFSET :offset")
fun getRecordsByUserPaged(userId: String, pageSize: Int, offset: Int): List<LocalUploadRecord>
/**
* 根据userId查询记录数
*/
@Query("SELECT COUNT(*) FROM upload_records WHERE user_id = :userId")
fun getRecordCountByUser(userId: String): Int
/**
* 根据id查询一条记录
*/
@Query("SELECT * FROM upload_records WHERE id = :id")
// suspend fun getRecordById(id: Long): UploadRecord 低版本在Dao中不能加suspend关键字,会报错,2.6.1中不会
fun getRecordById(id: Long): LocalUploadRecord
/**
* 删除用户的所有数据
*/
@Query("DELETE FROM upload_records WHERE user_id = :userId")
fun deleteAllByUserId(userId: String): Int
/**
* 根据id 批量删除数据
*/
@Query("DELETE FROM upload_records WHERE id IN (:ids)")
fun deleteByIds(ids: List<Long>): Int
}
各注解作用:
- @Dao:标记这是一个数据访问对象接口
- @Insert、@Update、@Delete:由Room 提供的便捷注解,可以自动生成对应 SQL
- @Query:自定义 SQL 查询,:参数名表示方法参数
3.3 创建数据库类
创建一个抽象类用于扩展 RoomDatabase:
Kotlin
/**
* @Description : UploadDatabase 创建数据库类
* @Database(entities = {MyUser.class}, version = 1):标记数据库类,指定包含的表和版本号
* Migration:定义数据库版本迁移逻辑
* 关于数据库迁移:
* 数据库迁移的核心作用是在修改数据库结构后(比如在实体类中添加新字段或是在库中添加新表),安全、无损地将旧版本数据库升级到新版本,同时保留用户的原有数据。
* 版本迁移逻辑:必须增加数据库的版本号,并提供一个Migration对象来告诉Room如何从旧版本正确迁移到新版本。
*/
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(
entities = [LocalUploadRecord::class],
version = 1,
exportSchema = false
)
abstract class UploadDatabase : RoomDatabase() {
abstract fun uploadRecordDao(): UploadRecordDao
companion object {
@Volatile
private var INSTANCE: UploadDatabase? = null
fun getInstance(context: Context): UploadDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
UploadDatabase::class.java,
"upload_database"
).build()
INSTANCE = instance
instance
}
}
}
}
各注解作用:
- @Database(entities = {MyUser.class}, version = 1):标记数据库类,指定包含的表和版本号
- Migration:定义数据库版本迁移逻辑
3.4 定义一个数据库管理类
Kotlin
import android.content.Context
import android.util.Log
import com.android.base.kits.KLog
import com.yixin.cloudphone.drive.data.bean.PagedResult
import com.yixin.cloudphone.drive.util.Constant
import kotlinx.coroutines.flow.Flow
/**
* @Description : UploadDatabaseManager
*/
class UploadDatabaseManager(private val context: Context) {
//每个用户最大存储上传记录个数
private val RECORD_LIMIT_COUNT=1000
private val database: UploadDatabase by lazy {
UploadDatabase.getInstance(context)
}
private val dao: UploadRecordDao by lazy {
database.uploadRecordDao()
}
/**
* 新增记录
* 每个用户id最多1000条数据,超过的要删除掉
*/
fun insertUploadRecord(record: LocalUploadRecord): Long {
val id=dao.insert(record)
val count=dao.getRecordCountByUser(record.userId)
if (count>RECORD_LIMIT_COUNT){
// 删除超过1000条的最老记录
val deleteCount=count-RECORD_LIMIT_COUNT
if(deleteCount>0){
dao.deleteOldest(record.userId,deleteCount)
KLog.d(Constant.TAG,"insertUploadRecord deleteOldest,sumSize:$count, deleteCount:$deleteCount")
}
}
return id
}
//更新上传记录
fun updateUploadRecord(record: LocalUploadRecord) {
dao.update(record)
}
// 获取用户的所有上传记录(Flow版本,用于观察数据变化)
fun getUploadRecordsByUser(userId: String): Flow<List<LocalUploadRecord>> {
return dao.getRecordsByUser(userId)
}
// 根据userId和FileStatus获取记录
fun getUploadRecordsByUser(userId: String,statusList: List<FileStatus>): List<LocalUploadRecord>{
return dao.getRecordsByUser(userId,statusList)
}
// 分页获取用户的上传记录
suspend fun getUploadRecordsPaged(
userId: String,
page: Int,
pageSize: Int = 20
): PagedResult<LocalUploadRecord> {
val offset = (page - 1) * pageSize
val records = dao.getRecordsByUserPaged(userId, pageSize, offset)
val totalCount = dao.getRecordCountByUser(userId)
val totalPages = (totalCount + pageSize - 1) / pageSize
return PagedResult(
data = records,
currentPage = page,
pageSize = pageSize,
totalCount = totalCount,
totalPages = totalPages
)
}
// 根据ID获取记录
fun getRecordById(id: Long): LocalUploadRecord? {
return dao.getRecordById(id)
}
}
3.5 使用数据库
Kotlin
//在Activity或viewmoudle中使用数据库
private val roomManager: UploadDatabaseManager by lazy {
UploadDatabaseManager(VCApplication.getInstance())
}
//查询数据库数据,使用时应该在线程中调用,数据库操作属于耗时操作
viewModelScope.launch(Dispatchers.IO) {
val fileList=roomManager.getUploadRecordsByUser(userId)
}
这里重点说一下,Flow中的很好用,可以看下博主示例Dao中这里的Flow<List<LocalUploadRecord>>,这样子定义后,如果数据库有更新,会自动收到通知,不用自己主动查询。