Android Room 的Kotlin实现

作者: Shanyaliux | 来源:发表于2020-03-02 18:48 被阅读0次

    原文链接https://www.shanya.world/archives/e6cb5eee.html

    Demo简介

    本Demo是演示Room在Kotlin语法下使用的一个简单的应用程序。

    Room介绍

    Room 持久性库在 SQLite 的基础上提供了一个抽象层,让用户能够在充分利用 SQLite 的强大功能的同时,获享更强健的数据库访问机制。

    该库可帮助您在运行应用的设备上创建本地数据库。

    步骤

    1、新建一个空的工程

    Android-Room-By-Kotlin2020-1-27-19-15-51

    2、更新Gradle文件

    必须将组件库添加到Gradle文件中
    在你的build.gradle(Module:app)中进行以下更改:
    通过将kapt 注释处理器 Kotlin插件添加到(Module:app)文件顶部定义的其他插件之后,来应用它。
    bulid.gradle

    apply plugin: 'kotlin-kapt'
    
    android {
        // 系统自动生成的在这里省略了……
    
        packagingOptions {
            exclude 'META-INF/atomicfu.kotlin_module'
        }
    }
    

    在代码dependencies块的末尾添加以下代码。

    // Room components
    implementation "androidx.room:room-runtime:$rootProject.roomVersion"
    implementation "androidx.room:room-ktx:$rootProject.roomVersion"
    kapt "androidx.room:room-compiler:$rootProject.roomVersion"
    androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion"
    
    // Lifecycle components
    implementation "androidx.lifecycle:lifecycle-extensions:$rootProject.archLifecycleVersion"
    kapt "androidx.lifecycle:lifecycle-compiler:$rootProject.archLifecycleVersion"
    androidTestImplementation "androidx.arch.core:core-testing:$rootProject.androidxArchVersion"
    
    // ViewModel Kotlin support
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$rootProject.archLifecycleVersion"
    
    // Coroutines
    api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$rootProject.coroutines"
    
    // UI
    implementation "com.google.android.material:material:$rootProject.materialVersion"
    
    // Testing
    androidTestImplementation "androidx.arch.core:core-testing:$rootProject.coreTestingVersion"
    

    之后再在 项目的build.gradle(Project)

    ext {
        roomVersion = '2.2.1'
        archLifecycleVersion = '2.2.0-rc02'
        androidxArchVersion = '2.1.0'
        coreTestingVersion = "2.1.0"
        coroutines = '1.3.2'
        materialVersion = "1.0.0"
    }
    

    这里也可以去官网找最新的版本号 链接

    3、创建一个实体

    Room允许您通过Entity创建表。让我们现在开始。

    @Entity(tableName = "todo_database")
    data class Todo (
        @PrimaryKey(autoGenerate = true) val id:Int,
        @ColumnInfo(name = "todo_title") val title:String,
        @ColumnInfo(name = "todo_content") val content:String
    
    )
    

    分析以下上述代码中@注释的意思

    • @Entity(tableName = "todo_database")
      每一个@Entity类代表一个SQLite表。注释你的类以表明它是一个实体类。如果希望表名与类名不同,则可以指定表名。这个表名为“todo_database”
    • @PrimaryKey
      每一个实体都需要一个主键。这里使用autoGenerate = true来自动生成
    • @ColumnInfo(name = "todo_title")
      如果您希望表中的列名称与成员变量的名称不同,则指定该名称。这将列命名为“todo_title”。

    4、创建Dao

    什么是Dao?

    在DAO(数据访问对象)中,指定SQL查询并将它们与方法调用关联。编译器检查SQL并从便利注释中生成常见查询(例如)的查询@Insert。Room使用DAO为您的代码创建一个干净的API。

    DAO必须是接口或抽象类。

    默认情况下,所有查询必须在单独的线程上执行。

    Room具有协程支持,允许您的查询使用suspend修饰符注释,然后从协程或另一个暂停函数调用。

    编写Dao

    让我们编写一个DAO,它提供以下查询:

    1. 按字母顺序排列所有标题
    2. 插入一个Todo
    3. 删除所有Todo
    @Dao
    interface TodoDao {
        @Query("SELECT * from todo_database ORDER BY todo_title ASC")
        fun getAlphabetizedTodoList(): LiveData<List<Todo>>
    
        @Insert(onConflict = OnConflictStrategy.IGNORE)
        suspend fun insert(todo: Todo)
    
        @Query("DELETE FROM todo_database")
        suspend fun deleteAll()
    }
    

    让我们来看一下:

    1. TodoDao是一个接口;Dao必须是接口或者抽象类。
    2. @Dao注解表示它是一个Room的Dao类
    3. suspend fun insert(todo: Todo)声明一个暂停功能以插入一个Todo。
    4. 该@Insert注释是一种特殊的DAO方法注释,您无需提供任何SQL!(还有@Delete和@Update注释,用于删除和更新行,但未在此应用中使用它们。)
    5. onConflict = OnConflictStrategy.IGNORE如果冲突策略中选定的Todo与列表中已有的Todo完全相同,则会忽略该单词。
    6. suspend fun deleteAll()声明一个暂停功能以删除所有Todo。
    • 关于@Query的用法有点多且杂查询请自行查阅资料

    5、LiveData类

    数据更改时,通常需要采取一些措施,例如在UI中显示更新的数据。这意味着您必须观察数据,以便在数据更改时可以做出反应。

    根据数据的存储方式,这可能很棘手。观察应用程序多个组件之间的数据更改可以在组件之间创建明确的,严格的依赖路径。这使测试和调试变得非常困难。

    LiveData,用于数据观察的生命周期库类可解决此问题。LiveData在方法描述中使用类型的返回值,然后Room会生成所有必要的代码来更新LiveData数据库。
    上面的代码块中已有体现,如下

    @Query("SELECT * from todo_database ORDER BY todo_title ASC")
        fun getAlphabetizedTodoList(): LiveData<List<Todo>>
    

    在本Demo的后面,将通过Observer 跟踪数据更改。

    7、添加RoomDatabase

    什么是RoomDatabase?

    • Room是SQLite数据库之上的数据库层。
    • Room负责处理以前使用NET处理的普通任务
    • Room使用DAO向其数据库发出查询。
    • 默认情况下,为避免UI性能下降,Room不允许您在主线程上发出查询。当Room查询返回LiveData时,查询将自动在后台线程上异步运行。
    • Room提供了SQLite语句的编译时检查。

    编写RomDatabase

    RoomDatabase类必须是抽象的并且可以扩展RoomDatabase。通常,整个应用程序只需要一个Room数据库实例。

    让我们现在做一个。创建一个名为的Kotlin类文件,WordRoomDatabase并添加以下代码:

    @Database(entities = arrayOf(Todo::class),version = 1,exportSchema = false)
    abstract class TodoDatabase: RoomDatabase() {
    
        abstract fun todoDao(): TodoDao
    
        private class TodoDatabaseCallback(
            private val scope: CoroutineScope
        ) : RoomDatabase.Callback() {
    
            override fun onCreate(db: SupportSQLiteDatabase) {
                super.onOpen(db)
                INSTANCE?.let { database ->
                    scope.launch {
                        var todoDao = database.todoDao()
    
                        // Delete all content here.
                        todoDao.deleteAll()
    
                        // Add sample todos.
                        var todo = Todo(0,"title","content")
                        todoDao.insert(todo)
                        todo = Todo(0,"title1","content1")
                        todoDao.insert(todo)
    
                        // TODO: Add your own words!
                        todo = Todo(0,"title2","content2")
                        todoDao.insert(todo)
                    }
                }
            }
        }
    
        companion object{
            @Volatile
            private var INSTANCE: TodoDatabase? = null
    
            fun getDatabase(context: Context,scope: CoroutineScope): TodoDatabase{
                val tempInstance = INSTANCE
                if (tempInstance != null) {
                    return tempInstance
                }
                synchronized(this) {
                    val instance = Room.databaseBuilder(
                        context.applicationContext,
                        TodoDatabase::class.java,
                        "todo_database"
                    ).addCallback(TodoDatabaseCallback(scope)).build()
                    INSTANCE = instance
                    return instance
                }
            }
        }
    }
    

    让我们来看一下代码:

    • Room的数据库类必须是abstract并且扩展RoomDatabase
    • 您使用注释该类为Room数据库,@Database并使用注释参数声明属于数据库的实体并设置版本号。每个实体对应一个将在数据库中创建的表。数据库迁移不在此代码实验室的范围内,因此exportSchema在此处设置为false以避免生成警告。在实际的应用程序中,您应该考虑为Room设置目录以用于导出架构,以便可以将当前架构签入版本控制系统。
    • 您通过为每个@Dao创建一个抽象的“getter”方法来使数据库提供其DAO。
    • 我们定义了singletonTodoRoomDatabase,以防止同时打开数据库的多个实例。
    • getDatabase返回单例。它将在首次访问数据库时使用Room的数据库构建器RoomDatabase在类的应用程序上下文中创建一个对象TodoRoomDatabase并将其命名,从而创建数据库"todo_database"。

    8、创建Repository

    什么是Repository?

    存储库类抽象了对多个数据源的访问。该存储库不是体系结构组件库的一部分,而是建议的代码分离和体系结构最佳实践。Repository类提供了一个干净的API,用于对应用程序其余部分的数据访问。

    Android-Room-By-Kotlin2020-1-28-9-8-56

    编写Repository

    创建一个名为的Kotlin类文件TodoRepository,并将以下代码粘贴到其中:

    class TodoRepository(private val todoDao: TodoDao) {
    
        val allTodo: LiveData<List<Todo>> = todoDao.getAlphabetizedTodoList()
    
        suspend fun insert(todo: Todo){
            todoDao.insert(todo)
        }
    }
    

    主要代码:

    1. DAO被传递到存储库构造函数,而不是整个数据库。这是因为它只需要访问DAO,因为DAO包含数据库的所有读/写方法。无需将整个数据库公开到存储库。
    2. Todo列表是公共财产。通过LiveDataRoom获取单词列表进行初始化;之所以可以这样做,是因为我们定义了getAlphabetizedTodoList返回LiveData的方法。Room在单独的线程上执行所有查询。然后,当LiveData数据已更改时,observed 将在主线程上通知观察者。
    3. suspend修饰符告诉编译器,这需要从协同程序或其他暂停功能调用。

    9、创建ViewModel

    什么是VIewModel?

    ViewModel的作用是提供数据的UI和生存的配置更改。ViewModel充当存储库和UI之间的通信中心。您还可以使用ViewModel在片段之间共享数据。ViewModel是生命周期库的一部分。

    为什么要使用ViewModel

    ViewModel以对生命周期敏感的方式保存应用程序的UI数据,以在配置更改后生存下来。将应用程序的UI数据Activity类与Fragment类分开,可以更好地遵循单一职责原则:您的活动和片段负责将数据绘制到屏幕上,而您则ViewModel可以负责保存和处理UI所需的所有数据。
    在中ViewModelLiveData用于UI将使用或显示的可变数据。使用LiveData有几个好处:

    1. 您可以将观察者放在数据上(而不是轮询更改),并且仅
      在数据实际更改时才更新UI。
    2. 资源库和用户界面由完全分隔.
    3. 没有来自的数据库调用ViewModel(这全部在存储库中处理),使代码更具可测试性。

    viewModelScope

    在Kotlin,所有协程都在内运行CoroutineScope。示波器通过其工作控制协程的生命周期。取消合并范围的作业时,它将取消在该合并范围内启动的所有协程。

    AndroidX lifecycle-viewmodel-ktx库添加了viewModelScope类的扩展功能ViewModel,使您能够使用范围。

    实现ViewModel

    为此创建一个Kotlin类文件,并添加以下代码:

    class TodoViewModel(application: Application): AndroidViewModel(application) {
    
        private val repository: TodoRepository
        val allTodo: LiveData<List<Todo>>
    
        init {
            val wordsDao = TodoDatabase.getDatabase(application,viewModelScope).todoDao()
            repository = TodoRepository(wordsDao)
            allTodo = repository.allTodo
        }
    
        fun insert(todo: Todo) = viewModelScope.launch {
            repository.insert(todo)
        }
    }
    

    10、最后

    现在我们只要调用ViewModel里面的inset方法即可插入数据了。

    具体的测试界面(RecyclerView等)代码见下方Github连接。

    Github仓库

    相关文章

      网友评论

        本文标题:Android Room 的Kotlin实现

        本文链接:https://www.haomeiwen.com/subject/ykjbkhtx.html