Android Room简单使用,结合Flow监听数据变化

Android Room简单使用,结合Flow监听数据变化

Room持久性库在 SQLite 的基础上供给了一个笼统层,让用户能够在充分利用 SQLite 的强大功能的同时,获享更强健的数据库拜访机制。

该库可协助您在运转运用的设备上创立运用数据的缓存。此缓存充当运用的单一可信来历,运用户能够在运用中检查要害信息的一致副本,不管用户是否具有互联网连接。

Room特色

  • 干流结构
  • 支撑SQL句子
  • 操作数据库需要编写笼统函数,操作简洁
  • 官方保护, JetPack组件中的数据库结构
  • 监听数据库
  • 支撑嵌套目标
  • 支撑Kotlin 协程/RxJava
  • 具有SQL句子高亮和编译期检查(具有AndroidStudio的支撑)
  • 运用SQLite便于多个平台的数据库文件传递(例如有些联系人信息便是一个SQLite文件)
  • 由于是SQLite能够经过第三方结构进行数据库加密(ROOM原生不支撑)
  • 能够合作AndroidStudio自带的数据库检查东西窗口

总结起来,简单入门,功能强大,数据库监听,支撑Kotlin协程、RxJava、Flow、Guava。

Room结构

Room 中有 3 个首要组件。

  • Database:此注解将类符号为数据库。它应该是一个扩展的笼统类RoomDatabaseRoom.databaseBuilder在运转时,您能够经过或获取它的一个实例Room.inMemoryDatabaseBuilder

    数据库类界说了数据库中实体和数据拜访目标的列表。它也是底层连接的首要拜访点。

  • Entity:此注解将类符号为数据库行。对于每个Entity,都会创立一个数据库表来保存项目。实体类必须在Database#entities数组中被引用。实体(及其超类)的每个字段都保存在数据库中,除非还有说明(有关Entity详细信息,请参阅文档)。

  • Dao:此注解将类或接口符号为数据拜访目标。数据拜访目标是 Room 的首要组件,担任界说拜访数据库的办法。被注解的类Database必须有一个笼统办法,该办法有 0 个参数并回来被 Dao 注解的类。在编译时生成代码时,Room 将生成此类的实现。

    运用 Dao 类进行数据库拜访而不是查询构建器或直接查询允许您在不同组件之间保持别离,并在测验您的运用程序时轻松模仿数据库拜访。

    Android Room简单使用,结合Flow监听数据变化

gradle添加依赖库

dependencies {
    //可选 - kotlin 协程,含有Flow
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
    def room_version = "2.2.3"
    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version" // Kotlin 运用 kapt 代替 annotationProcessor
    // 可选 - Kotlin扩展和协程支撑
    implementation "androidx.room:room-ktx:$room_version"
    // 可选 - RxJava 支撑
    implementation "androidx.room:room-rxjava2:$room_version"
    // 可选 - Guava 支撑, including Optional and ListenableFuture
    implementation "androidx.room:room-guava:$room_version"
    // 测验
    testImplementation "androidx.room:room-testing:$room_version"
}

Gradle装备


    javaCompileOptions {
        annotationProcessorOptions {
            arguments = [
                    "room.schemaLocation":"$projectDir/schemas".toString(),
                    "room.incremental":"true",
                    "room.expandProjection":"true"]
        }
    }
 }
  • room.expandProjection 运用星投影时会依据函数回来类型来重写SQL查询句子
  • room.schemaLocation 输出数据库概要,能够检查字段信息,版本号,数据库创立句子等
  • room.incremental 启用Gradle增量注释处理器

运用

首要创立Entity类

经过@Entity符号创立表格,数据库初始化时主动生成表,咱们能够经过该类界说对应的字段特点

@Entity(tableName = "user")//表名默以为UserEntity类名,运用tableName可重命名
data class UserEntity(
    @PrimaryKey //主键
    var code: Int?,
    @ColumnInfo(name = "user_name")//列名默以为字段名name,可运用该注解重命名
    var name:String?,
    var age:Int?,
    var grender:String?,
)

创立Dao类

创立接口类UserDao,运用@Dao符号,在该接口类中咱们界说增修改查的操作。

@Dao
interface UserFlowDao {
    @Query("select * from user")//结合flow监听数据变化,避免重复更新ui数据
    fun queryAllUser(): Flow<List<UserEntity>>
    //key键重复的替换
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(user: UserEntity)
    @Query("delete from user")//删除一切数据
    fun deleteAll()
    @Query("select * from user WHERE age = :age")//依据条件查询
    fun queryAgeUser(age: Int): List<UserEntity>
    @Query("delete from user WHERE code = :code")
    fun deletedUser(code: Int)
}

操作数据库需要在子线程中进行,不然会报异常java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.

Query查询操作,履行SQL句子时,数据表严厉对照类名(区分大小写),不然会影响重命名类名时无法照顾到SQL句子中的表名。

创立数据库

创立database笼统类,继承RoomDatabase,把一切Entity的class目标联系在一起,组成完好的数据库。经过@Database符号该笼统类,界说获取dao接口的笼统办法

@Database(entities = [UserEntity::class], version = 1, exportSchema = false)
abstract class SQLDatabase :RoomDatabase(){
    abstract fun userFlow():UserFlowDao //flow操作类
}

参数

  • entities 传入Entity类class目标,也是一切表格
  • version 数据库版本号
  • exportSchem 设置是否导出数据库schem,默以为true,需要在build.gradle中设置

Demo实例

在的activity或fragment中,room供给databaseBuilder()办法初始化

