今天学习android的数据库和协程

ROOM

ROOM类似于JPA,能够定义DAO层使用预置的方法进行数据的操作。ROOM主要包含下面三个组件:

  1. Database数据库类: 用于保存数据库并作为应用持久性数据底层连接的主要访问点。其实我感觉Database的类就是提供了Dao层的实例。

  2. Entity实体类:用于表示应用的数据库中的表。

  3. Dao类:提供您的应用可用于查询、更新、插入和删除数据库中的数据的方法。类似于Spring中的Dao层,定义了操作数据库的方法。

Entity

实体类就是我们常见的实体类,使用的注解也和JPA类似

  • @Entity(tableName = “users”):该注解说明该类是一个实体类,并且关联了users的表

  • @PrimaryKey:用于字段上,说明该字段是主键

  • @ColumnInfo(name = “last_name”):用于字段上,绑定实体类中的属性和表中的字段

1
2
3
4
5
6
@Entity(tableName = "users")
data class User (
    @PrimaryKey val id: Int,
    @ColumnInfo(name = "first_name") val firstName: String?,
    @ColumnInfo(name = "last_name") val lastName: String?
)

Dao

在Dao类中定义方法来操作数据库。下面是一个简单样例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Dao
interface UserDao {
    @Insert
    fun insertAll(vararg users: User)

    @Delete
    fun delete(user: User)

    @Query("SELECT * FROM user")
    fun getAll(): List<User>
}

可以看到提供了Insert和Delete的注解,不需要编码sql语句即可完成数据库操作,和JPA类似。

  • @Insert 和 @Update: insert和update就是插入和更新,参数为实体类

  • @Delete: 以实体类为参数删除实体,实体类中必须包含主键

  • @Query: query就直接可以写原生的sql去查询或者去进行数据库的操作

Database

关于Database类有点不太理解,已经有了Dao层去完成数据库操作,只需要在ViewModel中去引入Dao层然后操作即可,为什么还有要Database呢?下面是官方的样例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
abstract class SleepDatabase : RoomDatabase() {

    abstract val sleepDatabaseDao: SleepDatabaseDao

    companion object {

        @Volatile
        private var INSTANCE: SleepDatabase? = null

        fun getInstance(context: Context): SleepDatabase {
            synchronized(this) {
                var instance = INSTANCE

                if (instance == null) {
                    instance = Room.databaseBuilder(
                            context.applicationContext,
                            SleepDatabase::class.java,
                            "sleep_history_database"
                    )
                            .fallbackToDestructiveMigration()
                            .build()
                    INSTANCE = instance
                }
                return instance
            }
        }
    }
}// Song and Album are classes annotated with @Entity.
@Database(version = 1, entities = {Song.class, Album.class})
abstract class MusicDatabase : RoomDatabase {
  // SongDao is a class annotated with @Dao.
  abstract fun getSongDao(): SongDao

  // AlbumDao is a class annotated with @Dao.
  abstract fun getArtistDao(): ArtistDao

  // SongAlbumDao is a class annotated with @Dao.
  abstract fun getSongAlbumDao(): SongAlbumDao
}

可以看到使用了@Database的注解,包含了版本号,以及实体类,这里的实体类比较好理解,是下面引用的Dao类的实体类。那么版本号呢?

版本号用户数据库表结构的更新,如果数据库表结构变更了,则需要变更版本号,然后设置Database的实例创建增加fallbackToDestructiveMigration配置就可以了。

但是这样做会有缺点,所有的数表都会被删掉,所以需要使用迁移功能。

迁移功能在更新版本号的同事还需要重写migrate方法然后将表结构更改的语句加上即可,如下.

1
2
3
4
5
6
7
8
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
      //此处对于数据库中的所有更新都需要写下面的代码
        database.execSQL("ALTER TABLE users "
                + " ADD COLUMN last_update INTEGER");
    }
};

加完之后database的实例加上.addMigrations(MIGRATION_1_2)就完成了。

协程

一般java使用多线程来处理业务量比较大的操作,那么在android+kotlin里面使用协程来操作。

如果一个应用必须等待某项任务完成后才能继续,那么系统可能会阻止其正常执行。例如,读取大文件或执行冗长的数据库调用就是可能会阻止或“阻塞”整个应用执行的任务。这样不仅会降低应用对用户的响应能力,而且也不能高效地利用硬件。执行长时间运行的任务而不阻塞主线程的一种模式是使用回调。这是一种有用的模式,但它有一些缺点。有关这些概念的介绍,请参阅多线程和回调入门指南。

有关于协程的知识,官方解释的很清楚:

在 Kotlin 中,使用协程来顺畅且高效地处理长时间运行的任务,而不是回调。Kotlin 协程使您能够将基于回调的代码转换为顺序代码。顺序编写的代码通常更易于阅读和维护。与回调不同,协程可以安全地使用有价值的语言功能,如异常。最重要的是,协程具有更高程度的可维护性和灵活性。最后,协程和回调执行相同的功能:它们都能够处理应用中可能长时间运行的异步任务。

简而言之,对于需要回调的方法来说,使用协程可以不等回调,顺序执行。

有关协程的操作可以看官方文档:

https://kotlinlang.org/docs/coroutines-guide.html

协程在android中的使用

如room中的操作,对于数据库的操作完全可以使用协程后台操作。所以上述的简单样例可以改成这样:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Dao
interface UserDao {
    @Insert
    suspend fun insertAll(vararg users: User)

    @Delete
    suspend delete(user: User)

    @Query("SELECT * FROM user")
    suspend getAll(): List<User>
}

更多的协程的知识,请看系列博文:

https://juejin.cn/post/6953441828100112392