Jetpack Room 的简单使用

示例:通过本地数据库,实现AutoCompleteTextView 的提示人名的数据填充

参考 : AndroidRoomWithAView

1.介绍

使用Google的推荐架构


架构

2.配置Gradle 文件

1.在build.gradle(Module:app)添加kapt插件

apply plugin: 'kotlin-kapt'

2.在该packagingOptions块内添加该块,android以将原子函数模块从包中排除,并防止出现警告

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

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

3.在代码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"

4.在build.gradle(Project:RoomWordsSample)文件中,将版本号添加到文件的末尾,如下面的代码所示

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.创建实体类

创建一个新的Kotlin类文件:Name数据类。
此类创建表SQLite表。类中的每个公共属性都代表表中的一列。
Room最终将使用这些属性来创建表并从数据库中的行实例化对象。

为了使Name类对Room数据库有意义,您需要对其进行注释。
注释标识此类的每个部分如何与数据库中的条目相关。Room使用此信息来生成代码。

@Entity(tableName = "name_table")
class Name (@PrimaryKey @ColumnInfo(name = "name") val name:String)

让我们看看这些注释的作用:

@Entity(tableName = "word_table")
每个@Entity类代表一个SQLite表。注释您的类声明以表明它是一个实体。如果希望表名与类名不同,则可以指定表名。这将表命名为“ word_table”。

@PrimaryKey
每个实体都需要一个主键。为了简单起见,每个名字都充当其自己的主键。

@ColumnInfo(name = "name")
如果您希望表中的列名称与成员变量的名称不同,则指定该名称。这将列命名为“name”。

数据库中存储的每个属性都必须具有公共可见性,这是​​Kotlin的默认设置。

4.创建DAO(数据访问对象)

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

DAO必须是接口或抽象类。

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

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

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

  • 1.按字母顺序排列所有名字

  • 2.插入一个名字

  • 3.删除所有名字

@Dao
interface NameDao {

    @Query("SELECT * from name_table ORDER BY name ASC")
    fun getAllNames(): List<Name>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(name: Name)

    @Query("DELETE FROM name_table")
    suspend fun deleteAll()
}

让我们看看这些注释的作用:

  • 1.NameDao是一个接口; DAO必须是接口或抽象类。
  • 2.@Dao注解将其标识为Room的DAO类。
  • 3.suspend fun insert(name: Name):声明一个suspend函数以插入一个名字。
  • 4.@Insert注解是一种特殊的DAO方法注解,您无需提供任何SQL。(还有@Delete和@Update注解用于删除和更新行,但您未在此应用程序中使用它们。)
  • 5.onConflict = OnConflictStrategy.IGNORE:如果所选的冲突策略与列表中的一个名字完全相同,则忽略该单词。要了解有关可用冲突策略的更多信息,请查阅文档
  • 6.suspend fun deleteAll():声明一个suspend函数以删除所有名字。
  • 7.没有用于删除多个实体的便捷注释,因此使用通用@Query进行注释。
  • 8.@Query("SELECT * from name_table ORDER BY name ASC"):@Query要求您提供SQL查询作为注释的字符串参数,以允许进行复杂的读取查询和其他操作。
  • 9.fun getAllNames(): List<Name> 一种获取所有名字并使其返回名字列表的方法。

5.使用LiveData类

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

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

LiveData,用于数据观察的生命周期库类可解决此问题。LiveData在方法说明中使用类型的返回值,并且Room将生成所有必需的代码以在更新LiveData数据库时进行更新。

在中NameDao,更改getAllNames()方法,以便返回的List<Word>内容包含LiveData。

    @Query("SELECT * from name_table ORDER BY name ASC")
    fun getAllNames(): LiveData<List<Name>>

6.添加一个 Room database

创建数据库

@Database(entities = [Name::class], version = 1, exportSchema = false)
public abstract class NameRoomDatabase : RoomDatabase() {

    abstract fun nameDao(): NameDao

