irpas技术客

【Kotlin】Android-Room库持久性保存数据简单教程—附demo源码_大风起兮呼呼呼_android kotlin room

未知 3034

本文旨在以少量代码和简单逻辑,完整的使用Room,文中demo源码链接附在文末

?特别说明:

????????本文采用分—总结构展示代码(除gradle文件),即先展示局部代码,并说明其用法和作用;小节结尾处再给出该部分对应文件的完整代码。布局XML源文件在文末。

目录

前言

一.配置数据库

1.添加依赖

2.创建实体数据类

3.创建Dao接口

4.创建数据库抽象类

5.完善Dao

二.MainActivity中调用

1.创建添加、查询函数

?2.设置点击事件

3.真机测试结果

4.实时可视化查看本地数据库

三.布局设置


前言

????????谷歌官方提供的Room持久性库,允许更加便捷、流畅的操作本地SQLite数据库,但遗憾的是,官方的说明文档似乎有一段时间没有更新了,其中的示例代码存在一些问题,不能直接照搬到项目中,这会给初次接触Room的初学者造成很大的困扰。

Room需要至少3个类来运行,以及1个注意点:

实体数据类:定义数据库中的表Dao接口:创建实际访问数据库的函数数据库抽象类:声明数据库中的表,创建访问数据库的入口注意点:访问数据库属于耗时操作,只能在子线程或者协程中进行
一.配置数据库 1.添加依赖

????????首先需要在gradle(Moudle)中添加Room所需依赖。

