安卓应用更新后出现SQLiteDiskIOException崩溃的原因排查
问题分析:Room数据库更新后的SQLiteDiskIOException崩溃
问题背景
应用发布更新后出现磁盘I/O相关崩溃,此前为让asset中新增的数据库数据生效,将Room数据库版本从1升级到2,并使用fallbackToDestructiveMigration()配置。
崩溃日志
Fatal Exception: android.database.sqlite.SQLiteDiskIOException: disk I/O error (code 1802 SQLITE_IOERR_FSTAT[1802]): , while compiling: PRAGMA cache_size at android.database.sqlite.SQLiteConnection.nativePrepareStatement(SQLiteConnection.java) at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:1540) at android.database.sqlite.SQLiteConnection.executeForLong(SQLiteConnection.java:1075) at android.database.sqlite.SQLiteConnection.setCacheSizeFromConfiguration(SQLiteConnection.java:536) at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:416) at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:228) at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:758) at android.database.sqlite.SQLiteConnectionPool.tryAcquireNonPrimaryConnectionLocked(SQLiteConnectionPool.java:1355) at android.database.sqlite.SQLiteConnectionPool.waitForConnection(SQLiteConnectionPool.java:983) at android.database.sqlite.SQLiteConnectionPool.acquireConnection(SQLiteConnectionPool.java:619) at android.database.sqlite.SQLiteSession.acquireConnection(SQLiteSession.java:916) at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588) at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:63) at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37) at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:46) at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:2278) at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:2253) at androidx.sqlite.db.framework.FrameworkSQLiteDatabase.query(FrameworkSQLiteDatabase.kt:156) at androidx.room.RoomDatabase.query(RoomDatabase.kt:490) at androidx.room.util.DBUtil.query(DBUtil.kt:75) at myapppackage.data.datasource.db.language.MultiLanguageStationNameDao_Impl$1.call(MultiLanguageStationNameDao_Impl.java:37) at myapppackage.data.datasource.db.language.MultiLanguageStationNameDao_Impl$1.call(MultiLanguageStationNameDao_Impl.java:34) at androidx.room.CoroutinesRoom$Companion$createFlow$1$1$1.invokeSuspend(CoroutinesRoom.kt:128) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644) at java.lang.Thread.run(Thread.java:1012)
相关代码
数据库构建代码
private fun buildDatabase(context: Context): AppDatabase { return Room.databaseBuilder( context, AppDatabase::class.java, "new_database.db" ).createFromAsset("db/my_app_db.sqlite") .fallbackToDestructiveMigration() .build() }
数据库版本配置
@Database( entities = [ MultiLanguageStationNameEntity::class, ], version = 2 )
Repository代码
override fun getStationNames(): Flow<List<MultiLanguageStationNameRes>> { return multiLanguageStationNameDao.getStationNames() .map { entities -> entities.map { entity -> entity.asDomainModel() } } }
Dao代码
@Dao interface MultiLanguageStationNameDao { @Query("SELECT * FROM MultiLanguageStationName") fun getStationNames(): Flow<List<MultiLanguageStationNameEntity>> }
崩溃原因解析
1. 核心崩溃本质
SQLiteDiskIOException(code 1802)属于磁盘层面的I/O异常,常见触发场景:
- 设备存储空间不足
- 存储硬件故障或文件系统损坏
- 数据库文件被其他进程/线程占用,无法正常读写
- 应用缺少数据库文件的访问权限
2. 应用更新时数据库处于打开状态的影响
这种情况是崩溃的潜在诱因:
- 应用更新过程中,若旧版本后台进程未被系统完全杀死,仍持有数据库连接,会导致数据库文件处于被占用状态。
- 新版本启动后尝试打开或替换数据库文件时,会因文件被锁触发磁盘I/O错误。
3. fallbackToDestructiveMigration的作用与影响
- 该配置的逻辑是:当Room检测到版本不匹配且无对应迁移规则时,删除现有数据库文件,重新从asset复制新文件。
- 如果更新时旧数据库连接未释放,执行删除旧文件的操作会因文件被占用触发I/O异常,这是本次崩溃的关键触发点之一。
- 若设备存储空间不足,删除旧文件或复制新文件的操作也会直接触发这类错误。
排查与修复建议
- 检查崩溃设备的存储状态:确认剩余空间充足,存储设备无故障
- 优化数据库连接管理:确保应用退到后台或进程结束时,正确关闭所有数据库连接(Room单例实例需与应用生命周期绑定,避免泄漏)
- 添加异常捕获:在数据库初始化或查询逻辑中捕获I/O异常,提供友好提示或重试机制
- 提前清理旧数据库:新版本启动时,先检测旧数据库连接状态,确保释放后再执行迁移操作
内容的提问来源于stack exchange,提问作者thesquare12
相关产品推荐
相关产品推荐

