持续创作,加快生长!这是我参与「日新方案 10 月更文挑战」的第4天,[点击检查活动详情]

Jetpack从入门到简直入门(四)

(/post/714765… “/post/714765…”)

前言

Jetpack系列:

Jetpack从入门到简直入门(一) – ()

Jetpack从入门到简直入门(二) – ()

Jetpack从入门到简直入门(三) – ()

本文是我在学习guolin大神的《榜首行代码》第三版Jetpack部分的Room的常识总结,文中部分代码参考自《榜首行代码》第三版

在阅读本文前,您需求掌握kotlin语言的根本语法

Room简介

在学习Room之前,每逢提起数据库操作,咱们总是会想到SQLite。SQLite虽然简略易用,但是假如放到大型项目中却会让项目的代码变得紊乱。而Room是基于SQLite上供给的一个笼统层,它不只支撑SQL句子,而且还有许多快捷的功用:

  • 具有SQL句子高亮和编译期检查
  • 经过简略的注解完结接口的界说,易上手
  • 支撑协程、RxJava
  • 可以调配AndroidStudio自带的数据库检查窗口运用

Room的增修正查操作

运用Room前要在build.gradle上增加依赖

plugins {
   ...
  id 'kotlin-kapt'
}
​
def room_version = "2.4.2"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
kapt 'androidx.room:room-compiler:$room_version'

咱们先简略了解一下Room的三个组成部分

  • Entity :用于界说封装实际数据的实体类,每个实体类都在数据库中有一张对应的自动生成的表

  • Dao :Dao 指数据访问目标,对数据库各项操作的封装在此处进行,在实际编程中,逻辑层就不需求和底层数据库打交道,而是接和Dao 层进行交互

  • Database :用于界说数据库中的关键信息,包括数据库的版别号、包括哪些实体类以及提

    供Dao 层的访问实例。

接下来咱们创立一个实体类:

@Entity
data class User(var firstName: String, var lastName: String, var age: Int){
  @PrimaryKey(autoGenerate = true)
  var id:Long = 0
}

在类名上运用@Entity注解,然后加入id字段,运用@PrimaryKey注解将它设为主键。autoGenerate的值设为true,主键的值为自动生成。

接下来开始界说Dao,在这里封装访问数据库的操作(增修正查)。

新建一个UserDao接口

@Dao
interface UserDao {
  @Insert
  fun insertUser(user: User): Long
​
  @Update
  fun updateUser(newUser: User)
​
  @Query("select * from User")
  fun loadAllUsers(): List<User>
​
  @Query("select * from User where age > :age")
  fun loadUsersOlderThan(age: Int): List<User>
​
   @Delete
  fun deleteUser(user: User)
​
  @Query("delete from User where lastName = :lastName")
  fun deleteUserByLastName(lastName: String): Int
}

首先,咱们要在接口前用上@Dao注解,接着咱们进行对增修正查操作进行封装。Room为咱们供给了相应的注解@Insert、@Delete、@Update和@Query。

唯一需求写SQL句子的当地是在查询操作,咱们有必要告知Room需求查询什么当地,其他时分咱们直接运用注解标识即可。这样咱们就大体界说了增加用户、修正用户数据、查询用户、删去用户这几种数据库操作接口。

最终一个环节是界说Database,新建一个AppDatabase.kt 文件

@Database(version = 1, entities = [User::class])  //声明数据库的版别号,包括哪些实体类,多个实体类之间可用逗号隔开
abstract class AppDatabase : RoomDatabase(){
  abstract fun userDao(): UserDao //供给笼统办法,用于获取已编写的Dao实例;只声明办法,具体完成在Room底层自动完结
  companion object { //Kotlin的类中不答应你声明静态成员或办法,要增加Companion目标来包装这些静态引证
    private var instance: AppDatabase? = null
    @Synchronized
    fun getDatabase(context: Context) : AppDatabase {
      instance?.let {
        return it //instance变量不为空直接返回
       }
      //否则就调用Room.databaseBuilder()办法来构建一个AppDatabase的实例
      return Room.databaseBuilder(context.applicationContext,
        AppDatabase::class.java, "app_database")
         .build().apply {
          instance = this
         }
     }
   }
}

接下来咱们修正页面,加上增修正查的4个按钮

<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">
   ...
  <Button
    android:id="@+id/getUserBtn"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:text="Get User"/>
  <Button
    android:id="@+id/addDataBtn"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:text="Add Data"/>
  <Button
    android:id="@+id/updateDataBtn"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:text="Update Data"/>
  <Button
    android:id="@+id/deleteDataBtn"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:text="Delete Data"/>
  <Button
    android:id="@+id/queryDataBtn"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:text="Query Data"/>
</LinearLayout>

在MainActivity中完成代码的逻辑

class MainActivity : AppCompatActivity() {
  val userDao = AppDatabase.getDatabase(this).userDao()
  val user1 = User("Tom", "Brady", 40)
  val user2 = User("Tom", "Hanks", 63)
  val binding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) }
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
​
    setContentView(binding.root)
     ...
    binding.addDataBtn.setOnClickListener {
      thread {
        user1.id = userDao.insertUser(user1)
        user2.id = userDao.insertUser(user2) //更新ID
       }
     }
    binding.updateDataBtn.setOnClickListener {
      thread {
        user1.age = 42
        userDao.updateUser(user1)
       }
     }
    binding.deleteDataBtn.setOnClickListener {
      thread {
        userDao.deleteUserByLastName("Hanks")
       }
     }
    binding.queryDataBtn.setOnClickListener {
      thread {
        for (user in userDao.loadAllUsers()) {
          Log.d("MainActivity", user.toString())
         }
       }
     }
    
​
   ...
}

值得注意的是,数据库操作是耗时操作,所以Room默许不能在主线程中操作,但是Room也供给了办法答应咱们在主线程进行操作:

Room.databaseBuilder(context.applicationContext, AppDatabase::class.java,"app_database")
 .allowMainThreadQueries() 
 .build()

不过此办法只主张在测试环境下运用。

Room的数据库更新

进行数据库升降级时,咱们可以运用addMigration进行操作,在AppDatabase的getDatabase中编写instance:

instance = Room.databaseBuilder(
      context.applicationContext,
      AppDatabase::class.java,
      "user.db"
     ).addMigrations(object :Migration(1,2){ //从版别1到版别2,降级则倒转
      override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE user ADD age INTEGER Default 0 not null ")
       }
​
     })allowMainThreadQueries().build()

Room还供给了两个办法:

  • fallbackToDestructiveMigration:升级时可运用该办法,当未匹配到版别的时分就会直接删去表然后从头创立。
  • fallbackToDestructiveMigrationOnDowngrade:降级时可增加此办法,当未匹配到版别的时分就会直接删去表然后从头创立。

总结

学会