plugins { id 'kotlin-kapt' } dependencies { // ROOM库 implementation("androidx.room:room-runtime:2.4.2") kapt("androidx.room:room-compiler:2.4.2") }
2.创建实体数据类

????????2.1创建一个Room包,方便管理后续添加的类。

?????????2.2创建一个普通的实体数据类,命名为User,本文为方便仅设置3个属性id、name、age

data class User ( val id: Long=0, val name: String, val age: Int)

????????2.3添加Room的实体类注解。(User的完整代码)

@Entity data class User ( @PrimaryKey(autoGenerate = true) val id: Long=0, val name: String, val age: Int)

? ? ? ? 直接在类属性上方添加@PrimaryKey(autoGenerate = true)指将此属性设置为表的自增主键(数据库自动赋值,逐渐增大,而我们只要在类中随意设定一个默认值,在实例化此类时,就无需手动给id属性赋值,还省去了确保id值的唯一性的麻烦)。

? ? ? ? 其他属性若不加注解,则默认为表中的一个字段。


3.创建Dao接口

????????3.1创建一个普通的接口,命名为UserDao

interface UserDao { }

????????3.2添加Room的Dao注解(只写了一半,后文会完善)

@Dao interface UserDao { }
4.创建数据库抽象类

????????4.1创建一个普通的抽象类,命名为MyDataBase

abstract class MyDataBase : RoomDatabase() { }

????????4.2添加Room的数据库注解

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

数据库注解的解释:

version:数据库版本,未来数据库进行更新升级,其中的表、字段有变更的时候需要依次增加数据库版本,以此告知数据库,其中内容有变化了,否则会报错。entities:获取实体类User,自动定义出一张User表;Room中通过一个实体数据类即可自动定义一张表,若数据库中需要多张表,使用英文逗号分隔即可。(Admin类只用于演示说明,不存在于本demo) @Database(version = 1, entities = [User::class, Admin::class], exportSchema = false) exportSchema:数据库是否允许导出,在高版本的Android环境中,需要将此项设置为false,禁止导出,否则报错。

????????4.3创建访问数据库中User表的入口:即写一个抽象函数返回UserDao对象。

@Database(version = 1, entities = [User::class], exportSchema = false) abstract class MyDataBase : RoomDatabase() { abstract fun userDao(): UserDao }

????????4.4采用单例模式来获取该数据库类,防止多次创建该数据库类对象,开销很大。(MyDataBase的完整代码)

@Database(version = 1, entities = [User::class], exportSchema = false) abstract class MyDataBase : RoomDatabase() { abstract fun userDao(): UserDao companion object { private var instance: MyDataBase? = null @Synchronized fun getDataBase(context: Context): MyDataBase { return instance ?: Room.databaseBuilder(context.applicationContext, MyDataBase::class.java, "any_name_is_ok") .build() .apply { instance = this } } } }
5.完善Dao

????????向其中添加增删查改的函数。(UserDao的完整代码)

@Dao interface UserDao { @Insert // 增 fun insertUser(user: User) @Delete // 删 fun deleteUser(user: User) @Query("select * from user") // 查 fun getAllUser(): List<User> @Update // 改 fun updateUser(user: User) }

????????此时Room的强大优势就显现出来了,仅需添加注解即完成了完全的访问,其中以@Query注解的查询函数可以直接以User类的形式返回数据,无需处理cursor对象,极大的便捷了数据库的操作。

????????值得一提的是,自带的@Delete,@Update注解使用场景较为有限,或者说操作权限偏于安全,因为它们要求必须传进来的User对象于库中待修改的User对象的主键值必须一致。比如库中有一个小明,若我试图修改库中小明的年龄信息时,我必须首先得知小明的主键id,这样就不能随便实例化一个User对象来进行增删改的操作。

????????但@Query注解支持以SQL语句进行数据库的操作,所以日常使用及定制功能其实完全可以只用@Query注解实现。? ??


二.MainActivity中调用 1.创建添加、查询函数

? ? ? ? 为便于演示,本文不实现依赖于@Delete,@Update注解的逻辑较复杂的删改函数及布局。

fun addUser() { // 启动一个子线程 thread { // 从数据库类中获取Dao接口,再用Dao访问数据库 val dao = MyDataBase.getDataBase(this).userDao() val user = User(name = "小明", age = 18) dao.insertUser(user) } } fun queryUser(): String { var showText = "" // 启动一个子线程 thread { // 从数据库类中获取Dao接口,再用Dao访问数据库 val dao = MyDataBase.getDataBase(this).userDao() val list = dao.getAllUser() for (user in list) { showText += "名字:${user.name},年龄:${user.age},id:${user.id}\n\n" } }.join() // 为了获取子线程中查询到的结果,此处简单的使用join等待子线程完成,再结束函数 return showText } ?2.设置点击事件 // 绑定布局控件,XML文件在后文 val show = findViewById<TextView>(R.id.show) val addButton = findViewById<Button>(R.id.add) val queryButton = findViewById<Button>(R.id.query) // 设置“增”按钮的点击事件 addButton.setOnClickListener { addUser() } // 设置“查”按钮的点击事件 queryButton.setOnClickListener { val showText = queryUser() show.text = showText }

????????必须注意的是,访问数据库属于耗时操作,安卓不允许耗时操作在主线程(UI线程)中进行,长时间无响应,会引起页面崩溃,所以对数据库的所有操作都必须在子线程中使用。以下是简单调用子线程的方式。

thread { // 里面放代码块,就直接启动了一个子线程 } thread { // .join()表示等待子线程执行完 }.join() 3.真机测试结果

?

4.实时可视化查看本地数据库

????????请移步Android-实时可视化查看本地数据库

????????MainActivity的完整代码

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 绑定布局控件,XML文件在后文 val show = findViewById<TextView>(R.id.show) val addButton = findViewById<Button>(R.id.add) val queryButton = findViewById<Button>(R.id.query) // 设置“增”按钮的点击事件 addButton.setOnClickListener { addUser() } // 设置“查”按钮的点击事件 queryButton.setOnClickListener { val showText = queryUser() show.text = showText } } fun addUser() { // 启动一个子线程 thread { // 从数据库类中获取Dao接口,再用Dao访问数据库 val dao = MyDataBase.getDataBase(this).userDao() val user = User(name = "小明", age = 18) dao.insertUser(user) } } fun queryUser(): String { var showText = "" // 启动一个子线程 thread { // 从数据库类中获取Dao接口,再用Dao访问数据库 val dao = MyDataBase.getDataBase(this).userDao() val list = dao.getAllUser() for (user in list) { showText += "名字:${user.name},年龄:${user.age},id:${user.id}\n\n" } }.join() // 为了获取子线程中查询到的结果,此处简单的使用join等待子线程完成,再结束函数 return showText } }

?为便于演示,本文不实现依赖于@Delete,@Update注解的逻辑较复杂的删改函数及布局。

三.布局设置

????????在约束布局中,简单的设置1个文本框、1个“增”按钮、1个“查”按钮。

????????布局XML文件的完整代码

<?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=".MainActivity"> <TextView android:id="@+id/show" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.25" /> <Button android:id="@+id/add" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="增" android:textSize="24dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.74" /> <Button android:id="@+id/query" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:text="查" android:textSize="24dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/add" /> </androidx.constraintlayout.widget.ConstraintLayout>

如有任何疑问,请在评论区留言,我会努力回复

demo源码?github地址:

https://github.com/darlingxyz/demo_room


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Android #Kotlin #room