Android—Jetpack教程(六)
2021/11/29 6:08:18
本文主要是介绍Android—Jetpack教程(六),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
前言
在上一篇中,对Room进行了ViewModel+LiveData封装。在本篇中,将会讲解Room对应的升级与预填充。直接开始吧!
1、预填充数据库
如图所示
有时候我们希望应用自带一些数据供我们使用,我们可以讲数据库文件放入assets目录一起打包发布,在用户首次打开App时,使用createFromAsset()和createFrimFile()创建Room数据库。
如图所示
既然要自带一些数据,那么我们预先准备一些数据存入自带数据库里面。
那看看如何使用?
@Database(entities = [Student::class], version =1, exportSchema = false) abstract class MyDatabase : RoomDatabase() { companion object { private const val DATABASE_NAME = "my_db.db" private var mInstance: MyDatabase? = null @Synchronized @JvmStatic open fun getInstance(context: Context): MyDatabase? { if (mInstance == null) { mInstance = Room.databaseBuilder( context.applicationContext, MyDatabase::class.java, DATABASE_NAME ) //.allowMainThreadQueries() .createFromAsset("prestudent.db") .build() } return mInstance } abstract fun getStudentDao(): StudentDao? }
我们可以看到,额外加了一句.createFromAsset("prestudent.db")
,注意这里FromAsset
,也就是说,对应的预存储数据库是存放在Asset
目录下的,这里就不贴图了。
来看看运行效果(卸载原有的):
可以看出,在软件安装成功时,就预先保留了对应数据!
好了接着下一个!
2、使用Migration升级数据库
2.1 只增列名情况
这里都已经提到了使用Migration,那么试试看?
@Database(entities = [Student::class], version =1, exportSchema = false) abstract class MyDatabase : RoomDatabase() { companion object { private const val DATABASE_NAME = "my_db.db" private var mInstance: MyDatabase? = null @Synchronized @JvmStatic open fun getInstance(context: Context): MyDatabase? { if (mInstance == null) { mInstance = Room.databaseBuilder( context.applicationContext, MyDatabase::class.java, DATABASE_NAME ) //.allowMainThreadQueries() .addMigrations(MIGRATION_1_2, MIGRATION_2_3) .createFromAsset("prestudent.db") .build() } return mInstance } @JvmStatic val MIGRATION_1_2: Migration = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("ALTER TABLE student ADD COLUMN sex INTEGER NOT NULL DEFAULT 1") } } @JvmStatic val MIGRATION_2_3: Migration = object : Migration(2, 3) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("ALTER TABLE student ADD COLUMN bar_data INTEGER NOT NULL DEFAULT 1") } } } abstract fun getStudentDao(): StudentDao? }
代码解析
MIGRATION_1_2
与MIGRATION_2_3
分别代表对应数据库的升级策略- 1-2版本添加了
COLUMN sex INTEGER
列名 - 2-3版本添加了
COLUMN bar_data INTEGER
列名 @Database(xxx, version =1, xxxz)
这里面的version
表示当前版本,如果要升级2,那么就将最新App对应属性改为2- 注意,对应
Student
实体类也需要根据对应SQL做对应的修改!比如1-2版本添加了sex
,那么实体类也需要哟
实体类就不贴了哈,要根据SQL做对应的调整!
来看看1-2升级的运行效果
升级前对应版本1的数据库信息:
升级后对应版本2的数据库信息:(version=2 ,对应数据表实体类调整)
升级后对应版本3的数据库信息:(version=3 ,对应数据表实体类调整)
OK,测试都没问题!我们现在都是1升2,2升3。那么如果说保留1升2,2升3,没有直接1升3,用户版本就为1,而我们数据库已经升级到3了呢?
我们将版本改为1,对应数据表实体类也回退1状态,卸载原App重新试试!
重新运行后,当前App数据库版本为1,这时,
:(直接将版本改为3,使用版本3的升级策略,version=3 ,对应数据表实体类调整)
我们发现,当Room遇到跨版本升级时:
- Room会先判断有没有从1到3的升级方案,
- 如果有,就直接执行从1到3的升级方案;
- 如果没有,那么Room会按照顺序先后执行
Migration(1, 2)
、Migration(2, 3)
以完成升级!
这时我们看到,升级数据库只看到过添加列名情况!那万一说,对应列名需要该变量类型该怎么做呢?
比如说原有的sex为INTEGER
,后面突然发病要将sex改为TEXT
呢?
2.2 销毁和重建策略
大致分为以下步骤:
- 创建一张符合表结构要求的临时表temp_student
- 将数据从旧表student复制到临时表temp_student
- 删除旧表student
- 将临时表temp_student重命名为student
看完步骤,思路清晰了,来实现看看!
@Database(entities = [Student::class], version =3, exportSchema = false) abstract class MyDatabase : RoomDatabase() { companion object { private const val DATABASE_NAME = "my_db.db" private var mInstance: MyDatabase? = null @Synchronized @JvmStatic open fun getInstance(context: Context): MyDatabase? { if (mInstance == null) { mInstance = Room.databaseBuilder( context.applicationContext, MyDatabase::class.java, DATABASE_NAME ) //.allowMainThreadQueries() // .fallbackToDestructiveMigration() .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4) .createFromAsset("prestudent.db") .build() } return mInstance } @JvmStatic val MIGRATION_1_2: Migration = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("ALTER TABLE student ADD COLUMN sex INTEGER NOT NULL DEFAULT 1") } } @JvmStatic val MIGRATION_2_3: Migration = object : Migration(2, 3) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("ALTER TABLE student ADD COLUMN bar_data INTEGER NOT NULL DEFAULT 1") } } @JvmStatic val MIGRATION_3_4: Migration = object : Migration(3, 4) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL( "CREATE TABLE temp_student (" + "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + "name TEXT," + "age INTEGER NOT NULL," + "sex TEXT DEFAULT 'M'," + "bar_data INTEGER NOT NULL DEFAULT 1)" ) database.execSQL( "INSERT INTO temp_student (name,age,sex,bar_data)" + "SELECT name,age,sex,bar_data FROM student" ) database.execSQL("DROP TABLE student") database.execSQL("ALTER TABLE temp_student RENAME TO student") } } } abstract fun getStudentDao(): StudentDao? }
这时,我们看到,创建了MIGRATION_3_4
,里面的逻辑就是上面我们理清的步骤,那么?
将版本改为4(version=4,对应实体类需要对应调整)运行效果
我们看到对应列名类型已经修改成功!
那么在升级时,我们要不要将升级的过程保留下呢?如果能保留,以后排查问题的时候也会方便一点!
3、Schema文件
Room在每次数据库升级过程中,都会导出一个Schema文件,这是一个json格式的文件,其中包含了数据库的基本信息,有了该文件,开发者能清楚的知道数据可的厉次变更过程,极大方便了开发者排查问题!
那么该怎么使用呢?
android { compileSdkVersion 30 defaultConfig { ...略 // javaCompileOptions { // annotationProcessorOptions { // arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]//指定数据库schema导出的位置 // } // } kapt { arguments { arg("room.schemaLocation", "$projectDir/schemas".toString()) } } } }
注释部分是业务逻辑使用Java时,对应配置导出的位置!下者为Kotlin对应配置导出位置!
此外还需要将
@Database(entities = [Student::class], version =1, exportSchema = false){ }
对应的 exportSchema
改为true
(默认为true),卸载重新从版本1开始运行!
运行成功后,修改版本为2(version=2,对应实体类调整)重新运行后:
OK,我们看到左边已经有对应的新文件!打开看,就是对应的升级策略!
结束语
好了,本篇到这里就结束了,相信你对Room有了全面的理解!在下一篇中,将会讲解Jetpack对应的Navigation组件!
这篇关于Android—Jetpack教程(六)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-16在电脑上怎么模拟手机的运行环境?-icode9专业技术文章分享
- 2024-11-16接收socket数据,莫名其妙socket就关闭了是怎么回事?-icode9专业技术文章分享
- 2024-11-16ts nightly是什么?-icode9专业技术文章分享
- 2024-11-16如何升级vscode版本?-icode9专业技术文章分享
- 2024-11-16如何设置vscode默认的node版本?-icode9专业技术文章分享
- 2024-11-16shell 如何创建一个文件夹?-icode9专业技术文章分享
- 2024-11-16useReducer案例详解:从零开始理解与应用
- 2024-11-15聊聊用LangChain4J构建聊天机器人的那些事儿
- 2024-11-15LangChain 和 LlamaIndex 在检索增强生成(RAG)中的大比拼:全面对比评测
- 2024-11-15平台工程不只是配置管理:超越CFEngine的方法