private val database by lazy {
    Room.databaseBuilder(this, SQLDatabase::class.java, "test_demo").build()
}

获取该数据库目标,有了database咱们就能够拿到Dao的目标实例,经过这个能够进行数据库的增修改查操作。

第一个参数context上下文,传入当时this即可。

第二个参数class,传入room的database类class目标,也便是@Database符号的类。

第三个参数name,数据库保存后的文件名称。

UI界面布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:paddingTop="10dp"
        android:layout_marginTop="20dp"
        android:layout_gravity="center_vertical"
        android:orientation="horizontal">
        <TextView
            android:id="@+id/tv_add"
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:paddingRight="5dp"
            android:paddingLeft="5dp"
            android:text="添加数据"
            android:gravity="center"
            android:textColor="@color/white"
            android:layout_gravity="center_horizontal"
            android:background="@color/design_default_color_secondary"/>
        <TextView
            android:id="@+id/tv_deletedAll"
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:paddingRight="5dp"
            android:paddingLeft="5dp"
            android:layout_marginLeft="10dp"
            android:text="删除全部"
            android:gravity="center"
            android:textColor="@color/white"
            android:layout_gravity="center_horizontal"
            android:background="@color/design_default_color_secondary"/>
        <TextView
            android:id="@+id/tv_selectQuery"
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:paddingRight="5dp"
            android:paddingLeft="5dp"
            android:layout_marginLeft="10dp"
            android:text="条件查询"
            android:gravity="center"
            android:textColor="@color/white"
            android:layout_gravity="center_horizontal"
            android:background="@color/design_default_color_secondary"/>
    </LinearLayout>
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_marginTop="10dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

编写Activity

class UserDaoDemoActivity : AppCompatActivity(), CoroutineScope by MainScope() {
    //数据实例目标,能够初始化一次即可
    private val database by lazy {
        Room.databaseBuilder(this, SQLDatabase::class.java, "test_demo").build()
    }
    private var mUserFlowDao: UserFlowDao? = null
    private var mAdapter: UserAdapter? = null
    private var recyclerView: RecyclerView? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user_dao)
        mUserFlowDao = database.userFlow()
        initView()
    }
    private fun initView() {
        recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
        recyclerView!!.layoutManager = LinearLayoutManager(this)
        mAdapter = UserAdapter(this)
        recyclerView!!.adapter = mAdapter
        var index = 0
        //添加数据
        findViewById<TextView>(R.id.tv_add).setOnClickListener {
            launch {
                withContext(Dispatchers.IO) {//操作数据库必须要在子线程中操作,经过协程运用io线程履行添加数据的操作
                    index++
                    val randValue = (Math.random() * 900).toInt() + 10
                    mUserFlowDao!!.insert(UserEntity(randValue, "学生$randValue", 10 + index, "男"))
                }
            }
        }
        //删除一切
        findViewById<TextView>(R.id.tv_deletedAll).setOnClickListener {
            launch {
                withContext(Dispatchers.IO) {
                    mUserFlowDao!!.deleteAll()
                }
            }
        }
        findViewById<TextView>(R.id.tv_selectQuery).setOnClickListener {
            launch {//条件查询
                withContext(Dispatchers.IO) {
                    val list = mUserFlowDao!!.queryAgeUser(20)
                    withContext(Dispatchers.Main) {//查询的结果在主线程更新数据
                        mAdapter!!.setData(list)
                    }
                }
            }
        }
        mAdapter!!.setOnItemClickListener(object : UserAdapter.OnItemClickListener {
            override fun onDeleted(userEntity: UserEntity) {
                launch {
                    withContext(Dispatchers.IO) {
                        mUserFlowDao!!.deletedUser(userEntity.code!!)
                    }
                    Toast.makeText(this@UserDaoDemoActivity, "删除成功", Toast.LENGTH_SHORT).show()
                }
            }
        })
//结合flow监听数据库变化,咱们只需要注册一次即可,当履行增、删操作后会主动履行数据查询,更新UI。不然每次履行后咱们都要从头查询数据再更新UI
        updateView()
    }
    private fun updateView() {
        mUserFlowDao!!.queryAllUser().onEach {
            mAdapter!!.setData(it)
        }.launchIn(this)
    }
}

Adapter代码

class UserAdapter(var mContext: Context) :
    RecyclerView.Adapter<UserAdapter.ViewHolder>() {
    private var mData: List<UserEntity>? = null
    fun setData(data: List<UserEntity>?) {
        this.mData = data
        notifyDataSetChanged()
    }
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_user, parent, false))
    }
    override fun getItemCount() = if (mData.isNullOrEmpty()) 0 else mData!!.size
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val userEntity = mData!!.get(position)
        holder.tvContent!!.text =
            "${userEntity.name}  学生号:${userEntity.code}   年纪:${userEntity.age}  性别:${userEntity.grender}"
        holder.tvContent!!.setOnClickListener {
            if (listener != null)
                listener!!.onDeleted(mData!![position])
        }
    }
    class ViewHolder : RecyclerView.ViewHolder {
        var tvContent: TextView? = null
        constructor(itemView: View) : super(itemView) {
            tvContent = itemView.findViewById<TextView>(R.id.tv_content)
        }
    }
    private var listener: OnItemClickListener? = null
    fun setOnItemClickListener(listener: OnItemClickListener?) {
        this.listener = listener
    }
    interface OnItemClickListener {
        fun onDeleted(userEntity: UserEntity)
    }
}

运转APP

咱们也能够运用android studio供给的东西检查数据库实时数据,Android studio Electric Eel版本能够在App Inspection检查,如下图:

Android Room简单使用,结合Flow监听数据变化