gpt4 book ai didi

android - 当 room android db 损坏时会发生什么?

转载 作者:行者123 更新时间:2023-12-05 05:54:06 27 4
gpt4 key购买 nike

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

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

最佳答案

For such a user whats the fallback ?

备份和恢复,注意当有任何未完成的交易时不应进行备份。最好确保如果处于 WAL 模式(这是有空间的默认模式),则数据库已完全提交(即 WAL 文件为空或不存在)。

  • 备份可以是简单的文件副本,也可以使用 VACUUM INTO后者的优点是可以释放空间,但缺点是可能会占用更多资源。

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

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

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

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

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

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

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,从而创建一个新的空数据库。

上面使用的代码是:-

一个简单的@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

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 删除破坏了上面的数据库。对于被删除的 block 来说,它是非常有弹性的,至少对于较小的数据库来说是这样,在这些数据库中,WAL 文件的大小足以通过应用存储在其中的更改来恢复。然而,基于上述但插入第二个表和 100000 行的测试,部分块写入和丢失的 block 写入确实会损坏数据库。

关于android - 当 room android db 损坏时会发生什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69722729/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com