Realm数据库迁移:版本11前后Schema重建与数据保留实现咨询
刚好碰到过类似的Realm版本过渡场景,给你梳理一套完全适配需求的实现方案:
核心思路先理清楚
你的需求其实是典型的「从无数据持久化到需要数据保留」的版本切换,加上加密和动态数据库名称的约束,核心要做的就是:
- 对11版本之前的旧库:直接重建Schema(相当于删旧库建新房)
- 对11及之后的版本:严格执行迁移逻辑,保证用户数据不丢失
- 全程适配动态获取的加密密钥和基于密钥生成的数据库名
具体一步步来实现
1. 先搞定动态配置的生成逻辑
因为密钥是从服务器拿的,数据库名又依赖密钥,所以每次启动都得先拿到密钥再构建Realm配置:
// 先写个获取服务器授权密钥的方法(你要自己实现请求逻辑哈) fun fetchEncryptionKeyFromServer(): ByteArray { // 这里放你的服务器请求逻辑,注意处理异步/同步的情况,比如用Coroutine或者Callback return yourServerProvidedKeyByteArray } // 构建Realm配置的核心方法 fun createRealmConfig(): RealmConfiguration { val encryptionKey = fetchEncryptionKeyFromServer() // 把密钥哈希成安全的文件名,避免特殊字符搞出问题 val dbFileName = "app_db_${hashKeyToFileName(encryptionKey)}" return RealmConfiguration.Builder() .name(dbFileName) .encryptionKey(encryptionKey) .schemaVersion(11L) // 从11版本开始正式启用迁移 .migration(CustomRealmMigration()) // 绑定我们的自定义迁移类 .build() } // 辅助工具:把字节数组密钥转成合法的文件名 private fun hashKeyToFileName(key: ByteArray): String { return MessageDigest.getInstance("SHA-256") .digest(key) .joinToString("") { "%02x".format(it) } }
2. 实现自定义迁移类,核心逻辑在这里
迁移类的关键就是判断旧版本号,分情况处理:
class CustomRealmMigration : RealmMigration { override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { val schema = realm.schema var currentVersion = oldVersion // 重点:处理11版本之前的旧库——直接清空重建Schema if (currentVersion < 11L) { // 删除所有旧表,相当于把旧库清干净 schema.all.forEach { schema.remove(it.name) } // 重新创建11版本对应的表结构,比如你要存用户数据的表 schema.create("UserInputData") .addField("id", String::class.java, FieldAttribute.PRIMARY_KEY) .addField("content", String::class.java) .addField("createTime", Date::class.java) // 把版本号拉到11,后面好处理后续升级 currentVersion = 11L } // 处理11及以后的版本升级,比如从11升到12,新增字段之类的 if (currentVersion == 11L && newVersion == 12L) { // 示例:给UserInputData表加个"updateTime"字段 schema.get("UserInputData")?.addField("updateTime", Date::class.java) currentVersion = 12L } // 以后再升级版本,就在这里加对应的逻辑就行 } }
3. 调整Realm初始化逻辑,丢掉旧的删除方法
之前用的deleteRealmIfMigrationNeeded()现在可以扔了,因为我们的迁移类已经处理了老版本的重建:
// 应用启动时初始化Realm的方法 fun initRealmInApp(context: Context) { Realm.init(context) val realmConfig = createRealmConfig() // 这里不再调用deleteRealmIfMigrationNeeded(),迁移类已经搞定老版本的情况 Realm.setDefaultConfiguration(realmConfig) // 可以加个小验证,避免启动时出异常 try { Realm.getDefaultInstance().close() } catch (e: RealmException) { Log.e("RealmInit", "初始化Realm失败:${e.message}") // 这里可以加异常处理,比如提示用户重新授权获取密钥 } }
4. 边界情况都给你考虑到了
- 首次装11+版本的用户:直接用新Schema,正常存数据
- 从<11版本升级到11+的用户:迁移时会清空旧数据,重建新表,符合你之前版本无需保留数据的需求
- 11+版本之间升级的用户:执行对应的迁移逻辑,所有用户数据都会保留
几个要注意的坑
- 密钥安全:从服务器拿密钥一定要用HTTPS,密钥尽量只存在内存里,别随便存本地文件或者SharedPreferences
- 数据库名唯一性:用密钥哈希生成文件名,能保证不同用户的数据库完全隔离,不会串数据
- 迁移逻辑可扩展:以后版本升级,只需要在
migrate方法里加对应版本的处理就行,别乱改旧逻辑
内容的提问来源于stack exchange,提问作者Est Stalegaykin
相关产品推荐
相关产品推荐

