Loading

重新学习Android——(六)Room官方示例

本篇笔记记录的是Android的官方Codelab——带 View 的 Android Room - Kotlin中的内容。

Room

Room是一个安卓平台的ORM框架,它在Sqlite上提供了一个抽象层,让你不必再繁琐的使用SqliteOpenHelper进行操作。

概念

Room中提供如下概念

  1. 数据库:Sqlite中的一个数据库,在Room中使用@Database注解标注。一般一个应用程序只建立一个数据库,并且这个数据库对象在程序中全局单例。
  2. 实体:实体即数据库中的一个表,在Room中使用@Entity注解标注,在Kotlin代码中表现为一个数据类,Room会自动完成数据库表到数据类之间的转换。
  3. DAO:数据访问对象,用于操作数据库完成增删改查操作,在Room中使用@Dao注解标注

依赖

dependencies {
    def roomVersion = "2.4.0"
    def lifecycleVersion = "2.4.0"
    def coroutineVersion = "1.6.0"

    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.0'
    // Activity
    implementation 'androidx.activity:activity-ktx:1.4.0'
    // Room
    implementation "androidx.room:room-ktx:$roomVersion"
    kapt "androidx.room:room-compiler:$roomVersion"
    androidTestImplementation "androidx.room:room-testing:$roomVersion"

    // Lifecycle
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"

    // Kotlin components
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion"


    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

同时启用kapt

plugins {
    // other plugins ...
    id 'kotlin-kapt'
}

创建实体

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

创建DAO

@Dao
interface WordDao {

    @Query("SELECT * FROM word_table")
    fun getAlphabetizedWords(): Flow<List<Word>>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(word: Word)

    @Query("DELETE FROM word_table")
    suspend fun deleteAll()

}

Dao是抽象类或接口,同时它的方法可以被声明成suspend,之后可以在协程中调用

创建数据库

@Database(entities = arrayOf(Word::class), version = 1, exportSchema = false)
abstract class WordRoomDatabase : RoomDatabase() {

    // 该类只是为了测试使用,在每次数据库实例创建后删除word表中所有数据,添加两条测试数据
    private class WordDataBaseCallback(
        private val scope: CoroutineScope
    ) : RoomDatabase.Callback() {
        override fun onCreate(db: SupportSQLiteDatabase) {
            super.onCreate(db)
            INSTANCE?.let { db ->
                scope.launch {
                    var wordDao = db.wordDao()
                    wordDao.deleteAll()
                    wordDao.insert(Word("Hello"))
                    wordDao.insert(Word("Word"))
                }
            }
        }
    }

    // 这个dao实现类应该是会在编译时自动创建??官方教程里也没写,就一下带过了。
    // 反正,和大部分ORM框架一样,这里就是一些魔法,Dao实现类会自动创建
    abstract fun wordDao(): WordDao

    companion object {
        private var INSTANCE: WordRoomDatabase? = null

        fun getDatabase(context: Context, scope: CoroutineScope): WordRoomDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    WordRoomDatabase::class.java,
                    "word_database"
                ).addCallback(WordDataBaseCallback(scope)).build()

                INSTANCE = instance
                instance
            }
        }

    }
}

Repository

根据应用架构指南,我们抽象出一个Repository层,这个Repository依赖wordDao,这个wordDao会在外部被注入

class WordRepository(private val wordDao: WordDao) {
    val allWords: Flow<List<Word>> = wordDao.getAlphabetizedWords()

    @Suppress("RedundantSuspendModifier")
    @WorkerThread
    suspend fun insert(word: Word) {
        wordDao.insert(word)
    }

}

Application

创建Application协程作用域,创建database实例和repository实例。

我们希望数据库实例和respository实例跟随应用的作用域,所以我们在这里创建它们

class WordApplication : Application() {
    val applicationScope = CoroutineScope(SupervisorJob())

    val database by lazy { WordRoomDatabase.getDatabase(this, applicationScope) }
    val repository by lazy { WordRepository(database.wordDao()) }

}

MainActivity

这里只有和viewModel交互相关的代码,完整代码可以去看官方Codelab

private val wordViewModel: WordViewModel by viewModels {
    WordViewModelFactory((application as WordApplication).repository)
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    wordViewModel.allWords.observe(this) {
        adapter.submitList(it)
    }
}

下面是利用从另一个Activity返回的信息来插入单词

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    if (resultCode == RESULT_OK && requestCode == newWordActivityRequestCode) {
        data?.getStringExtra(NewWordActivity?.EXTRA_REPLY)?.let {
            wordViewModel.insert(Word(it))
        }
    } else {
        Toast.makeText(this, R.string.empty_not_saved, Toast.LENGTH_SHORT).show()
    }
}
posted @ 2022-01-10 17:07  yudoge  阅读(307)  评论(0编辑  收藏  举报