如何用Android Room创建并预填充SQLite数据库?解决回调未触发问题
为什么数据库文件没生成、onCreate回调没触发?
Room采用懒加载机制:调用databaseBuilder().build()只是创建了数据库的配置实例,并不会立即生成实际的数据库文件、创建表或者触发回调。只有当你第一次执行数据库操作(比如获取DAO实例、插入/查询数据)时,Room才会真正初始化数据库,这时候才会生成文件并调用onCreate方法。
另外你代码里还有个小错误:super.onCreate(db)传错了参数,应该用回调方法里的ssdb,这个错误可能会导致回调执行异常,也是回调没触发的潜在原因。
如何创建数据库并完成预填充?
按照以下步骤操作就能实现你的需求:
1. 修正回调的参数错误
把回调里的super.onCreate(db)改成super.onCreate(ssdb),确保父类方法拿到正确的数据库对象:
.addCallback(new RoomDatabase.Callback() { @Override public void onCreate(@NonNull SupportSQLiteDatabase ssdb) { super.onCreate(ssdb); // 修正参数为ssdb Log.d(TAG, "Database created - populate database"); // 这里写预填充逻辑 } })
2. 主动触发数据库初始化
在调用build()之后,主动触发数据库的初始化流程,这样就能立即生成数据库文件并触发onCreate回调。推荐用getOpenHelper().getWritableDatabase()方法:
MyDatabaseSample db = Room.databaseBuilder(context, MyClass.class, "DatabaseName.db") .addCallback(/* 上面的回调 */) .build(); // 主动触发初始化 - 注意:不要在主线程执行! new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... voids) { db.getOpenHelper().getWritableDatabase(); return null; } }.execute();
为什么要用异步线程?因为Room默认禁止在主线程执行数据库操作,直接在主线程调用会抛出异常,所以必须放到后台线程。
3. 在onCreate中完成预填充
在onCreate方法里,使用传入的SupportSQLiteDatabase对象执行SQL语句来预填充数据(不要直接用Room的DAO,此时Room实例还未完全初始化,会出问题):
@Override public void onCreate(@NonNull SupportSQLiteDatabase ssdb) { super.onCreate(ssdb); Log.d(TAG, "Database created - populate database"); // 示例:插入初始数据到your_table表 ssdb.execSQL("INSERT INTO your_table (column_name1, column_name2) VALUES ('初始值1', '初始值2')"); // 如果有多个预填充操作,可以批量执行提升效率 ssdb.beginTransaction(); try { ssdb.execSQL("INSERT INTO ..."); ssdb.execSQL("INSERT INTO ..."); ssdb.setTransactionSuccessful(); } finally { ssdb.endTransaction(); } }
注意:SQL语句里的表名、字段名要和你的实体类一致。如果实体类用了
@Entity(tableName = "xxx")或者@ColumnInfo(name = "yyy")注解,要使用注解里的名称。
额外注意事项
- 你的Room版本是1.1.0-alpha2,这个版本的懒加载机制和后续版本一致,核心逻辑没有变化;
- 如果你后续升级Room到2.1+,可以使用
createFromAsset()或createFromFile()方法直接从预打包的数据库文件初始化,更方便,但1.1.x版本不支持这个特性; - 预填充完成后,后续正常使用Room的DAO进行数据库操作即可,不需要再重复触发初始化。
内容的提问来源于stack exchange,提问作者Manoel Rui

