使用Room onCreate回调+Hilt初始化数据时出现OverlappingFileLockException异常
解决Room+Hilt初始化数据时的OverlappingFileLockException异常
问题原因
你在Room的onCreate回调里直接调用了provideDatabase()方法,这会创建一个全新的AppDatabase实例。此时正在创建的原数据库实例已经持有了数据库文件的锁,新实例又尝试获取同一个文件的锁,直接触发了java.nio.channels.OverlappingFileLockException。
正确解决方案
方案1:直接用回调提供的SupportSQLiteDatabase执行SQL
不需要创建新的数据库实例,直接通过回调传入的SupportSQLiteDatabase执行插入操作,避免锁冲突:
@Singleton @Provides fun provideDatabase(@ApplicationContext applicationContext: Context): AppDatabase { return Room.databaseBuilder(applicationContext, AppDatabase::class.java, DATABASE_NAME) .allowMainThreadQueries() .fallbackToDestructiveMigration() .addCallback(object : RoomDatabase.Callback() { override fun onCreate(db: SupportSQLiteDatabase) { super.onCreate(db) // 替换成你的user表实际字段和LocalUser对应的值 db.execSQL( "INSERT INTO user (username, email) VALUES (?, ?)", arrayOf("default_user", "default@example.com") ) } }) .build() }
方案2:用Hilt注入Dao做初始化(更符合架构规范)
把初始化逻辑抽成单独的类,通过Hilt注入Dao,在App启动时执行初始化,同时避免重复插入:
// 数据初始化类 class DatabaseInitializer @Inject constructor(private val userDao: UserDao) { fun initData() { // 先检查是否已有数据,防止重复初始化 if (userDao.getTotalUsers() == 0) { userDao.createUser(LocalUser()) } } } // 数据库提供方法去掉回调 @Singleton @Provides fun provideDatabase(@ApplicationContext applicationContext: Context): AppDatabase { return Room.databaseBuilder(applicationContext, AppDatabase::class.java, DATABASE_NAME) .allowMainThreadQueries() .fallbackToDestructiveMigration() .build() } // 在Application中触发初始化 @HiltAndroidApp class MyApplication : Application() { @Inject lateinit var dbInitializer: DatabaseInitializer override fun onCreate() { super.onCreate() dbInitializer.initData() } }
为什么无Hilt的教程能正常运行
无Hilt场景下,一般会通过单例模式确保全局只有一个数据库实例,初始化时要么直接操作原始数据库连接,要么复用已有的单例实例,不会出现多个实例同时抢占文件锁的情况,所以不会触发这个异常。
内容的提问来源于stack exchange,提问作者Pavel Poley
相关产品推荐
相关产品推荐

