如何仅在首次启动Android应用程序时创建预填充数据库?

问题描述 投票:0回答:1

大家好!我是 Room 的初学者,我需要在第一次启动应用程序时使用启动数据创建数据库,然后修改应用程序中的数据并在第二次启动应用程序时使用修改后的数据。

我有这个代码

companion object {
    @Volatile
    private var INSTANCE: AppDatabase? = null

    fun getDatabase(context: Context): AppDatabase {
        return INSTANCE ?: synchronized(this) {
            val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "app_database.db",
                )
                    .createFromAsset("database/01-01-2024.db")
                    .build()
        
            INSTANCE = instance

            instance
        }
    }

它成功创建了预填充的数据库,我可以在应用程序的下一个屏幕中修改数据。但是,当我关闭应用程序并第二次打开数据库时,修改后的数据将使用预填充的数据库重写。

如何仅当 BD 不存在时才使用预填充数据库?

附注我正在观看文档,但没有帮助。

android database sqlite android-room
1个回答
0
投票

根据您提供的代码,预填充的数据库应该在应用程序首次运行时将预填充的数据库从资产复制到标准数据库位置,后续运行应该看到数据库存在,然后不再重做副本。

  • 标准位置位于 data/data//databases 文件夹/目录中

这个过程是这样的:-

  1. 数据库是否存在于标准位置。
  2. 如果没有,则将资产复制到标准位置。
  3. 在标准位置打开数据库。

因此,您的问题是更改未应用于数据库,或者数据库正在被删除。

  • 如果您不重新运行应用程序,而是卸载然后运行应用程序,这将导致数据库被删除,因为它位于应用程序的数据中。

无法确定哪种情况导致您遇到问题。但是,也许可以考虑以下演示,它修改了您提供的可用于调试和确定问题的代码:-

首先是修改后的代码(参见注释),包括对包含单个@Dao接口检索的

AppDatabase
抽象类的猜测:-

@Database(entities = [TheTable::class], exportSchema = false, version = 1)
abstract class AppDatabase: RoomDatabase() {
    abstract fun getAllDAOs(): AllDAOs
    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getDatabase(context: Context): AppDatabase {
            /* Amended to inspect the database prior to acess it via Room */
            preDBAccess(context)
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "app_database.db",
                )
                        /* Amended to invoke the PrePackagedDatabaseCallback (just to log when prepackaged database is used) */
                    .createFromAsset("database/01-01-2024.db", prePackagedDatabaseCallback)
                        /* Amended to invoke the standard callbacks (OnCreate,OnOpen,OnDestMig) IF INVOKED */
                    .addCallback(cb)
                        /* Amended to allow running on the main thread for brevity */
                    .allowMainThreadQueries()
                    .build()
                INSTANCE = instance

                instance
            }
        }
        val cb = object: Callback(){
            override fun onCreate(db: SupportSQLiteDatabase) {
                super.onCreate(db)
                Log.d(TAG+"ONCRT","OnCreate Callback invoked!.")
                showDBInfo(db)
            }

            override fun onOpen(db: SupportSQLiteDatabase) {
                super.onOpen(db)
                Log.d(TAG+"ONOPN","OnOpen Callback invoked!.")
                showDBInfo(db)
            }

            override fun onDestructiveMigration(db: SupportSQLiteDatabase) {
                super.onDestructiveMigration(db)
                Log.d(TAG+"ONDM","OnDestructiveMigration invoked!.")
                showDBInfo(db)
            }
        }

        val prePackagedDatabaseCallback = object:PrepackagedDatabaseCallback(){
            override fun onOpenPrepackagedDatabase(db: SupportSQLiteDatabase) {
                super.onOpenPrepackagedDatabase(db)
                Log.d(TAG + "_PPDC","PrePackagedDatabase Callback invoked!.")
                showDBInfo(db)
            }
        }

        fun showDBInfo(db: SupportSQLiteDatabase) {
            var csr = db.query("SELECT * FROM sqlite_master")
            DatabaseUtils.dumpCursor(csr)
            csr = db.query("SELECT * FROM thetable")
            DatabaseUtils.dumpCursor(csr)
            csr.close()
        }
        fun preDBAccess(context: Context) {
            Log.d(TAG+"PDBA","Pre Database Access processing initiated!.")
            val dbFileExists = context.getDatabasePath("app_database.db").exists()
            if (!dbFileExists) {
                Log.d(TAG+"PDBA","Database file app_database.db does not exist! (should be copied from asset)")
            } else {
                Log.d(TAG+"PDBA","Database file app_database.db exists!.")
                val db = SQLiteDatabase.openDatabase(context.getDatabasePath("app_database.db").path,null,0)
                Log.d(TAG+"PDBAV","Database version is ${db.version}")
                var csr = db.rawQuery("SELECT * FROM sqlite_master",null)
                DatabaseUtils.dumpCursor(csr)
                csr = db.rawQuery("SELECT * FROM thetable",null)
                DatabaseUtils.dumpCursor(csr)
                csr.close()
                db.close()
            }
        }
    }
}
  • 可以看出,这包括相对全面的调试(请参阅下面的用法)

用于演示的其他 Room 类包括:-

const val TAG: String ="DBINFO"
@Entity
data class TheTable(
    @PrimaryKey
    var id: Long?=null,
    var a_column: String
)
@Dao
interface AllDAOs {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(theTable: TheTable): Long
    @Query("SELECT * FROM thetable")
    fun getAllFromTheTable(): List<TheTable>
}

实际演示一些活动代码(为了简洁起见,使用主线程):-

class MainActivity : AppCompatActivity() {
    lateinit var db: AppDatabase
    lateinit var dao: AllDAOs
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        db = AppDatabase.getDatabase(this)
        dao = db.getAllDAOs()
        /* NOTE database has NOT been accessed yet so no copy undertaken */
        for (tt in dao.getAllFromTheTable()) {
            Log.d(TAG+"ADBA","TT Row ID=${tt.id} a_column=${tt.a_column}")
        }
        for (i in 1..10) {
            dao.insert(TheTable(null,"INSRT_${i} at ${System.currentTimeMillis()}"))
        }
        for (tt in dao.getAllFromTheTable()) {
            Log.d(TAG+"AINSRT","TT Row ID=${tt.id} a_column=${tt.a_column}")
        }
    }
}

当应用程序运行时,这将是:-

  1. 构造一个 AppDatabase 对象作为 db
    1. 将 preDBAccess 调用的结果写入日志
      1. 这将表明数据库文件存在或不存在
      2. 如果数据库文件确实存在,那么预打包的数据库不会被复制,那么数据库信息(TheTable的schema、版本和内容)将被写入日志。
  2. 检索 AllDAOs 对象作为 dao
    1. 注意,因为这是 Room 访问/打开数据库时,相应的日志记录将会发生
  3. TheTable表中提取所有行并将提取的数据写入日志。
  4. 将 10 行插入到 TheTable
  5. 再次提取所有行 TheTable 表将提取的数据写入日志(期望有 10 行额外数据)
© www.soinside.com 2019 - 2024. All rights reserved.