Kotlin代理与Room持久性库

6

我目前使用 Kotlin 开发一个新的 Android 应用程序。我尝试使用 Room 进行数据存储,但是我无法使用 Kotlin 代理使其工作。

我创建了一个 Identifier 代理以确保在初始化后不会更改 id。该代理看起来像这样:

class Identifier: ReadWriteProperty<Any?, Long> {

    private var currentValue = -1L

    override fun getValue(thisRef: Any?, property: KProperty<*>): Long {
        if (currentValue == -1L) throw IllegalStateException("${property.name} is not initialized.")
        return currentValue
    }

    override fun setValue(thisRef: Any?, property KProperty<*>, value: Long) {
        if (currentValue != -1L) throw IllegalStateException("${property.name} can not be changed.")
        currentValue = value
    }
}

我的实体类看起来像这样:

@Entity
class Sample {

    @delegate:PrimaryKey(autoGenerate = true)
    var id by Identifier()
}

当我尝试启动应用程序时,kapt会给出以下错误消息:

Cannot figure out how to save this field into database. You can consider adding a type converter for it.
private final com.myapp.persistence.delegates.Identifier id$delegate = null;

我可以不为每个委托编写TypeConverter而使其工作吗?

3个回答

21

使用@delegate:Ignore

我曾遇到与我的实体对象和... by lazy属性类似的问题。

例如:

var name: String = "Alice"

val greeting: String by lazy { "Hi $name" }

这里的问题是 Room "无法将此字段保存到数据库"。我尝试添加 "@Ignore",但得到了一个lint消息,指出 "该注释不适用于具有委托的成员属性目标"。

事实证明,在这种情况下,正确的注释是@delegate:Ignore

解决方案:

var name: String = "Alice"

@delegate:Ignore
val greeting: String by lazy { "Hi $name" }

谢谢,这很有效。你是怎么想到要使用@delegate: Ignore而不是@忽略的? - MikeOscarEcho

2

很遗憾,不行 - Room 默认为实体中定义的每个字段创建一列,当我们使用 delegate 时,会生成如下代码:

   @PrimaryKey(autoGenerate = true)
   @NotNull
   private final Identifier id$delegate = new Identifier();

   public final long getId() {
      return this.id$delegate.getValue(this, $$delegatedProperties[0]);
   }

   public final void setId(long var1) {
      this.id$delegate.setValue(this, $$delegatedProperties[0], var1);
   }

这就是为什么Room尝试为Identifier id$delegate创建列的原因。

然而,如果您只想确保在对象初始化后不更改id,则根本不需要代理,只需将变量标记为final并将其放置在构造函数中,例如:

@Entity
data class Sample(
    @PrimaryKey(autoGenerate = true)
    val id: Long
)

0

我遇到了以下代码类似的问题:

data class ForecastItem(
val city: String,
val time: Long,
val temp: Int,
val tempMax: Int,
val tempMin: Int,
val icon: String
) {
    val formattedTime: String by lazy {
        val date = Date(this.time * 1000L)
        val dateFormat = SimpleDateFormat("E HH:mm")
        dateFormat.timeZone = TimeZone.getTimeZone("GMT+1")
        dateFormat.format(date)
  }
}

在这种情况下,我得到了与formattedTime委托相关的相同错误:

无法确定如何将此字段保存到数据库中。您可以考虑为其添加类型转换器。

在我的情况下,最终我用一个函数替换了委托。虽然不完全相同,但对我有用。我不确定这是否实际上是设计解决方案的最佳方式,但我希望它能帮助任何遇到类似问题的人。
fun getFormattedTime(): String {
    val date = Date(this.time * 1000L)
    val dateFormat = SimpleDateFormat("E HH:mm")
    dateFormat.timeZone = TimeZone.getTimeZone("GMT+1")
    return dateFormat.format(date)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接