Android Jetpack - Room

作者: SkyRiN | 来源:发表于2019-07-01 10:22 被阅读0次

    Room 简介

    Room 持久化库提供了一个基于 SQLite 的抽象层,以便在利用 SQLite 的全部功能的同时实现更强大的数据库访问

    Room 库帮你的 App 在设备上创建一个缓存,并作为此 App 的单一事实来源,允许用户在应用内查看关键信息的一致副本,无论用户是否具有互联网连接

    处理重要数量的结构化数据的应用程序可以从本地保存数据中受益匪浅。最常见的用例是缓存相关的数据。这样,当设备无法访问网络时,用户仍然可以在离线时浏览该内容。然后,在设备重新联机后,任何用户启动的内容更改都会同步到服务器

    Room 的三个主要部分包括

    • Database

      包含数据库持有者,并作为应用程序的持久关系数据的基础连接的主要访问点。

      使用@Database注释的类应满足以下条件:

      • 继承了 RoomDatabase 的抽象类
      • 在注解中包含与数据库相关联的实体类列表
      • 包含一个无参抽象方法,并返回使用 @Dao 注释的类。 在运行时,你可以通过调用 Room.databaseBuilder()Room.inMemoryDatabaseBuilder()来获取 Database 实例
    • Entity

      数据库中的表

    • DAO

      包含用于访问数据库的方法

    该应用程序使用 Room 数据库来获取与该数据库关联的数据访问对象或 DAO。然后,应用程序使用每个 DAO 从数据库中获取实体,并将对这些实体的任何更改保存回数据库。最后,应用程序使用实体来获取和设置与数据库中的表列对应的值

    Room 组件关系图

    Repository 简介

    什么是 Repository ?

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

    img
    为什么使用 Repository ?

    Repository 管理查询并允许您使用多个后端。在最常见的示例中,Repository 实现了用于决定是从网络获取数据还是使用在本地数据库中缓存的结果的逻辑,既避免了 ViewModel 和数据的直接交互又统一了单一真实数据源的逻辑

    Repository 在 MVVM 架构中的位置

    使用 Room 组件

    本示例我会参照 CodeLabs 来做一个 WordList,我会精简一下流程,完整代码示例在文末

    1、创建 Project

    2、添加依赖

    app 的 build.gradle 添加依赖

    apply plugin: 'kotlin-kapt'
    ...
    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
        implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$rootProject.coroutines"
        implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$rootProject.coroutines"
        // Material
        implementation 'androidx.recyclerview:recyclerview:1.0.0'
        implementation 'com.google.android.material:material:1.0.0'
    }
    

    Project 的 build.gradle 中追加版本号

    ext {
        roomVersion = '2.2.0-alpha01'
        archLifecycleVersion = '2.2.0-alpha02'
        androidxArchVersion = '2.0.0'
        coroutines = '1.2.1'
    }
    

    Project 的 gradle.properties 中追加如下内容,转换为 AndroidX 项目

    android.enableJetifier=true
    android.useAndroidX=true
    
    3、创建 Entity、DAO、Database

    Entity

    在 Room 中,每个被 @Entity 标注的 data class 都被视为 Database 中的一张表

    @Entity(tableName = "word_table")
    data class Word(@PrimaryKey @ColumnInfo(name = "c_word") val word: String)
    

    为了使 Demo 尽可能的简单,该 Entity 只有一个参数,并被 @PrimaryKey 注释作为主键,此处 @ColumnInfo 的作用是给此参数取一个别名 c_word ,该别名会被真实的记录于数据库的字段中,如果在同一张表中存在多个可能重复的字段时例如 BookA().wordBookB().word ,就可以使用 @ColumnInfo(name = "a_word")@ColumnInfo(name = "b_word") 的方式来区分,如果你想直接使用参数名作为字段名就不用加此注解

    @Entity(tableName = "word_table")
    data class Word(@PrimaryKey(autoGenerate = true) val id: Int,
                    val word: String)
    

    关于 Entity 详细解释见 Entity DOC

    DAO

    为了避免 UI 阻塞,一些比较耗时的操作如 insert ,可以使用 suspend 关键字修饰,然后利用协程在非 UI 线程执行此方法

    @Dao
    interface WordDao {
        @Insert
        suspend fun insert(word: Word)
        @Query("DELETE FROM word_table")
        suspend fun deleteAll()
        @Delete
        suspend fun delete(word: Word)
        @Query("SELECT * from word_table ORDER BY word ASC")
        fun getAllWords() : LiveData<List<Word>>
    }
    

    关于 DAO ,Room 为我们提供了四种注解:@Insert @Delete@Update@Query ,其中只有 Query 的参数是 SQL 语句,它支持 SELECT、INSERT、UPDATE 、DELETE 四种语句,所以如果你愿意多写几句 SQL 的话,理论上你的 DAO 中完全可以只有 Query

    Database

    通过单例模式提供全局唯一的 WordRoomDatabase 实例,通过 addCallback 方法添加可以在数据库启动阶段的回调,在 onOpenonCreate 阶段处理一些初始化操作,比如填充默认数据等

    @Database(entities = [Word::class], version = 1,exportSchema = false)
    public abstract class WordRoomDatabase : RoomDatabase() {
        abstract fun wordDao(): WordDao
        companion object {
            @Volatile
            private var INSTANCE: WordRoomDatabase? = null
            fun getDatabase(context: Context, scope: CoroutineScope): WordRoomDatab
                val tempInstance = INSTANCE
                if (tempInstance != null) {
                    return tempInstance
                }
                synchronized(this) {
                    val instance = Room.databaseBuilder(
                        context.applicationContext,
                        WordRoomDatabase::class.java,
                        "Word_database"
                    ).addCallback(WordDatabaseCallback(scope))
                     .build()
                    INSTANCE = instance
                    return instance
                }
            }
        }
        private class WordDatabaseCallback(
            private val scope: CoroutineScope
        ) : RoomDatabase.Callback() {
            override fun onOpen(db: SupportSQLiteDatabase) {
                super.onOpen(db)
                INSTANCE?.let { database ->
                    scope.launch(Dispatchers.IO) {
                        populateDatabase(database.wordDao())
                    }
                }
            }
            suspend fun populateDatabase(wordDao: WordDao) {
                // when you want to execute something on database open
                            // wordDao.deleteAll()
                            
                            // var word = Word("Hello")
                            // wordDao.insert(word)
                            // word = Word("World!")
                            // wordDao.insert(word)
            }
        }
    }
    
    4、创建 Repository

    Repository 作为 ViewModel 与数据操作的中间层,避免了 ViewModel 与数据的直接交互,即方便了 ViewModel 的测试,又能在 Repository 中实现单一真实数据源策略,从而使 ViewModel 更加关注于业务层逻辑

    class WordRepository (private val wordDao: WordDao){
        val allWords: LiveData<List<Word>> = wordDao.getAllWords()
        @WorkerThread
        suspend fun insert(word: Word){
            wordDao.insert(word)
        }
        @WorkerThread
        suspend fun delete(word: Word){
            wordDao.delete(word)
        }
    }
    
    5、创建 ViewModel

    由于初始化 Database 需要用到 Context ,所以此 ViewModel 继承了包含 Application 的 AndroidViewModel。因为要从主线程调用 insert、delete 等方法,所以此处启动了协程来执行这些方法进行数据库操作,并使用 IO Dispatchers

    class WordViewModel (application: Application) : AndroidViewModel(Application()){
        private val repository: WordRepository
        val allWords: LiveData<List<Word>>
        init {
            val wordsDao = WordRoomDatabase.getDatabase(application,viewModelScope).wordDao()
            repository = WordRepository(wordsDao)
            allWords = repository.allWords
        }
        fun insert(word: Word) = viewModelScope.launch (Dispatchers.IO){
            repository.insert(word)
        }
        fun delete(word: Word) = viewModelScope.launch (Dispatchers.IO){
            repository.delete(word)
        }
    }
    
    6、在 MainActivity 中注册 Observer
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
      
        ...
      
        viewModel.allWords.observe(this, Observer {
            adapter.setWords(it)
        })
    }
    

    allWords 是一个 LiveData 类型,经过注册之后,无论是修改或者删除,都会立刻被 Observer 感知并通知 UI 更新列表

    完整示例代码

    https://github.com/realskyrin/jetpack_room

    参考

    https://developer.android.com/topic/libraries/architecture/room

    https://github.com/googlesamples/android-sunflower

    https://medium.com/androiddevelopers/introducing-android-sunflower-e421b43fe0c2

    https://codelabs.developers.google.com/codelabs/android-room-with-a-view-kotlin

    https://developer.android.com/reference/android/arch/persistence/room/OnConflictStrategy

    Jetpack 系列文章

    Android Jetpack - Lifecycles

    Android Jetpack - ViewModel

    Android Jetpack - DataBinding

    Android Jetpack - Room

    Android Jetpack - LiveData(待更)

    Android Jetpack - WorkManager(待更)

    Android Jetpack - Paging(待更)

    相关文章

      网友评论

        本文标题:Android Jetpack - Room

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