KMP中Android与iOS端用Room访问预填充SQLite数据库方案问询
在KMP中为Android和iOS端通过Room使用预填充SQLite数据库的方案
前提配置:共享模块引入Room Multiplatform
首先在KMP共享模块的build.gradle.kts中配置Room Multiplatform依赖,确保跨平台支持:
plugins { kotlin("multiplatform") id("com.android.library") id("androidx.room") } kotlin { androidTarget() iosX64() iosArm64() iosSimulatorArm64() sourceSets { val commonMain by getting { dependencies { implementation("androidx.room:room-runtime:2.6.1") implementation("androidx.room:room-ktx:2.6.1") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") } } val androidMain by getting { dependencies { implementation("androidx.core:core-ktx:1.12.0") } } val iosMain by getting } } room { schemaDirectory("$projectDir/schemas") }
在共享模块中定义通用的实体、DAO和数据库基类:
// 实体类 @Entity(tableName = "sample_table") data class SampleEntity( @PrimaryKey val id: Int, val name: String ) // DAO接口 @Dao interface SampleDao { @Query("SELECT * FROM sample_table") suspend fun getAll(): List<SampleEntity> } // 数据库核心类,两端分别实现预填充逻辑 @Database(entities = [SampleEntity::class], version = 1, exportSchema = true) abstract class AppDatabase : RoomDatabase() { abstract fun sampleDao(): SampleDao companion object { fun buildDatabase(context: Any): AppDatabase = when (context) { is Context -> buildAndroidDb(context) is NSFileManager -> buildIosDb(context) else -> throw IllegalArgumentException("Unsupported context type") } // Android端预填充实现 private fun buildAndroidDb(context: Context): AppDatabase = Room.databaseBuilder(context, AppDatabase::class.java, "mysqlite_sample.db") .createFromAsset("mysqlite_sample.db") .build() // iOS端预填充实现(下文详细说明) private fun buildIosDb(fileManager: NSFileManager): AppDatabase { // 逻辑见下文 } } }
Android端实现
- 将预填充的
mysqlite_sample.db放入Android模块的src/main/assets目录。 - 在Android应用中直接传入
Context获取数据库实例:
val db = AppDatabase.buildDatabase(applicationContext) // 调用DAO方法示例 lifecycleScope.launch { val data = db.sampleDao().getAll() // 处理数据 }
Room会自动将assets目录中的数据库复制到应用的私有数据库目录,无需额外操作。
iOS端实现
iOS没有Room原生的createFromAsset方法,需要手动完成数据库复制逻辑:
1. 添加数据库到iOS资源
将mysqlite_sample.db拖入iOS模块的Resources目录,在Xcode中勾选"Copy items if needed",并确保该文件被加入目标应用的Build Phases > Copy Bundle Resources列表。
2. 实现数据库复制与初始化
完善共享模块中buildIosDb方法的逻辑,将Bundle中的数据库复制到应用的Documents目录(Room在iOS上默认使用该目录存储数据库):
private fun buildIosDb(fileManager: NSFileManager): AppDatabase { val dbName = "mysqlite_sample.db" // 获取Documents目录路径 val documentsDir = fileManager.URLsForDirectory( NSDocumentDirectory, NSUserDomainMask ).first() as NSURL val targetDbPath = documentsDir.path + "/$dbName" // 仅当目标路径无数据库时执行复制 if (!fileManager.fileExistsAtPath(targetDbPath)) { val bundleDbPath = NSBundle.mainBundle().pathForResource(dbName, null) ?: throw IllegalStateException("Pre-populated database not found in iOS bundle") fileManager.copyItemAtPath(bundleDbPath, targetDbPath, null) } // 初始化Room数据库 return Room.databaseBuilder<AppDatabase>( name = dbName, factory = { AppDatabase::class.instantiateImpl() } ).build() }
3. iOS端调用示例
在Swift代码中传入FileManager实例获取数据库:
let db = AppDatabase.buildDatabase(context: FileManager.default) Task { do { let samples = try await db.sampleDao().getAll() // 处理数据 } catch { print("Error fetching data: \(error)") } }
关键注意事项
- 确保预填充数据库的版本号与共享模块
@Database注解中的version完全一致,否则Room会触发迁移逻辑导致预填充数据失效。 - iOS端需处理文件复制的异常情况,避免因资源缺失导致应用崩溃。
- 测试时可通过Xcode查看应用包内容,确认
mysqlite_sample.db已被正确打包。
内容的提问来源于stack exchange,提问作者Aristotele Songhori
相关产品推荐
相关产品推荐

