Android开发Android开发经验谈kotlin

Android Room 入坑详解

2022-04-26  本文已影响0人  枫未晚

Room 持久性库在 SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库。具体来说,Room 具有以下优势:

主要组件

Room 包含三个主要组件:

数据库类为应用提供与该数据库关联的 DAO 的实例。反过来,应用可以使用 DAO 从数据库中检索数据,作为关联的数据实体对象的实例。此外,应用还可以使用定义的数据实体更新相应表中的行,或者创建新行供插入。


Room 的不同组件之间的关系

引入Room依赖

首先在build.gradle导入依赖,Kotlin和RxJava可以按需导入,注释处理工具选一个即可。

def room_version = "2.4.2"
implementation"androidx.room:room-runtime:$room_version"
// 注释处理工具
annotationProcessor "androidx.room:room-compiler:$room_version"
// Kotlin注释处理工具(kapt)
kapt"androidx.room:room-compiler:$room_version"

// kotlin扩展和协同程序对Room的支持
implementation "androidx.room:room-ktx:$room_version"
// RxJava2
implementation "androidx.room:room-rxjava2:$room_version"
// RxJava3
implementation "androidx.room:room-rxjava3:$room_version"

如果使用Kotlin注释处理工具(kapt),还需要在build.gradle文件顶部添加下方定义。

apply plugin: 'kotlin-kapt'

android 块中添加 packagingOptions 块,以从软件包中排除原子函数模块并防止出现警告。

android {
    // other configuration (buildTypes, defaultConfig, etc.)

    packagingOptions {
        exclude 'META-INF/atomicfu.kotlin_module'
    }

    // 要使用的一些 API 需要 1.8 `jvmTarget`
    kotlinOptions {
        jvmTarget = "1.8"
    }

}

创建实体

Room 允许通过实体创建表,以下代码定义了一个 User 数据实体。User 的每个实例都代表应用数据库中 user 表中的一行。

@Entity
data class User(
    @PrimaryKey val uid: Int,
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?,
    @Ignore val picture: Bitmap?
)

创建DAO

在 DAO(数据访问对象)中,可以指定 SQL 查询并将其与方法调用相关联,DAO 必须是一个接口或抽象类。默认情况下,所有语句都必须在单独的线程上执行。Room 支持Kotlin 协程,可使用 suspend 修饰符对查询进行注解,然后从协程或其他挂起函数对其进行调用。

以下代码定义了一个名为 UserDao 的 DAO。UserDao 提供了应用的其余部分用于与 user 表中的数据交互的方法。

@Dao
interface UserDao {
    @Query("SELECT * FROM user")
    fun getAll(): List<User>

    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    fun loadAllByIds(userIds: IntArray): List<User>

    @Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
           "last_name LIKE :last ORDER BY last_name DESC LIMIT 1")
    fun findByName(first: String, last: String): User

    // onConflict 配置主键冲突处理
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertUsers(vararg users: User)

    @Insert
    fun insertBothUsers(user1: User, user2: User)

    @Insert
    fun insertUsersAndFriends(user: User, friends: List<User>)

    @Delete
    fun delete(user: User)
}
interface BaseDAO<T> {

    @Insert
    suspend fun insert(obj: T)

    @Insert
    suspend fun insert(list: List<T>)

    @Update
    suspend fun update(obj: T)

    @Update
    suspend fun update(list: List<T>)

    @Delete
    suspend fun delete(obj: T)

    @Delete
    suspend fun delete(list: List<T>)
}

除了直接在DAO中写方法,还可以创建DAO的基类,把基础方法提取出来,减少重复代码。

配置数据库

Room 数据库类必须是抽象且必须扩展 RoomDatabase。整个应用通常只需要一个 Room 数据库实例。数据库类必须满足以下条件:

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

   abstract fun userDao(): UserDao

   companion object {
        // 防止同一时间创建多个实例
        @Volatile
        private var INSTANCE: AppDatabase ? = null

        fun getDatabase(context: Context): AppDatabase {
            // 单例
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                        // 可以使用单例Application来代替此参数传递
                        context.applicationContext,
                        AppDatabase::class.java,
                        "database_name"
                    ).build()
                INSTANCE = instance

                instance
            }
        }
   }
}

现在数据库已经可以正常使用了,可以通过以下方式操作数据库:

AppDatabase.getDatabase(context).userDao().insertUsers(user)

保存Date等复杂类型数据

Date等类型字段,Room是不知道怎么存的,需要通过转换器来保存和读取。

class Converters {
    @TypeConverter
    fun fromTimestamp(value: Long?): Date? {
        return value?.let { Date(it) }
    }

    @TypeConverter
    fun dateToTimestamp(date: Date?): Long? {
        return date?.time?.toLong()
    }
}

在数据库类配置注解。

@Database(entities = [User::class], version = 1, exportSchema = false)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase()

接下来直接在实体声明Date类型的参数即可,当写的时候会调用dateToTimestamp,读的时候会调用fromTimestamp。

上一篇下一篇

猜你喜欢

热点阅读