You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:19:56