Android初识系列-Room,我来了~避雷指南

Posted Q-CODER

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android初识系列-Room,我来了~避雷指南相关的知识,希望对你有一定的参考价值。

大家好,我是一个热爱技术的Coder。对于android,我是-【不厉害但爱‘玩’ 】。今天记录的是数据存储这块的内容。

2018年 Room 就问世了,可是到现在有多少人正式用过它呢?这个问题,我也不知道。我问出来,只是因为我之前一直没用,对于本地数据的存储涉及不到。最近项目中有需要,就记录了一下吧。以后,要多多了解新出的技术,例如最近的Compose Beta版,与时代同步。

老规矩,是什么?为什么?怎么用?三步走。

对于一个东西是什么?最好的了解方式,就是上官网,看官网的定义。上才。。不对,上链接。传送门

Room 在 SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库。

那为什么我们直接用 SQLite 呢?这种问题,关于为什么要用一个新的技术,而不用原来的呢?其实官网也会提上一嘴。

处理大量结构化数据的应用可极大地受益于在本地保留这些数据。最常见的用例是缓存相关数据。这样,当设备无法访问网络时,用户仍可在离线状态下浏览相应内容。设备重新连接到网络后,用户发起的所有内容更改都会同步到服务器。
由于 Room 负责为您处理这些问题,因此我们强烈建议您使用 Room(而不是 SQLite)。

在刚开始使用的时候,我发现了一些注解,那么我们先来了解一下这些注解吧。

注解名官方解释

自己理解

@Database

Marks a class as a RoomDatabase.

注意:

这需要是一抽象类并且继承RoomDatabase;

如果您的应用在单个进程中运行,在实例化 AppDatabase 对象时应遵循单例设计模式。每个 RoomDataBase 实例的成本相当高,而您几乎不需要在单个进程中访问多个实例。

就是建立数据库
@Dao

Marks the class as a Data Access Object.

注意:

被这个标注的类需要是一个接口或者是抽象类,

建议有多少个表就有多少个 Dao

就是操作数据库的(CRUD)
@Entity

Marks a class as an entity. This class will have a mapping SQLite table in the database.

注意:

至少有一个主键

可以看作-建表,数据库里的一张表

那他们之间的关系是什么呢?

那么怎么用呢?主要是三个类的创建(DatabaseDaoEntity)

不过首先要记得在gradle中配置依赖

dependencies 
  def room_version = "2.2.6"

  implementation "androidx.room:room-runtime:$room_version"
  kapt "androidx.room:room-compiler:$room_version"

  // optional - Kotlin Extensions and Coroutines support for Room
  implementation "androidx.room:room-ktx:$room_version"

  // optional - Test helpers
  testImplementation "androidx.room:room-testing:$room_version"

Database

@Database(entities = [RecordDataBean::class], version = 1, exportSchema = true)
abstract class RecordDatabase:RoomDatabase() 

    abstract fun chatRecordDao(): ChatRecordDao

    companion object 
        @JvmStatic
        @Volatile
        private var INSTANCE: RecordDatabase? = null

        @JvmStatic
        fun getDatabase(context: Context): RecordDatabase 
            val tempInstance = INSTANCE
            if (tempInstance != null) 
                return tempInstance
            

            synchronized(this) 
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    RecordDatabase::class.java,
                    "record_database" //数据库名称
                ).build()
                INSTANCE = instance
                return instance
            
        
    

这里采用了官方推荐的单例模式来创建 Database


 Dao

@Dao
interface ChatRecordDao 

    //Insert 可以返回插入数据的id值
    @Insert
    suspend fun insert(recordDataBean: RecordDataBean):Long

    @Delete
    suspend fun delete(recordDataBean: RecordDataBean): Int

    @Update
    suspend fun update(recordDataBean: RecordDataBean): Int

    @Query("select * from chat_record where user_account =:userAccount order by date asc")
    suspend fun queryAllRecord(userAccount:String): List<RecordDataBean>

    @Query("select  * from chat_record order by id desc limit 1")
    suspend fun queryLastRecord(): RecordDataBean

    @Query("select * from chat_record where msg_id = :msgId")
    suspend fun isExistInDatabase(msgId:Int): RecordDataBean?


根据自己的业务需求,编写操作数据库的方法。这里最关键的就是 SQL 语句的编写


Entity

@Entity(tableName = "chat_record")
data class RecordDataBean (
    @PrimaryKey(autoGenerate = true) var id:Int?=0,
    @ColumnInfo(name="user_account") var userAccount:String,
    @ColumnInfo(name="user_type") var userType:Int,
    @ColumnInfo(name="content") var content:String,
    @ColumnInfo(name="content_type") var contentType:Int,
    @ColumnInfo(name="date") var date:String,
    @ColumnInfo(name="status") var status:Int,
    @ColumnInfo(name="msg_id") var msgId:Int?=-1
)

最后就是 Entity 了。


在开发过程中,遇到一些疑问和Bug,这里分享出来,避免大家踩坑。

问题描述

Execution failed for task ':app:kaptDebugKotlin'. > A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptExecution > java.lang.reflect.InvocationTargetException (no error message)

解决方案:因为在Dao 忘记用注解了@Dao,加上@Dao注解即可

问题描述

UNIQUE constraint failed: chat_record.id (code 1555 SQLITE_CONSTRAINT_PRIMARYKEY)

解决方案:因为主键重复了,所以报错

问题描述:如何让主键自动增加

解决方案:

data class RecordDataBean ( @PrimaryKey(autoGenerate = true) var id:Int?=0 //省略其他字段 )
//在创建一条数据的时候,将id值为空即可,这样就可以自增了 val recordDataBean3 = RecordDataBean(null)

问题追加1:我填了null,我又怎么得到插进入的数据的id是多少呢?

解决方案:在插入后,可以将对应的主键值返回

//Insert 可以返回插入数据的id值 
@Insert 
suspend fun insert(recordDataBean: RecordDataBean):Long

问题描述:如何通过动态的值来查询数据,例如通过不同userId来获取有关数据

解决方案:

@Query("select * from chat_record where user_id is (:userId) order by date asc") 
suspend fun queryAllRecord(userId: Int): List<RecordDataBean>

问题描述:如果查询结果为空,会返回什么?

测试结果:会返回空


如有疑问和指教,可评论区留言交流。

以上是关于Android初识系列-Room,我来了~避雷指南的主要内容,如果未能解决你的问题,请参考以下文章

Android关于BottomNavigationView效果实现指南

Android关于BottomNavigationView效果实现指南

无关技术 - Android我来了

数据分析避雷指南:2大难关和5个“避坑”技巧

再见腾讯,B站我来了(Android面经)

再见腾讯,B站我来了(Android面经)