当room android db损坏时会发生什么? [英] What happens when room android db gets corrupted?

查看:68
本文介绍了当room android db损坏时会发生什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在大型应用程序中,我的 db 文件(通过 android room lib 创建)可能会损坏.对于这样的用户,回退是什么?

In a large scale app, there are chances of my db file (created through android room lib), getting corrupted. For such a user whats the fallback ?

room 会删除 db 文件并在生产模式下从头开始重新创建还是我必须自己处理?

Will room delete the db file and re-create from scratch in production mode or will I have to handle that myself ?

推荐答案

对于这样的用户,回退是什么?

For such a user whats the fallback ?

备份和恢复,注意当有任何打开的事务时不应该进行备份.最好确保如果在 WAL 模式(这是有空间的默认模式)数据库完全提交(即 WAL 文件为空或不存在).

Backups and restore, noting that backups should not be taken when there are any open transactions. It is best to ensure that if in WAL mode (which is the default mode with room) that the database is fully committed (i.e. the WAL file is empty or doesn't exist).

  • 备份可以是一个简单的文件副本,或者您可以使用 VACUUM INTO 后者具有潜在释放空间的优点,但缺点是它可能会占用更多资源.
  • The backup could be a simple file copy or you can use the VACUUM INTO the latter having the advantage of potentially freeing space but the disadvantage is that it could be more resource intensive.

您可以维护其他数据库,在主数据库更改时,更改会应用到其他数据库.显然会有开销.这样做的好处是,如果发生损坏,在其他数据库中不太可能发生损坏.

You could maintain additional databases where changes are applied to other databases as and when the main database is changed. Obviously there would be an overhead. This would have the advantage that if corruption were to occur that it would be less likely to occur in the other databases.

您可以维护允许回滚和前滚更改的日志.后者用于从备份前滚到损坏点或接近损坏点.

You could maintain a log that allowed roll back and roll forward of changes. The latter being used to roll forward from a backup to the point of corruption or close to the point of corruption.

room 会删除 db 文件并在生产模式下从头开始重新创建还是我必须自己处理?

Will room delete the db file and re-create from scratch in production mode or will I have to handle that myself ?

如果检测到(打开时)则是:-

if detected (when opening) then yes :-

onCorruption检测到数据库损坏时调用的方法.默认实现将删除数据库文件.

onCorruption The method invoked when database corruption is detected. Default implementation will delete the database file.

https://developer.android.com/reference/androidx/sqlite/db/SupportSQLiteOpenHelper.Callback#onCorruption(androidx.sqlite.db.SupportSQLiteDatabase)

当房间 android db 损坏时会发生什么?

What happens when room android db gets corrupted?

这是文件损坏的示例

2021-10-27 10:36:19.281 7930-7930/a.a.so69722729kotlinroomcorrupt E/SQLiteLog: (26) file is not a database
2021-10-27 10:36:19.285 7930-7930/a.a.so69722729kotlinroomcorrupt E/SupportSQLite: Corruption reported by sqlite on database: /data/user/0/a.a.so69722729kotlinroomcorrupt/databases/thedatabase
2021-10-27 10:36:19.286 7930-7930/a.a.so69722729kotlinroomcorrupt W/SupportSQLite: deleting the database file: /data/user/0/a.a.so69722729kotlinroomcorrupt/databases/thedatabase
2021-10-27 10:36:19.306 7930-7930/a.a.so69722729kotlinroomcorrupt D/TAG: onCreate Invoked.
2021-10-27 10:36:19.312 7930-7930/a.a.so69722729kotlinroomcorrupt D/TAG: onOpen Invoked.

从回调中可以看出,在删除文件后调用 onCreate 从而创建一个新的空数据库.

As can be seen by logging from the callbacks that after the file is deleted that onCreate is invoked thus creating a new empty database.

上面使用的代码,您可能希望修改以进行测试,是:-

The code used for the above, which you may wish to adapt for testing, is :-

一个简单的@Entity 东西 :-

A simple @Entity Something :-

@Entity
data class Something(
    @PrimaryKey
    val id: Long?=null,
    val something: String
)

一个简单的@Dao AllDao

@Dao
abstract class AllDao {
    @Insert
    abstract fun insert(something: Something)
    @Query("SELECT * FROM something")
    abstract fun getAllFromSomething(): List<Something>
}

@Database TheDatabase

@Database(entities = [Something::class],version = 1)
abstract class TheDatabase: RoomDatabase() {
        abstract fun getAllDao(): AllDao

    companion object {
        const val DATABASENAME = "thedatabase"
        const val TAG = "DBINFO"
        private var instance: TheDatabase? = null
        var existed: Boolean = false
        fun getInstance(context: Context): TheDatabase {
            existed = exists(context)
            if (exists(context)) {
                Log.d(TAG,"Database exists so corrupting it before room opens the database.")
                corruptDatabase(context)
            }
            if (instance == null) {
                instance = Room.databaseBuilder(context,TheDatabase::class.java, DATABASENAME)
                    .allowMainThreadQueries()
                    .addCallback(cb)
                    .build()
            }
            instance!!.openHelper.writableDatabase // Force open
            return instance as TheDatabase
        }

        object cb: Callback() {
            override fun onCreate(db: SupportSQLiteDatabase) {
                super.onCreate(db)
                Log.d("TAG","onCreate Invoked.")
            }

            override fun onDestructiveMigration(db: SupportSQLiteDatabase) {
                super.onDestructiveMigration(db)
                Log.d("TAG","onDestructiveMigration Invoked.")
            }

            override fun onOpen(db: SupportSQLiteDatabase) {
                super.onOpen(db)
                Log.d("TAG","onOpen Invoked.")
            }

        }

        fun exists(context: Context): Boolean {
            val db = File(context.getDatabasePath(DATABASENAME).path)
            return db.exists()
        }

        /**
         * Corrupt the database by
         * copying the file via buffered reads and write
         * BUT only write part of a  buffer
         * AND skip the 3rd block
         * Furthermore, delete the -wal file (possible that this )
         * Note that often it is the -wal file deletion that corrupts
         */
        fun corruptDatabase(context: Context) {
            Log.d("TAG","corruptDatabase Invoked.")
            var db: File = File(context.getDatabasePath(DATABASENAME).path)
            logFileInfo(db,"Initial")
            var tempdb = File(context.getDatabasePath("temp" + DATABASENAME).path)
            logFileInfo(tempdb,"Initial")
            val blksize = 128
            var buffer = ByteArray(blksize)
            var i: InputStream
            var o: FileOutputStream
            try {
                i = FileInputStream(db)
                o = FileOutputStream(tempdb)
                var blocks = 0;
                var writes = 0;
                while (i.read(buffer) > 0) {
                    if(blocks++ % 2 == 1) {
                        writes++
                        o.write(buffer,buffer.size / 4,buffer.size / 4)
                    }
                }
                Log.d(TAG,"${blocks} Read ${writes} Written")
                o.flush()
                o.close()
                i.close()
                db = File(context.getDatabasePath(DATABASENAME).path)
                logFileInfo(db,"After copy")
                tempdb = File(context.getDatabasePath("temp${DATABASENAME}").path)
                logFileInfo(tempdb,"After copy")
            } catch (e: IOException) {
                e.printStackTrace()
            }
            db.delete()
            //(context.getDatabasePath(DATABASENAME+"-wal")).delete()
            logFileInfo(db,"After delete")
            tempdb.renameTo(context.getDatabasePath(DATABASENAME))
            logFileInfo(tempdb,"After rename")
            logFileInfo(context.getDatabasePath(DATABASENAME),"After rename/new file")
        }

        fun logFileInfo(file: File, prefix: String) {
            Log.d(TAG,"${prefix} FileName is ${file.name}\n\tpath is ${file.path}\n\tsize is ${file.totalSpace} frespace is ${file.freeSpace}")
        }
    }
}

最后是一个调用 Activity MainActivity

Finally an invoking Activity MainActivity

class MainActivity : AppCompatActivity() {
    lateinit var db: TheDatabase
    lateinit var dao: AllDao
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        db = TheDatabase.getInstance(this);
        dao = db.getAllDao()
        dao.insert(Something(something = "Something1 " + TheDatabase.existed))
        dao.insert(Something(something = "Something2 " + TheDatabase.existed))
        for(s: Something in dao.getAllFromSomething()) {
            Log.d(TheDatabase.TAG,"Something with ID " + s.id +  " is " + s.something)
        }
    }
}

  • 请注意,上面的数据库损坏的是 -wal 删除.对于被删除的块而言,它是非常有弹性的,至少对于 WAL 文件有足够大小可以通过应用存储在其中的更改进行恢复的较小数据库.但是,基于上述测试但插入了第二个表和 100000 行,然后部分块写入和丢失的块写入确实会损坏数据库.
  • 这篇关于当room android db损坏时会发生什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