    companion object {

        @Volatile
        private var INSTANCE: NameRoomDatabase? = null

        fun getDatabase(context: Context): NameRoomDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    NameRoomDatabase::class.java,
                    "name_database"
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}

Room的数据库类必须为abstract并且扩展RoomDatabase

您使用注释该类为Room数据库,@Database并使用注释参数声明该数据库中的实体并设置版本号。
每个实体对应一个将在数据库中创建的表。数据库迁移不在此代码实验室的范围内,因此exportSchema在此处设置为false以避免生成警告。
在实际的应用程序中,您应考虑为Room设置目录以用于导出架构,以便可以将当前架构签入版本控制系统。

您通过为每个@Dao创建一个抽象的“ getter”方法来使数据库提供其DAO。

我们定义了singleton,NameRoomDatabase,以防止同时打开多个数据库实例。
getDatabase返回单例。它将在首次访问数据库时使用Room的数据库构建器RoomDatabase在类的应用程序上下文中创建一个对象NameRoomDatabase并将其命名,从而创建数据库"name_database"。

7.创建存储库

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

存储库管理查询,并允许您使用多个后端。在最常见的示例中,存储库实现了用于确定是从网络中获取数据还是使用本地数据库中缓存的结果的逻辑。

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

class NameRepository(private val nameDao: NameDao) {

    val allNames: LiveData<List<Name>> = nameDao.getAllNames()

    suspend fun insert(name: Name) {
        nameDao.insert(name)
    }
}

主要要点

  • 1.DAO被传递到存储库构造函数,而不是整个数据库。这是因为它只需要访问DAO,因为DAO包含数据库的所有读/写方法。无需将整个数据库公开到存储库。
  • 2.这个列表里的名字是公有属性。通过从Room获取名字的LiveData列表进行初始化;之所以可以这样做,是因为我们通过定义getAllNames方法以在“ LiveData类”步骤中返回LiveData。 Room在单独的线程上执行所有查询。 然后,观察到的LiveData将在数据更改时通知主线程上的观察者。
  • 3.suspend修饰符告诉编译器需要从协程或另一个suspend函数中调用它。

8.创建ViewModel

该ViewModel的作用是提供UI的数据和数据的修改。
一个ViewModel充当存储库和UI之间的通信。
您还可以使用ViewModel在Fragment之间共享数据。ViewModel是生命周期库的一部分

class NameViewModel(application: Application) : AndroidViewModel(application) {

    private val repository: NameRepository

    val allNames: LiveData<List<Name>>

    init {
        val namesDao = NameRoomDatabase.getDatabase(application).nameDao()

        repository = NameRepository(namesDao)
        allNames = repository.allNames
    }

    fun insert(name: Name) {
        viewModelScope.launch {
            repository.insert(name)
        }
    }
}

9.创建界面

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintTop_toTopOf="parent">

        <AutoCompleteTextView
            android:id="@+id/mMainActv"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:hint="Input Name" />

        <Button
            android:id="@+id/mMainBtn"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Save" />

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

10.实现功能

class MainActivity : AppCompatActivity() {

    private lateinit var namesViewModel: NameViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        namesViewModel = ViewModelProvider(this).get(NameViewModel::class.java)
        initAutoView()
        initListener()
    }

    private fun initAutoView() {

        val adapter = ArrayAdapter(
            this,
            android.R.layout.simple_dropdown_item_1line, mutableListOf<String>()
        )

        mMainActv.threshold = 1
        mMainActv.setAdapter(adapter)
        mMainActv.dropDownHeight = 800

        val noticeList = Transformations.map(namesViewModel.allNames) { it ->
            val addressStrList = mutableListOf<String>()
            it.forEach { name ->
                addressStrList.add(name.name)
            }
            return@map addressStrList
        }

        noticeList.observeForever {
            adapter.clear()
            adapter.addAll(it)
            adapter.notifyDataSetChanged()
        }
    }

    private fun initListener() {
        mMainBtn.setOnClickListener {
            val text = mMainActv.text.toString()
            if (text.isNotEmpty()) {
                namesViewModel.insert(Name(text))
            }
        }
    }
}

到此,运行APP即刻体验

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容