美文网首页
Jetpack 之 Room 小白入手

Jetpack 之 Room 小白入手

作者: 鹅鹅鹅曲项向天歌呀 | 来源:发表于2021-07-01 08:27 被阅读0次

    声明 : https://www.jianshu.com/p/714062a9af75
    目录

    简介
    原理
    使用方法

    1,基本使用
    2, Room 与 LiveData,ViewModel 的结合使用
    3,数据库升级

    🍀 简介:

       Android 存储数据的方式:SharePerference,文件存储,SQLite 数据库存储,第三方数据库.Google 在 SQLite 基础上封装了了轻量级的数据库 Room,访问数据库更稳更快.
       Room持久性库在 SQLite 的基础上提供了一个抽象层,让用户能够在充分利用 SQLite 的强大功能的同事,获享更强健的数据库访问机制,该库可以帮助您运行应用的设备上创建应用数据的缓存.此缓存应充当应用的单一可信来源,使用户能够在应用中查看关键的信息的一致副本,无论用户是否具有互联网链接.

    🍀 原理:

    image.png
    • Data Access Objects : 数据访问集
    • Get Entities from db : 从数据库获取实体类
    • Persist changes back to db : 数据持久化后返回数据库
    • get/set field values : 获取字段的值
    • Dao : 包含用户访问数据库的方法

    🍀 几个关键的注解:

    • 🐥 @Entity 对应数据库中的一张表,可以设置表名,若不指定,则为类名
    • 🐥 @PrimaryKey 指定表的主键
    • 🐥 @ColumnInfo 指定列的名字,若不指定,则为属性名字
    • 🐥 @Ignore 忽略字段或者方法
    • 🐥 @Dao (data access object) 接口文件或者抽象类,以便对 Entity 进行访问
    • 🐥 @Database 告诉系统这是 Room 数据库对象.enterties 属性用于指定有哪些表,多张表,用逗号隔开,version 用于指定数据库的版本号.

    🍀 使用方法:

    官网连接 : https://developer.android.google.cn/topic/libraries/architecture/room 查看引用的最新版本

    1.build.gradle

       apply plugin: 'kotlin-kapt'
       .......
    
        //room
        implementation "androidx.room:room-runtime:2.3.0"
        annotationProcessor "androidx.room:room-compiler:2.3.0"
        //Room的Kotlin扩展和对协程的支持
        kapt "androidx.room:room-compiler:2.3.0"
    

    因为我用 kotlin 写的项目,所以要加第三个引用~~~否则会报错哦.
    java ,不需要~~

    2.创建一个学生的 Entity,即创建一张学生的表

    @Entity(tableName = "table_student")
    data class Student(
        @PrimaryKey(autoGenerate = true)
        @ColumnInfo(name = "s_id", typeAffinity = ColumnInfo.INTEGER)
        var id: Int,
        @ColumnInfo(name = "s_name", typeAffinity = ColumnInfo.TEXT)
        var name: String,
        @ColumnInfo(name = "s_sex", typeAffinity = ColumnInfo.TEXT)
        var sex: String
    ) {
    
        /***
         * 由于 Room 只能识别和使用一个构造器,如果想使用多个构造器,使用 @Ignore 标签,忽略构造器
         *
         */
        @Ignore
        constructor(name: String, sex: String) : this(0, name, sex) {
            this.name = name
            this.sex = sex
        }
    }
    

    3.学生的Dao 接口文件

    @Dao
    interface StudentDao {
        @Insert
        fun insertSudent(stdent: Student)
    
        @Delete
        fun deleteStudent(stdent: Student): Int
    
        @Update
        fun updataStudent(stdent: Student): Int
    
        @Query("select * from table_student")
        fun getStudentList(): MutableList<Student>
    
        @Query("select * from table_student where s_id =:id")
        fun getStudent(id: Int): Student
    }
    

    4.创建数据库

    @Database(entities = [Student::class], version = 1)
    abstract class StuentDataBase : RoomDatabase() {
        companion object {
            @Synchronized
            fun getInstance(context: Context): StuentDataBase {
                var stuentDataBase: StuentDataBase? = null
                val DATABASE_NAME = "student_db"
                stuentDataBase =
                    Room.databaseBuilder(context, StuentDataBase::class.java, DATABASE_NAME).build()
                return stuentDataBase
            }
        }
    
        abstract fun studentDao(): StudentDao
    }
    

    5.对数据库进行增删改查

    ⚠️⚠️⚠️不能再 UI线程操作增删改查,必须在工作线程中⚠️⚠️⚠️
    看完成的代码吧,我是通过 AsycTask 的方式实现的,当然了,其他方式还有很多种~~~

    class TestActivity : AppCompatActivity() {
        lateinit var stuentDataBase: StuentDataBase
        lateinit var studentList: MutableList<Student>
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_test)
            stuentDataBase = StuentDataBase.getInstance(this)
    
        }
    
        fun onclick(view: View) {
            when (view.id) {
                R.id.button1 -> {//插入数据
                    InsertStudentTask(Student("张三", "男")).execute()
                }
                R.id.button -> {//删除数据
                    DeleteStudentTask(Student(2, "张三", "男")).execute()
                }
                R.id.button2 -> {//更新数据
                    UpdateStudentTask(Student(1, "张三", "女")).execute()
                }
                R.id.button3 -> {//查询数据
                    QueryStudentTask().execute()
                }
            }
        }
    
        inner class InsertStudentTask(var student: Student) : AsyncTask<Void?, Void?, Void?>() {
    
            override fun doInBackground(vararg p0: Void?): Void? {
                stuentDataBase.studentDao().insertSudent(student)
                return null
            }
    
            override fun onPostExecute(unused: Void?) {
                super.onPostExecute(unused)
            }
    
    
        }
    
        inner class DeleteStudentTask(var student: Student) : AsyncTask<Void?, Void?, Void?>() {
    
            override fun doInBackground(vararg p0: Void?): Void? {
                var deleteStudent = stuentDataBase.studentDao().deleteStudent(student)
                Log.e("TestActivity", "删除成功$deleteStudent")
                return null
            }
    
            override fun onPostExecute(unused: Void?) {
                super.onPostExecute(unused)
            }
    
    
        }
    
        inner class UpdateStudentTask(var student: Student) : AsyncTask<Void?, Void?, Void?>() {
    
            override fun doInBackground(vararg p0: Void?): Void? {
                var updataStudent = stuentDataBase.studentDao().updataStudent(student)
                Log.e("TestActivity", "更新成功$updataStudent")
    
                return null
            }
    
            override fun onPostExecute(unused: Void?) {
                super.onPostExecute(unused)
            }
    
    
        }
    
        inner class QueryStudentTask : AsyncTask<Void?, Void?, Void?>() {
    
            override fun doInBackground(vararg p0: Void?): Void? {
                studentList = stuentDataBase.studentDao().getStudentList()
                return null
            }
    
            override fun onPostExecute(unused: Void?) {
                super.onPostExecute(unused)
                for (student in studentList) {
                    Log.e("TestActivity", student.toString())
                }
            }
            
        }
        
    }
    

    看运行截图


    image.png

    好了,到此为止,简单的使用已经完成了~~~~😊😊😊

    🍀 Room 与 LiveData,ViewModel 的结合使用

       我们知道LiveData 通常和 ViewModel 一起使用.ViewModel 是存放数据的,因此我们可以将数据库的初始化放在 Viewm 中.当我们执行增,删,改的操作的时候,我们可以用过 LiveData 组件通知我们数据的变化,实现数据自动更新.

    1,修改StudentDao

        @Query("select * from table_student")
        fun getStudentList(): LiveData<MutableList<Student>>
    

    2,创建 StudentViewModel

       数据库的初始化需要 Context,我们不宜传入 Context,会导致内存泄漏,但是我们可以使用子类AndroidViewModel,其构造器中含有 Application 的参数,Application 作为 Context 的子类,可以用于数据库的初始化.

    public class StudentViewModel extends AndroidViewModel {
    
        private StuentDataBase stuentDataBase;
    
        public LiveData<List<Student>> getLivaDataStudent() {
    
            return livaDataStudent;
        }
    
        private LiveData<List<Student>> livaDataStudent;
    
        public StudentViewModel(@NonNull @NotNull Application application) {
            super(application);
            stuentDataBase = StuentDataBase.Companion.getInstance(application);
            livaDataStudent = stuentDataBase.studentDao().getStudentList();
        }
    
    }
    

    3,在TestActivity实例化 StudentViewModel,监听数据变化

    class TestActivity : AppCompatActivity() {
        lateinit var stuentDataBase: StuentDataBase
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_test)
            stuentDataBase = StuentDataBase.getInstance(this)
            var studentViewModel = ViewModelProvider(this).get(StudentViewModel::class.java)
            studentViewModel.livaDataStudent.observe(this,
                {
                    for (student in it) {
                        Log.e("TestActivity", student.toString())
                    }
                })
        }
    }
    

    当在执行插入,修改,删除的操作的时候,会自动执行查询才做,检测结果.

    🍀 数据库升级

      随着业务的增加,我们会对数据库做一些调整,比如在表中新加一个字段.Android 提供了一个类为 Migration 的类来进行升级.Migration有两个参数,startVersion 和 endVersion.

    //从 1 到 2
      val MIGRATION_1_2: Migration = object : Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                //执行相关的操作
            }
        }
     //从 2 到 3
      val MIGRATION_2_3: Migration = object : Migration(2, 3) {
            override fun migrate(database: SupportSQLiteDatabase) {
                //执行相关的操作
            }
        }
    //.........
    

    以此类推,然后通过addMigrations()添加升级,

    Room.databaseBuilder(context.applicationContext,StuentDataBase::class.java,DATABASE_NAME)
                        .addMigrations(MIGRATION_1_2,MIGRATION_2_3,....)
                        .build()
    

    同事,也要修改数据库的版本号

    @Database(entities = [Student::class], version = 2)
    

    ⚠️如果当前数据库版本的为 1,而当前要安装的版本为 3,那么 Room 会判断当前有没有从 1 升到 3 的方案,如果有,则执行 Migration(1 , 3) 的方案,如果没有,则会执行 Migration(1 , 2),再从Migration(2 , 3).
    ⚠️如果数据库版本升到 4,但又没有相应 Migration ,为了防止异常崩溃,我们可以再创建数据库的时候,加入fallbackToDestructiveMigration()方法,该方法在出现异常的时候,重新创建数据库表,虽然不会崩溃,但是数据会丢失哦.

    Room.databaseBuilder(context.applicationContext,StuentDataBase::class.java,DATABASE_NAME)
                        .fallbackToDestructiveMigration()
                        .addMigrations(MIGRATION_1_2,MIGRATION_2_3,....)
                        .build()
    

    举例说明: 上面的例子中,学生的表中,新加一个 age 字段

    1.Student

    @Entity(tableName = "table_student")
    data class Student(
        @PrimaryKey(autoGenerate = true)
        @ColumnInfo(name = "s_id", typeAffinity = ColumnInfo.INTEGER)
        var id: Int,
        @ColumnInfo(name = "s_name", typeAffinity = ColumnInfo.TEXT)
        var name: String,
        @ColumnInfo(name = "s_sex", typeAffinity = ColumnInfo.TEXT)
        var sex: String,
        @ColumnInfo(name = "s_age")
        var age: Int,
    ) {
    
        /***
         * 由于 Room 只能识别和使用一个构造器,如果想使用多个构造器,使用 @Ignore 标签,忽略构造器
         *
         */
        @Ignore
        constructor(name: String, sex: String) : this(0, name, sex, 0) {
            this.name = name
            this.sex = sex
        }
    
    }
    

    2 编写Migration类,并执行升级

    @Database(entities = [Student::class], version = 2)
    abstract class StuentDataBase : RoomDatabase() {
        companion object {
            private val MIGRATION_1_2: Migration = object : Migration(1, 2) {
                override fun migrate(database: SupportSQLiteDatabase) {
                    //创建一个新表
                    database.execSQL(
                        "create table table_student2(s_id INTEGER PRIMARY KEY not null,s_name TEXT not null,s_sex TEXT not null,s_age  INTEGER not null default 0 )"
                    )
                    //复制数据
                    database.execSQL("insert into table_student2(s_id,s_name,s_sex) select s_id,s_name,s_sex from table_student")
                    //删除旧的表
                    database.execSQL("drop table table_student")
                    //重新命名数据表的名字
                    database.execSQL("alter table table_student2 rename to table_student")
                }
            }
            private var stuentDataBase: StuentDataBase? = null
            private const val DATABASE_NAME = "student_db"
    
            @Synchronized
            fun getInstance(context: Context): StuentDataBase {
                if (stuentDataBase == null) {
                    stuentDataBase = Room.databaseBuilder(
                        context.applicationContext,
                        StuentDataBase::class.java,
                        DATABASE_NAME
                    )
                        .addMigrations(MIGRATION_1_2)
                        .build()
                }
                return stuentDataBase as StuentDataBase
            }
        }
    
        abstract fun studentDao(): StudentDao
    }
    

    3,TestActivity还是不变的~,看一下运行截图

    我们更新了一条数据,更新是没问题的,age 字段也打印出来啦,默认是 0.


    image.png

    END

    相关文章

      网友评论

          本文标题:Jetpack 之 Room 小白入手

          本文链接:https://www.haomeiwen.com/subject/skbpjltx.html